Wie man spärlich komplexe Matrizen effizient von meinem Code zu PETSc bringt

8

Was ist der effizienteste Weg, um eine komplexe Matrix mit geringer Dichte von meinem Fortran-Code auf PETSc zu übertragen? Ich verstehe, dass dies problemabhängig ist, deshalb habe ich versucht, so viele relevante Details wie möglich unten anzugeben.

Ich habe mit dem FEAST-Eigenwertlöser [1] für Probleme vom Typ , die Dimension der Matrizen A und B ist N , und fast die ganze Zeit wird damit verbracht, N × N komplexe lineare zu lösen System mit M0 rechts. N ist groß (Anzahl der FE-Basisfunktionen in 3D), M0 ist klein (in meinem Fall interessiert mich M0 ~ 20). Die Matrizen A und B sind real, symmetrisch und dünn, und das komplexe Problem, das gelöst werden muss, ist z A - B , wobei zAx=λBxABNN×NABzABzist eine komplexe Zahl. Der Autor von FEAST scheint zu vermuten, dass die Genauigkeit der Lösung für dieses lineare System nicht sehr hoch sein muss, um hochgenaue Eigenwerte und Eigenvektoren zu erhalten. Daher könnten einige schnelle iterative Löser eine gute Lösung dafür sein.

Bisher habe ich nur Lapack für das komplexe System verwendet, und das funktioniert hervorragend für auf meinem Computer. Für größere N weiß ich noch nicht, was der beste Löser ist, deshalb wollte ich einfach PETSc verwenden und dort mit den iterativen Lösern spielen.N<1500N

Ich habe einen einfachen C-Treiber geschrieben und ihn von Fortran aus aufgerufen, siehe [2] für den gesamten Code, aber das Problem liegt nur bei diesem Teil (Update: Ich habe hier alle Zeilen eingefügt, um die Matrix zu erstellen, wie ich gerade festgestellt habe dass dies relevant ist):

ierr = MatCreate(PETSC_COMM_WORLD,&A);CHKERRQ(ierr);
ierr = MatSetSizes(A,PETSC_DECIDE,PETSC_DECIDE,n,n);CHKERRQ(ierr);
ierr = MatSetFromOptions(A);CHKERRQ(ierr);
ierr = MatSetOption(A,MAT_IGNORE_ZERO_ENTRIES,PETSC_TRUE);CHKERRQ(ierr);
ierr = MatSetUp(A);CHKERRQ(ierr);
for (i=0; i<n; i++) col[i] = i;
ierr = MatSetValues(A,n,col,n,col,A_,INSERT_VALUES);CHKERRQ(ierr);

Was extrem langsam ist (dh für N ~ 1500 dauert dies vielleicht 2 Sekunden, während das tatsächliche Lösen sofort erfolgt), tatsächlich MatSetValuesdauert die Linie fast 100% der Zeit für die gesamte Eigenwertberechnung ... Die Matrix A_ ist a 2D-Matrix, die aus Fortran stammt. Ich habe versucht, das zu deaktivieren, MAT_IGNORE_ZERO_ENTRIESaber es machte keinen Unterschied. Ich denke also, dass das Problem einfach darin besteht, dass ich selbst für moderates N wie 1500 ein spärliches Matrixformat verwenden muss. Ist das richtig?

ABzABMatCreateSeqAIJWithArrays

ABzABzAxBxAxBxMatCreateSeqAIJWithArraysAB

Ich würde gerne wissen, ob dieser CSR-Ansatz für dieses Problem richtig ist oder ob ich es falsch mache (eindeutig ist mein ursprünglicher Ansatz mit dem MatSetValuesnicht optimal). Danke für alle Tipps.

[1] http://www.ecs.umass.edu/~polizzi/feast/

[2] https://github.com/certik/hfsolver/pull/14

[3] http://www.mcs.anl.gov/petsc/petsc-3.1/docs/manualpages/Mat/MatCreateSeqAIJWithArrays.html

Ondřej Čertík
quelle

Antworten:

7

Es ist wichtig, die Zuordnung korrekt vorzunehmen. Dies ist mit ziemlicher Sicherheit der Grund, warum Ihre Montage langsam war. Wenn Sie mit einer dichten Matrixdarstellung beginnen, scannen Sie sie einfach durch, indem Sie die Anzahl der Nicht-Nullen pro Zeile zählen, und rufen Sie dann an MatSeqAIJSetPreallocation(). Siehe diese FAQ . Die Option MAT_IGNORE_ZERO_ENTRIESsoll wirklich verwendet werden, wenn diese ansonsten dichten Blöcke eine leichte Sparsamkeit aufweisen, anstatt die gesamte Matrix in einem Aufruf aus einer dichten Matrix zu erstellen. Aus diesem Grund erfolgt die Vorbelegung nicht automatisch basierend auf der Sparsamkeit dieses einen Blocks.

Das Erstellen einer dichten Zwischenmatrix ist nicht speicherskalierbar, daher sollten Sie dies eventuell vermeiden. MatSetValues()ist eigentlich für logisch dichte Blöcke in einer spärlichen Matrix gedacht . Sie rufen normalerweise entweder einmal pro Zeile (oder eine Gruppe von Zeilen, typisch für FD-Methoden) oder einmal pro Element (typisch für FEM-Methoden) auf. Wenn Sie eine vorhandene zusammengesetzte Sparse-Matrix übersetzen, rufen Sie einfach MatSetValues()einmal pro Zeile auf. Wenn Sie die Zwischenmatrix lieber überspringen möchten (bessere Leistung und geringerer Speicher), rufen Sie einfach MatSetValues()einmal pro Element auf.

Beachten Sie, dass Sie PETSc direkt von Fortran aus aufrufen können, obwohl zwischen Fortran 77 und den neuesten Versionen Benutzer aller Fortran-Dialekte vorhanden sind. Die Schnittstellen werden für Sie als eifriger Anwender der neuesten Funktionen ziemlich grob aussehen. Vorschläge für wartbare Möglichkeiten zur Verbesserung der Unterstützung für die neuesten Dialekte sind willkommen.

Jed Brown
quelle
Danke für die hervorragende Antwort. Ich kann jetzt die Idee dahinter sehen MatSetValues(). Ist es für die Übersetzung einer vorhandenen Matrix nicht schneller, nur MatCreateSeqAIJWithArrayseinmal anzurufen, als anzurufen , MatSeqAIJSetPreallocationund dann MatSetValuesfür jede Zeile?
Ondřej Čertík
Ich werde Feedback zu den Fortran-Wrappern geben. Im Moment ist es für mich am schnellsten, den benötigten Treiber in C zu schreiben und diesen Treiber nur selbst zu verpacken.
Ondřej Čertík
1
Sicher, aber dazu müssen Sie mit einer CSR-Matrix beginnen, die nach derselben Konvention erstellt wurde. Wenn Sie ein anderes Format verwenden, packen Sie jeweils nur eine Zeile. Wenn Sie vorab zuweisen, ist die aufgewendete Zeit im MatSetValues()Vergleich zur Berechnung der Einträge und zur Lösung des Systems sehr gering. Außerdem MatSetValues[Blocked][Local]()verhindert das Zusammenstellen mit, dass Ihr Code von einem bestimmten Matrixformat abhängt, sodass Sie zur Laufzeit Speicherformate auswählen können.
Jed Brown