Come gestire la complessità del codice numerico, ad esempio, quando si ha a che fare con grandi matrici giacobine?


10

Sto risolvendo un sistema non lineare di equazioni accoppiate e ho calcolato il giacobino del sistema discretizzato. Il risultato è davvero complicato, di seguito sono (solo!) Le prime 3 colonne di una matrice ,3×9

Matrice giacobina parziale

(La complessità sorge, in parte, perché lo schema numerico richiede un adattamento esponenziale per la stabilità.)

Ho una domanda abbastanza generica sull'implementazione di codici numerici usando i giacobini.

Posso andare avanti e implementare questa matrice nel codice. Ma la mia intuizione mi sta dicendo di aspettarmi qualche giorno (forse settimane!) Di noioso debugging a causa della pura complessità e dell'inevitabile introduzione di errori. Come si fa a far fronte a complessità come questa in codice numerico, sembra inevitabile ?! Usi la generazione automatica di codice da pacchetti simbolici (quindi modifica il codice a mano)?

Per prima cosa ho intenzione di eseguire il debug dell'analitico Jacobian con un'approssimazione di differenza finita, dovrei essere a conoscenza di eventuali insidie? Come gestisci problemi simili nel tuo codice?

Aggiornare

Sto scrivendo questo codice in Python e ho usato sympy per generare il giacobino. Forse posso usare la funzione di generazione del codice ?


Quale sistema di algebra del computer stai usando per generare le espressioni giacobine? Se stai usando Maple, potresti voler esaminare il codegenpacchetto in esso in quanto può generare codice C o Fortran compatto ed efficiente per ciascuna o tutte le espressioni automaticamente.
Pedro,

Ci sono così tante risposte utili qui, non ha senso sceglierne una. Devo fare di questo un post Wiki della community?
boyfarrell,

Risposte:


6

Una sola parola: modularità .

Ci sono molte espressioni ripetute nel tuo giacobino che potrebbero essere scritte come una propria funzione. Non c'è motivo per cui tu scriva la stessa operazione più di una volta, e questo renderà più facile il debug; se lo scrivi solo una volta c'è un solo posto per un errore (in teoria).

Il codice modulare faciliterà anche i test; Puoi scrivere test per ogni componente del tuo Jacobiano invece di provare a testare l'intera matrice. Ad esempio, se scrivi la tua funzione am () in modo modulare, puoi facilmente scrivere test di integrità per esso, verificare se la stai differenziando correttamente, ecc.

Un altro suggerimento sarebbe quello di esaminare le librerie automatiche di differenziazione per l'assemblaggio del giacobino. Non c'è garanzia che siano privi di errori, ma probabilmente ci saranno meno debug / meno errori rispetto alla scrittura. Ecco alcuni che potresti voler guardare:

  • Sacado (Sandia Labs)
  • ADIC (Argonne)

Scusa, ho appena visto che stai usando Python. ScientificPython ha il supporto per AD.


Buon Consiglio. Le espressioni intermedie spesso non hanno bisogno di avere le proprie funzioni, basta memorizzarle in variabili intermedie.
David Ketcheson,

5

Fammi pesare qui con alcune parole di cautela, precedute da una storia. Molto tempo fa, ho lavorato con un collega quando avevo appena iniziato. Aveva un problema di ottimizzazione da risolvere, con un obiettivo piuttosto disordinato. La sua soluzione era quella di generare i derivati ​​analitici per un'ottimizzazione.

Il problema che ho visto era che questi derivati ​​erano cattivi. Generati usando Macsyma, convertiti in codice fortran, erano ciascuno dozzine di dichiarazioni di continuazione lunghe. In effetti, il compilatore Fortran si è arrabbiato per questo, poiché ha superato il numero massimo di dichiarazioni di continuazione. Mentre abbiamo trovato una bandiera che ci ha permesso di aggirare quel problema, c'erano altri problemi.

  • Nelle espressioni lunghe, come sono comunemente generate dai sistemi CA, esiste il rischio di un'enorme cancellazione sottrattiva. Calcola un sacco di numeri grandi, solo per scoprire che tutti si annullano a vicenda per produrre un piccolo numero.

  • Spesso i derivati ​​generati analiticamente sono in realtà più costosi da valutare rispetto ai derivati ​​generati numericamente usando differenze finite. Un gradiente per n variabili può richiedere più di n volte il costo della valutazione della funzione obiettivo. (Potresti essere in grado di risparmiare un po 'di tempo perché molti dei termini possono essere riutilizzati tra le varie derivate, ma ciò ti costringerà anche a fare un'attenta codifica manuale, invece di utilizzare espressioni generate dal computer. E ogni volta che scrivi un codice matematico espressioni, la probabilità di un errore non è banale. Assicurati di verificare la precisione di questi derivati.)

