Complexidade da inversão da matriz em numpy

11

Estou resolvendo equações diferenciais que exigem inverter matrizes quadradas densas. Essa inversão de matriz consome a maior parte do meu tempo de computação, então eu queria saber se estou usando o algoritmo mais rápido disponível.

Minha escolha atual é numpy.linalg.inv . Dos meus números, vejo que ele é escalado como que n é o número de linhas, de modo que o método parece ser a eliminação gaussiana.O(n3)

Segundo a Wikipedia , existem algoritmos mais rápidos disponíveis. Alguém sabe se existe uma biblioteca que os implementa?

Eu me pergunto, por que não está entorpecido usando esses algoritmos mais rápidos?

physicsGuy
fonte
Você precisa executar suas matrizes antes. Olhe para Scipy. Dispensa por sua ajuda. Ele contém muitas ferramentas necessárias.
Tobal 11/02
@ Global não tenho certeza que eu sigo ... como você "executar" uma matriz? e exatamente como scipy.sparseajudaria?
GoHokies
O @GoHokies scipy é um complemento para o numpy. Matrizes densas / esparsas devem ser implementadas bem antes de você fazer alguns cálculos; isso melhora seus cálculos. Por favor, leia este docs.scipy.org/doc/scipy/reference/sparse.html que me explica melhor do que eu com meu inglês ruim.
Tobal
@ Global A questão refere-se especificamente a matrizes densas , então não vejo como scipy.sparseé relevante aqui?
Christian Clason
2
@ Global - Acho que ainda não entendo. O que exatamente você quer dizer com "pré-forma suas matrizes" e "matrizes devem ser implementadas bem antes de você fazer alguns cálculos"? Com relação ao seu último comentário, certamente você concordará que as técnicas que podem ser usadas para matrizes esparsas e densas são muito diferentes.
Wolfgang Bangerth

Respostas:

20

(Isso está demorando muito para comentários ...)

Suponho que você realmente precise calcular uma inversa no seu algoritmo. 1 Primeiro, é importante observar que esses algoritmos alternativos não são realmente mais rápidos , apenas que eles têm melhor complexidade assintótica (o que significa que o número necessário de operações elementares cresce mais lentamente). De fato, na prática, eles são realmente (muito) mais lentos que a abordagem padrão (para o dado ), pelos seguintes motivos:n

  1. A anotação oculta uma constante diante do poder de , que pode ser astronomicamente grande - tão grande que pode ser muito menor que para qualquer que pode ser manuseado por qualquer computador no futuro próximo. (Este é o caso do algoritmo Coppersmith – Winograd, por exemplo.) n C 1 n 3 C 2 n 2. x nOnC1n3C2n2.xn

  2. A complexidade pressupõe que toda operação (aritmética) leva o mesmo tempo - mas isso está longe de ser verdade na prática: multiplicar um monte de números com o mesmo número é muito mais rápido do que multiplicar a mesma quantidade de números diferentes . Isso se deve ao fato de que o maior gargalo na computação atual está colocando os dados no cache, não nas operações aritméticas reais desses dados. Portanto, um algoritmo que pode ser reorganizado para ter a primeira situação (chamada de reconhecimento de cache ) será muito mais rápido do que aquele em que isso não for possível. (Este é o caso do algoritmo Strassen, por exemplo.)

Além disso, a estabilidade numérica é pelo menos tão importante quanto o desempenho; e aqui, novamente, a abordagem padrão geralmente vence.

Por esse motivo, as bibliotecas padrão de alto desempenho (BLAS / LAPACK, que Numpy chama quando você pede uma computação inversa) geralmente implementam apenas essa abordagem. Obviamente, existem implementações Numpy de, por exemplo, o algoritmo de Strassen, mas um algoritmo ajustado manualmente no nível da montagem supera profundamente um algoritmo escrito em uma linguagem de alto nível para qualquer tamanho de matriz razoável.O ( n 2. x )O(n3)O(n2.x)


1 Mas eu ficaria errado se não afirmasse que isso raramente é realmente necessário: sempre que você precisar calcular um produto , você deve resolver o sistema linear (por exemplo, use ) e use - isso é muito mais estável e pode ser feito (dependendo da estrutura da matriz ) muito mais rapidamente. Se você precisar usar várias vezes, poderá precompor uma fatoração de (que geralmente é a parte mais cara da solução) e reutilizá-la posteriormente.A x = b x A A - 1 AA1bAx=bnumpy.linalg.solvexAA1A

Christian Clason
fonte
Ótima resposta, obrigado, senhor, em particular por apontar o diabo nos detalhes (constantes em grande notação O) que fazem uma grande diferença entre velocidade teórica e velocidade prática.
gaborous
Eu acho que a parte "inversa raramente é necessária" deve ser enfatizada mais. Se o objetivo é resolver um sistema de equações diferenciais, não parece provável que seja necessário um inverso completo.
Jared Goguen
@o_o Bem, esse foi o meu primeiro comentário original (que eu apaguei depois de consolidá-los todos em uma resposta). Mas eu pensei que, para o benefício do site (e leitores posteriores), uma resposta deve responder à pergunta real da pergunta (que é razoável e no tópico), mesmo se houver um problema XY por trás dele. Além disso, eu não quero parecer demasiado admoestando ...
Christian Clason
1
Como escrevi, em quase todos os casos, você pode reescrever seu algoritmo para substituir operações envolvendo o inverso com a solução do sistema linear correspondente (ou, nesse caso, sequência de sistemas lineares) - se você estiver interessado, poderá fazer uma pergunta separada sobre isso ("posso evitar a inversão de matrizes neste algoritmo?"). E sim, como o número de matrizes não depende de , a complexidade ainda é a mesma (você apenas obtém uma constante maior - por um fator de quatro no seu caso). n
Christian Clason
1
@Heisenberg: Depende da estrutura dos trabalhos de decomposição - LU, Cholesky ou mesmo QR. A questão (que é feita em qualquer texto sobre álgebra linear numérica) é que a aplicação da decomposição é barata em comparação com a computação; portanto, você faz a última apenas uma vez e depois a aplica várias vezes. A
Christian Clason
4

Provavelmente, você deve observar que, enterrada profundamente no código-fonte numpy (consulte https://github.com/numpy/numpy/blob/master/numpy/linalg/umath_linalg.c.src ), a rotina inv tenta chamar a função dgetrf do pacote LAPACK do sistema, que executa uma decomposição da LU da sua matriz original. Isso é moralmente equivalente à eliminação gaussiana, mas pode ser ajustado para uma complexidade um pouco menor usando algoritmos de multiplicação de matriz mais rápidos em um BLAS de alto desempenho.

Se você seguir esta rota, deve ser avisado de que forçar toda a cadeia de bibliotecas a usar a nova biblioteca em vez do sistema que veio com sua distribuição é bastante complexo. Uma alternativa nos sistemas modernos de computadores é examinar métodos paralelizados usando pacotes como scaLAPACK ou (no mundo python) petsc4py. No entanto, estes são tipicamente mais felizes sendo usados ​​como solucionadores iterativos para sistemas de álgebra linear do que aplicados a métodos diretos e o PETSc, em particular, tem como alvo sistemas esparsos mais que os densos.

origimbo
fonte