Complessità di inversione di matrice in numpy


11

Sto risolvendo equazioni differenziali che richiedono di invertire matrici quadrate dense. Questa inversione di matrice consuma la maggior parte del mio tempo di calcolo, quindi mi chiedevo se sto usando l'algoritmo più veloce disponibile.

La mia scelta attuale è numpy.linalg.inv . Dalla mia numerazione vedo che si ridimensiona come dove n è il numero di righe, quindi il metodo sembra essere l'eliminazione gaussiana.O(n3)

Secondo Wikipedia , ci sono algoritmi più veloci disponibili. Qualcuno sa se esiste una libreria che li implementa?

Mi chiedo, perché non è intorpidito usando questi algoritmi più veloci?


È necessario eseguire le matrici prima. Guarda Scipy. Sparse per il vostro aiuto. Contiene molti strumenti di cui hai bisogno.
Tobal,

@Tobal non sono sicuro di seguire ... come "eseguiresti" una matrice? ed esattamente come sarebbe d' scipy.sparseaiuto?
GoHokies,

@GoHokies scipy è un complemento di numpy. Le matrici dense / sparse devono essere implementate molto prima di eseguire alcuni calcoli, migliora i calcoli. Si prega di leggere questo docs.scipy.org/doc/scipy/reference/sparse.html che spiega meglio di me con il mio cattivo inglese.
Tobal,

@Tobal La domanda si riferisce specificamente alle matrici dense , quindi non vedo quanto scipy.sparsesia rilevante qui?
Christian Clason,

2
@Tobal - Penso di non capire ancora. Cosa intendi esattamente con "preforma le tue matrici" e "le matrici devono essere implementate molto prima di fare dei calcoli"? Per quanto riguarda il tuo ultimo commento, sicuramente sarai d'accordo sul fatto che le tecniche che possono essere utilizzate per matrici sparse e dense sono molto diverse.
Wolfgang Bangerth,

Risposte:


21

(Sta diventando troppo lungo per i commenti ...)

Suppongo che in realtà devi calcolare un inverso nel tuo algoritmo. 1 In primo luogo, è importante notare che questi algoritmi alternativi non sono effettivamente dichiarati più veloci , ma solo che hanno una migliore complessità asintotica (il che significa che il numero richiesto di operazioni elementari cresce più lentamente). In pratica, in pratica questi sono (molto) più lenti dell'approccio standard (per una data ), per i seguenti motivi:n

  1. La notazione nasconde una costante di fronte alla potenza di , che può essere astronomicamente grande - così grande che può essere molto più piccola di per qualsiasi che può essere gestito da qualsiasi computer nel prossimo futuro. (Questo è il caso dell'algoritmo Coppersmith – Winograd, per esempio.) n C 1 n 3 C 2 n 2. x nOnC1n3C2n2.xn

  2. La complessità presuppone che ogni operazione (aritmetica) richieda lo stesso tempo, ma nella pratica reale ciò è tutt'altro che vero: moltiplicare un gruppo di numeri con lo stesso numero è molto più veloce che moltiplicare la stessa quantità di numeri diversi . Ciò è dovuto al fatto che il principale collo di bottiglia nell'attuale elaborazione sta portando i dati nella cache, non le effettive operazioni aritmetiche su tali dati. Quindi un algoritmo che può essere riorganizzato per avere la prima situazione (chiamata cache-aware ) sarà molto più veloce di quello in cui ciò non è possibile. (Questo è il caso dell'algoritmo Strassen, per esempio.)

Inoltre, la stabilità numerica è importante almeno quanto le prestazioni; e qui, di nuovo, l'approccio standard di solito vince.

Per questo motivo, le librerie standard ad alte prestazioni (BLAS / LAPACK, che Numpy chiama quando gli chiedi di calcolare un inverso) di solito implementano solo questo approccio. Naturalmente, ci sono implementazioni Numpy di, ad esempio, l'algoritmo di Strassen là fuori, ma un algoritmo sintonizzato a mano a livello di assemblaggio batterà profondamente un algoritmo scritto in un linguaggio di alto livello per qualsiasi dimensione di matrice ragionevole.O ( n 2. x )O(n3)O(n2.x)


1 Ma mi dispiacerebbe se non facessi notare che questo è molto raramente davvero necessario: ogni volta che devi calcolare un prodotto , dovresti invece risolvere il sistema lineare (ad es. usando ) e usa invece - questo è molto più stabile e può essere fatto (a seconda della struttura della matrice ) molto più velocemente. Se è necessario utilizzare più volte, è possibile pre-calcolare una fattorizzazione di (che di solito è la parte più costosa della risoluzione) e riutilizzarla in seguito.A x = b x A A - 1 AA1bAx=bnumpy.linalg.solvexAA1A


Ottima risposta, grazie signore, in particolare per aver sottolineato il diavolo nei dettagli (costanti in notazione O grande) che fa una grande differenza tra velocità teorica e velocità pratica.
gaboroso

Penso che la parte "raramente sia necessaria" dovrebbe essere enfatizzata maggiormente. Se lo scopo è risolvere un sistema di equazioni differenziali, non sembra probabile che sia necessario un inverso completo.
Jared Goguen,

@o_o Bene, quello è stato il mio primo commento originale (che ho eliminato dopo averli consolidati tutti in un'unica risposta). Ma ho pensato, a beneficio del sito (e dei lettori successivi), una risposta dovrebbe rispondere alla domanda effettiva nella domanda (che è sia ragionevole che in argomento), anche se c'è un problema XY dietro di esso. Inoltre, non volevo sembrare troppo ammonitore ...
Christian Clason,

1
Come ho scritto, in quasi tutti i casi puoi riscrivere il tuo algoritmo per sostituire le operazioni che coinvolgono l'inverso con la risoluzione del sistema lineare corrispondente (o, in questo caso, la sequenza di sistemi lineari) - se sei interessato, potresti fare una domanda separata su che ("posso evitare di invertire le matrici in questo algoritmo?"). E sì, poiché il numero di matrici non dipende da , la complessità è sempre la stessa (ottieni solo una costante più grande - di un fattore quattro nel tuo caso). n
Christian Clason,

1
@Heisenberg: dipende dalla struttura delle opere di decomposizione - LU, Cholesky o QR. Il punto (che viene fatto in qualsiasi testo sull'algebra lineare numerica) è che applicare la decomposizione è economico rispetto a calcolarlo, quindi lo fai solo una volta e poi lo applichi molte volte. A
Christian Clason,

4

Probabilmente dovresti notare che, sepolto nel profondo del codice sorgente numpy (vedi https://github.com/numpy/numpy/blob/master/numpy/linalg/umath_linalg.c.src ) la routine inv tenta di chiamare la funzione dgetrf dal pacchetto LAPACK di sistema, che quindi esegue una decomposizione LU della matrice originale. Questo è moralmente equivalente all'eliminazione gaussiana, ma può essere sintonizzato su una complessità leggermente inferiore utilizzando algoritmi di moltiplicazione a matrice più veloce in un BLAS ad alte prestazioni.

Se segui questa strada, dovresti essere avvisato che forzare l'intera catena di librerie a usare la nuova libreria piuttosto che quella di sistema fornita con la tua distribuzione è abbastanza complessa. Un'alternativa ai moderni sistemi informatici è esaminare metodi paralleli usando pacchetti come scaLAPACK o (nel mondo di Python) petsc4py. Tuttavia, questi sono in genere più felici quando vengono utilizzati come solutori iterativi per sistemi di algebra lineare rispetto a metodi diretti e PETSc in particolare mira a sistemi sparsi più di quelli densi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.