用scipy稀疏矩阵求解方程组

时间:2013-01-17 14:09:14

标签: python scipy linear-algebra sparse-matrix equations

这是How to set up and solve simultaneous equations in python的后续行动,但我认为任何答案都应该有自己的声誉点。

对于固定整数n,我有一组2(n-1)联立方程如下。

M(p) = 1+((n-p-1)/n)*M(n-1) + (2/n)*N(p-1) + ((p-1)/n)*M(p-1)

N(p) = 1+((n-p-1)/n)*M(n-1) + (p/n)*N(p-1)

M(1) = 1+((n-2)/n)*M(n-1) + (2/n)*N(0)

N(0) = 1+((n-1)/n)*M(n-1)

M(p)是为1 <= p <= n-1定义的。 N(p)定义了0 <= p <= n-2。另请注意,p在每个等式中只是一个常数整数,因此整个系统是线性的。

如何在python中建立方程组给出了一些非常好的答案。但是,系统很稀疏,我想为大n解决它。我怎样才能使用scipy的稀疏矩阵表示和http://docs.scipy.org/doc/scipy/reference/sparse.linalg.html代替?

3 个答案:

答案 0 :(得分:5)

这是使用scipy.sparse的解决方案。不幸的是这里没有说明问题。因此,为了理解这个解决方案,未来的访问者必须首先在问题中提供的链接下查找问题。

使用scipy.sparse的解决方案:

from scipy.sparse import spdiags, lil_matrix, vstack, hstack
from scipy.sparse.linalg import spsolve
import numpy as np


def solve(n):
    nrange = np.arange(n)
    diag = np.ones(n-1)

    # upper left block
    n_to_M = spdiags(-2. * diag, 0, n-1, n-1)

    # lower left block
    n_to_N = spdiags([n * diag, -nrange[-1:0:-1]], [0, 1], n-1, n-1)

    # upper right block
    m_to_M = lil_matrix(n_to_N)
    m_to_M[1:, 0] = -nrange[1:-1].reshape((n-2, 1))

    # lower right block
    m_to_N = lil_matrix((n-1, n-1))
    m_to_N[:, 0] = -nrange[1:].reshape((n-1, 1))

    # build A, combine all blocks
    coeff_mat = hstack(
                       (vstack((n_to_M, n_to_N)),
                        vstack((m_to_M, m_to_N))))

    # const vector, right side of eq.
    const = n * np.ones((2 * (n-1),1))

    return spsolve(coeff_mat.tocsr(), const).reshape((-1,1))

答案 1 :(得分:5)

我通常不会继续击败死马,但碰巧我解决你的另一个问题的非矢量化方法,当事情变大时有一些优点。因为我实际上一次填充一个项目的系数矩阵,所以很容易转换为COO稀疏矩阵格式,可以有效地转换为CSC并求解。以下是:

import scipy.sparse

def sps_solve(n) :
    # Solution vector is [N[0], N[1], ..., N[n - 2], M[1], M[2], ..., M[n - 1]]
    n_pos = lambda p : p
    m_pos = lambda p : p + n - 2
    data = []
    row = []
    col = []
    # p = 0
    # n * N[0] + (1 - n) * M[n-1] = n
    row += [n_pos(0), n_pos(0)]
    col += [n_pos(0), m_pos(n - 1)]
    data += [n, 1 - n]
    for p in xrange(1, n - 1) :
        # n * M[p] + (1 + p - n) * M[n - 1] - 2 * N[p - 1] +
        #  (1 - p) * M[p - 1] = n
        row += [m_pos(p)] * (4 if p > 1 else 3)
        col += ([m_pos(p), m_pos(n - 1), n_pos(p - 1)] +
                ([m_pos(p - 1)] if p > 1 else []))
        data += [n, 1 + p - n , -2] + ([1 - p] if p > 1 else [])
        # n * N[p] + (1 + p -n) * M[n - 1] - p * N[p - 1] = n
        row += [n_pos(p)] * 3
        col += [n_pos(p), m_pos(n - 1), n_pos(p - 1)]
        data += [n, 1 + p - n, -p]
    if n > 2 :
        # p = n - 1
        # n * M[n - 1] - 2 * N[n - 2] + (2 - n) * M[n - 2] = n
        row += [m_pos(n-1)] * 3
        col += [m_pos(n - 1), n_pos(n - 2), m_pos(n - 2)]
        data += [n, -2, 2 - n]
    else :
        # p = 1 
        # n * M[1] - 2 * N[0] = n
        row += [m_pos(n - 1)] * 2
        col += [m_pos(n - 1), n_pos(n - 2)]
        data += [n, -2]
    coeff_mat = scipy.sparse.coo_matrix((data, (row, col))).tocsc()
    return scipy.sparse.linalg.spsolve(coeff_mat,
                                       np.ones(2 * (n - 1)) * n)

当然,正如TheodorosZelleke所做的那样,它比从矢量化块构建它更加冗长,但是当你计算两种方法时,会发生一件有趣的事情:

enter image description here

首先,这是(非常)好的,时间在两个解决方案中线性扩展,正如人们对使用稀疏方法所期望的那样。但是我在这个答案中给出的解决方案总是更快,对于更大的n来说更是如此。只是为了它的乐趣,我还从另一个问题中定时TheodorosZelleke的密集方法,这给出了这个漂亮的图表,显示了两种类型的解决方案的不同缩放,以及在n = 75附近的解决方案,这里的解决方案是你的选择:

enter image description here

我不太了解scipy.sparse以确定两种稀疏方法之间的差异,尽管我怀疑使用LIL格式稀疏矩阵。通过将TheodorosZelleke的答案转换为COO格式,虽然代码中有很多紧凑性,但可能会有一些非常小的性能提升。但这只是OP的练习!

答案 2 :(得分:0)

我之前已经看过一些代码:http://jkwiens.com/heat-equation-using-finite-difference/他的函数使用scipy稀疏矩阵包实现了一个有限差分方法来解决热方程。