Il punto della mia storia è che queste espressioni generate dalla CA hanno problemi propri. La cosa divertente è che il mio collega era davvero orgoglioso della complessità del problema, che stava chiaramente risolvendo un problema davvero difficile perché l'algebra era così brutta. Quello che non penso che considerasse era se quell'algebra stava effettivamente calcolando la cosa corretta, se lo stava facendo in modo accurato e lo stava facendo in modo così efficiente.

Se fossi stata la persona più anziana al momento in questo progetto, avrei letto l'atto antisommossa. Il suo orgoglio lo indusse a utilizzare una soluzione probabilmente inutilmente complessa, senza nemmeno verificare che un gradiente basato sulla differenza finita fosse adeguato. Scommetto che abbiamo trascorso forse una settimana uomo per far funzionare questa ottimizzazione. Per lo meno, l'avrei consigliato di testare attentamente il gradiente prodotto. È stato preciso? Quanto è stato accurato rispetto ai derivati ​​a differenza finita? In effetti, ci sono strumenti in giro oggi che restituiranno anche una stima dell'errore nella loro previsione derivata. Questo è certamente vero per il codice di differenziazione adattativa, (derivato) che ho scritto in MATLAB.

Prova il codice. Verifica i derivati.

Ma prima di fare QUALUNQUE di questo, considera se altri, migliori schemi di ottimizzazione sono un'opzione. Ad esempio, se stai eseguendo un adattamento esponenziale, allora c'è un'ottima possibilità che tu possa usare un minimo quadrato non lineare partizionato (a volte chiamato minimo quadrato separabile. Penso che fosse il termine usato da Seber e Wild nel loro libro.) L'idea consiste nel suddividere l'insieme di parametri in insiemi intrinsecamente lineari e intrinsecamente non lineari. Utilizzare un'ottimizzazione che funziona solo con i parametri non lineari. Dato che questi parametri sono "conosciuti", i parametri intrinsecamente lineari possono essere stimati usando minimi quadrati lineari semplici. Questo schema ridurrà lo spazio dei parametri nell'ottimizzazione. Rende il problema più solido, poiché non è necessario trovare valori iniziali per i parametri lineari. Riduce la dimensionalità del tuo spazio di ricerca, rendendo il problema più rapido. Ancora una volta ho fornitouno strumento per questo scopo , ma solo in MATLAB.

Se si utilizzano i derivati ​​analitici, codificarli per riutilizzare i termini. Questo può essere un serio risparmio di tempo e può effettivamente ridurre i bug, risparmiando tempo. Ma poi controlla quei numeri!


5

Esistono diverse strategie da considerare:

  1. Trova i derivati ​​in forma simbolica usando un CAS, quindi esporta il codice per calcolare i derivati.

  2. Utilizzare uno strumento di differenziazione automatica (AD) per produrre codice che calcola le derivate dal codice per calcolare le funzioni.

  3. Usa approssimazioni alle differenze finite per approssimare il giacobino.

La differenziazione automatica potrebbe produrre un codice più efficiente per il calcolo dell'intero giacobino, quindi utilizzando il calcolo simbolico per produrre una formula per ogni voce nella matrice. Le differenze finite sono un buon modo per ricontrollare i tuoi derivati.



1

Oltre agli eccellenti suggerimenti di BrianBorcher, un altro possibile approccio per funzioni con valori reali è quello di utilizzare l'approssimazione derivativa a fasi complesse (vedere questo articolo (paywalled) e questo articolo ). In alcuni casi, questo approccio produce derivate numeriche più accurate al costo di cambiare i valori delle variabili nella tua funzione da reali a complessi. Il secondo articolo elenca alcuni casi in cui la complessa approssimazione della funzione di step potrebbe non funzionare.

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.