Использование scipy разреженных матриц для решения системы уравнений

Это продолжение Как настроить и решить одновременные уравнения в 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 например вместо этого?


person lip1    schedule 17.01.2013    source источник


Ответы (3)


Обычно я бы не стал бить дохлую лошадь, но бывает, что мой не векторизованный подход к решению вашего другого вопроса имеет некоторые преимущества, когда дела становятся большими. Поскольку я фактически заполнял матрицу коэффициентов по одному элементу за раз, ее очень легко перевести в формат разреженной матрицы 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)

Это, конечно, гораздо более многословно, чем построение из векторизованных блоков, как это делает Theodoros Zelleke, но происходит интересная вещь, когда вы учитываете оба подхода:

введите здесь описание изображения

Во-первых, и это (очень) приятно, время линейно масштабируется в обоих решениях, как и следовало ожидать при использовании разреженного подхода. Но решение, которое я дал в этом ответе, всегда быстрее, особенно для больших n. Просто для удовольствия я также рассчитал плотный подход Теодорос Зеллеке из другого вопроса, который дает этот хороший график, показывающий различное масштабирование обоих типов решений и то, как очень рано, где-то около n = 75, решение здесь должно быть вашим выбором:

введите здесь описание изображения

Я недостаточно знаю о scipy.sparse, чтобы действительно понять, почему существуют различия между двумя разреженными подходами, хотя я сильно подозреваю об использовании разреженных матриц формата LIL. Может быть очень незначительный прирост производительности, хотя в коде много компактности, если перевести ответ TheodorosZelleke в формат COO. Но это остается в качестве упражнения для ОП!

person Jaime    schedule 17.01.2013
comment
Вы называете это избиением дохлой лошади, но я называю это увлекательным и чрезвычайно полезным. Спасибо за это! - person lip1; 17.01.2013

Это решение с использованием 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))
person Theodros Zelleke    schedule 17.01.2013
comment
Хороший! Я получаю ошибку MemoryError при n~10^4 — это ожидаемо? У меня нет хорошего представления о том, сколько промежуточного хранилища требуется. - person DSM; 17.01.2013
comment
@DSM Если вы замените hstacking и vstacking на coeff_mat = scipy.sparse.bmat([[n_to_M, m_to_M], [n_to_N, m_to_N]], format='csc'), он работает без проблем для n = 10**5, но все равно не работает для n = 10**6. - person Jaime; 17.01.2013
comment
@TheodrosZelleke Большое спасибо. Я добавил вопрос к вопросу, как вы и предложили. - person lip1; 17.01.2013

Здесь есть некоторый код, на который я уже смотрел: http://jkwiens.com/heat-equation-using-finite-difference/ Его функция реализует метод конечных разностей для решения уравнения теплопроводности с использованием пакета разреженных матриц scipy.

person BenDundee    schedule 17.01.2013