Welche Programmierstrategien kann ich zum einfachen Ändern von Algorithmusparametern verwenden?

17

Die Entwicklung wissenschaftlicher Algorithmen ist ein sehr iterativer Prozess, bei dem häufig viele Parameter geändert werden, die ich entweder im Rahmen meines experimentellen Designs oder im Rahmen der Optimierung der Algorithmusleistung ändern möchte. Welche Strategien kann ich verwenden, um diese Parameter so zu strukturieren, dass ich sie zwischen den Iterationen problemlos ändern und neue hinzufügen kann?

Scottie T
quelle

Antworten:

14

Es ist für den Benutzer umständlich, jeden Aspekt eines Algorithmus anzugeben. Wenn der Algorithmus verschachtelte Komponenten zulässt, ist keine endliche Anzahl von Optionen ausreichend. Daher ist es wichtig, dass Optionen nicht unbedingt auf die oberste Ebene "aufsprudeln", wie dies bei expliziten Argumenten oder Vorlagenparametern der Fall ist. Dies wird in der Softwareentwicklung manchmal als "Konfigurationsproblem" bezeichnet. Ich glaube, PETSc verfügt über ein einzigartig leistungsfähiges System für das Konfigurationsmanagement. Es ähnelt dem Service Locator-Muster in Martin Fowlers Aufsatz über die Umkehrung der Kontrolle .

Das Konfigurationssystem von PETSc basiert auf einer Kombination aus benutzerdefinierten Konfigurationen, die von den Solver-Objekten (mit Abfragen zum Abrufen und Festlegen) und der Optionsdatenbank verwaltet werden. Jede Komponente der Simulation kann eine Konfigurationsoption, einen Standardwert und einen Ort für das Ergebnis deklarieren. Verschachtelte Objekte haben Präfixe, die zusammengesetzt werden können, sodass jedes Objekt, das konfiguriert werden muss, unabhängig adressiert werden kann. Die Optionen selbst können über die Befehlszeile, die Umgebung, die Konfigurationsdateien oder über Code gelesen werden. Wenn eine Option deklariert wird, werden eine Hilfezeichenfolge und eine Manpage angegeben, damit die -helpOption verständlich ist und eine ordnungsgemäß verknüpfte GUI geschrieben werden kann.

Der Benutzer ruft eine SetFromOptionsMethode auf, mit der sich ein Objekt anhand der Befehlszeilenoptionen selbst konfigurieren kann. Der Aufruf dieser Funktion ist optional und kann möglicherweise nicht aufgerufen werden, wenn der Benutzer (der Code schreibt, der PETSc aufruft) die Optionen über eine andere Schnittstelle bereitstellt. Es wird dringend empfohlen, dass der Benutzer die Optionsdatenbank verfügbar macht, da dies dem Endbenutzer (der die Anwendung ausführt) viel Macht verleiht, dies ist jedoch nicht erforderlich.

Eine typische Konfiguration, die über aufgerufen wird

PetscObjectOptionsBegin(object); /* object has prefix and descriptive string */
PetscOptionsReal("-ts_atol",                                      /* options database key */
                 "Absolute tolerance for local truncation error", /* long description */
                 "TSSetTolerances",                               /* function and man page on topic */
                  ts->atol,                                       /* current/default value *?
                  &ts->atol,                                      /* place to store value */
                  &option_set);                                   /* TRUE if the option was set */
PetscOptionsList("-ts_type","Time stepping method","TSSetType",TSList,
                 defaultType,typeName,sizeof typeName,&option_set);
TSAdaptSetFromOptions(ts->adapt);                                 /* configures adaptive controller method */
/* ... many others */
/* ... the following is only called from implicit implementations */
SNESSetFromOptions(ts->snes);                                     /* configure nonlinear solver. */
PetscOptionsEnd();

Anmerkungen:

  • PetscOptionsList()präsentiert dem Benutzer eine Auswahl aus einer dynamischen Liste. Es gibt eine Plugin-Architektur, mit der sich neue Implementierungen als erstklassig für Aufrufer ausgeben können. (Diese Implementierungen können in gemeinsam genutzten Bibliotheken abgelegt und als erstklassig verwendet werden, ohne dass Programme neu kompiliert werden müssen.)
  • SNESSetFromOptions() Konfiguriert rekursiv die linearen Löser, Vorkonditionierer und alle anderen Komponenten, die konfiguriert werden müssen.
Jed Brown
quelle
11

Dieses Problem trat mehrmals auf, als ich meine eigenen Simulationscodes von Grund auf neu entwickelte: Welche Parameter in einer Eingabedatei gespeichert werden sollten, welche von der Befehlszeile übernommen werden sollten usw. Nach einigen Experimenten stellte sich Folgendes als effizient heraus. (Es ist nicht so weit fortgeschritten wie PETSc.)

Anstatt ein experimentelles Simulationsprogramm zu schreiben, bin ich eher geneigt, ein Python-Paket zu schreiben, das alle Funktionen und Klassen enthält, die zum Ausführen der Simulation erforderlich sind. Die traditionelle Eingabedatei wird dann durch ein kleines Python-Skript mit 5 bis 10 Codezeilen ersetzt. Einige Zeilen beziehen sich normalerweise auf das Laden von Datendateien und das Festlegen der Ausgabe. Andere sind Anweisungen für die eigentliche Berechnung. Gute Standardwerte für optionale Argumente im Python-Paket ermöglichen es Anfängern, die Bibliothek für einfache Simulationen zu verwenden, während der fortgeschrittene Benutzer weiterhin Zugriff auf alle Schnickschnack hat.

Einige Beispiele:

Toon Verstraelen
quelle
Das ist großartig, aber ich denke, es ist orthogonal zum Konfigurationsproblem. Wenn Sie einen hierarchischen oder verschachtelten Algorithmus angeben müssen, können Sie Optionen für viele innere Objekte angeben. Der Code, der diese aufruft, sollte eigentlich nicht einmal über ihre Existenz informiert sein, da sich die Anzahl der Ebenen und die Arten der Verschachtelung ändern können. Dies ist das Problem all dieser Entscheidungen, die "sprudeln". Mit Ihrem Python-Code auf hoher Ebene können Sie das Festlegen dieser Optionen "einfach" gestalten, müssen sie jedoch weiterhin im Code angeben. Ich denke, das ist im Allgemeinen keine gute Sache.
Jed Brown
xmonad verwendet diese Methode zum Konfigurieren des Fenstermanagers für X.
rcollyer
2

Als erstes würde ich den Algorithmus und die Software so allgemein wie möglich machen. Ich habe das auf die harte Tour gelernt.

Angenommen, Sie beginnen mit einem einfachen Testfall. Das geht schneller. Wenn Sie jedoch die Software für diesen ersten Fall zu spezifisch (zu wenige Parameter) gemacht haben, verlieren Sie immer mehr Zeit, sie jedes Mal anzupassen, wenn Sie einen neuen Freiheitsgrad hinzufügen. Was ich jetzt tue, ist, dass ich am Anfang mehr Zeit damit verbringe, das Ding ziemlich allgemein zu machen und die Variation der Parameter zu erhöhen, während ich mich vorwärts bewege.

Dies erfordert von Anfang an mehr Tests, da Sie vom Startpunkt an mehr Parameter haben. Dies bedeutet jedoch, dass Sie mit dem Algorithmus bei Null oder sehr niedrigen Kosten viel spielen können.

Beispiel: Der Algorithmus berechnet das Oberflächenintegral des Skalarprodukts zweier Vektorfunktionen. Nehmen Sie nicht von Anfang an die Größe, Geometrie und Diskretisierung der Oberfläche an, wenn Sie dies in Zukunft ändern möchten. Machen Sie eine Skalarproduktfunktion, machen Sie die Oberfläche so allgemein wie möglich, berechnen Sie das Integral auf formale Weise. Sie können jede Funktion einzeln testen.

Zu Beginn können Sie beginnen, über einfache Geometrien zu integrieren, und am Anfang können Sie Parameter als Konstanten deklarieren. Wenn Sie die Geometrie im Laufe der Zeit ändern möchten, können Sie dies problemlos tun. Hätten Sie zu Beginn Annahmen getroffen, müssten Sie den gesamten Code jedes Mal ändern.

jbcolmenares
quelle