NUMERISCHES PRAKTIKUM Numerische Berechnung der topologischen Entropie eines diskreten ” dynamischen Systems bezüglich einer endlichen Partition“ Christoph Kawan April 2006 1 Theorieteil Ziel des Praktikums war es, den von Froyland, Junge und Ochs in [1] entwickelten Algorithmus zur approximativen Berechnung der topologischen Entropie eines diskreten dynamischen Systems bzgl. einer endlichen Partition des Phasenraums zu implementieren. Bevor ich auf die Details der Implementierung eingehe, will ich kurz den theoretischen Hintergrund erläutern: Gegeben sei eine stetige Abbildung T : M → M eines kompakten metrischen Raums (M, d). Ferner sei A = {A1 , . . . , Aq } eine beliebige endliche Partition von M . Dann definiert man für jede natürliche Zahl N ∈ N die Menge WN (T, A) := [a0 , . . . , aN −1 ]|∃x ∈ M : T i (x) ∈ Aai , 0 ≤ i ≤ N − 1 , und bezeichnet mit |WN (T, A)| die Anzahl ihrer Elemente. Man definiert: 1 log |WN (T, A)|. N →∞ N h∗ (T, A) := lim Aufgrund der Subadditivität der Folge cN := log |WN (T, A)| existiert dieser Limes und es gilt: h∗ (T, A) = inf N ≥1 1 log |WN (T, A)|. N Die so definierte reelle Zahl h∗ (T, A) wird auch als die topologische Entropie von T bzgl. der Partition A bezeichnet. Da offensichtlich stets |WN (T, A)| ≤ q N gilt, folgt: h∗ (T, A) ≤ lim N →∞ 1 1 log |WN (T, A)| ≤ lim log q N = log q. N →∞ N N 2 Folgender Satz, dessen Beweis in [1] zu finden ist, stellt den Zusammenhang zwischen der Größe h∗ (T, A) und der topologischen Entropie h(T )1 her: 1.1 Satz: W −i (1) Es gelte ∞ i=0 T (A) = B, wobei B die Borel’sche σ-Algebra von M bezeichnet. Dann gilt: h(T ) ≤ h∗ (T, A). (2) h(T ) ≤ lim inf diam A→0 h∗ (T, A). Eine Partition, die die Voraussetzung von Teil 1 des Satzes erfüllt, nennt man auch erzeugend. Teil 2 des Satzes besagt, dass man durch Verfeinerung einer beliebigen Partition A im Limes eine obere Schranke für h(T ) erhält. Der in [1] präsentierte Algorithmus liefert beliebig genaue Approximationen (in Form von oberen Schranken) der Größe h∗ (T, A). Das von Michael Dellnitz und Oliver Junge entwickelte Softwarepaket GAIO2 (Global Analysis of Invariant Objects) erlaubt eine effiziente Umsetzung des Algorithmus für Abbildungen, die auf kompakten durch Quader approximierbaren Teilmengen des Rn definiert sind. Im folgenden Abschnitt werden die Details der Implementierung des Algorithmus mit Hilfe der Programms MATLAB und des Softwarepakets GAIO erläutert. Unabdingbare Voraussetzungen zum Verständnis dieser Erläuterungen sind die Kenntnis von [1] sowie der Programmiersprache C. Außerdem sind die in [3] und [4] zu findenden Informationen über GAIO hilfreich. 2 Implementierung 2.1 Die Modelldatei Die Definition der Abbildung, deren Entropie berechnet werden soll, erfolgt in einer C-Datei, die typischerweise den folgenden selbst erklärenden Aufbau besitzt: 1 2 3 4 5 6 7 8 9 10 11 12 1 2 char *typ = "map"; /*map, ode or newton (so far)*/ char *name = "Logistic Map"; /*a descriptive name*/ int dim = 1; /*dimension of phase space*/ int paramDim = 1; /*dimension of parameter space*/ char *paramNames[] = { "a" };/*parameter names*/ double a = 4.0; /*their initial values*/ double c[1] = { 0.5 }; /*center of outer box*/ double r[1] = { 0.5 }; /*radius of outer box*/ double tFinal = 1; /*integration time*/ /*"right hand side" of the system*/ void rhs(double *x, double *u, double *y) { Eine Definition der topologischen Entropie findet man z.B. in [2], S.108-109. siehe http://www.upb.de/math/˜agdellnitz/software/gaio.html 3 13 y[0] = a*x[0]*(1-x[0]); 14 } Wie man in diesem Beispiel sieht, kann die Abbildung auch von Parametern abhängen. Hier handelt es sich um die durch den Parameter a parametrisierte logistische Familie fa (x) = ax(1 − x). Der DEFAULT-Wert des Parameters a wird in Zeile 6 auf 4.0 gesetzt. Der Phasenraum - das kompakte Intervall [0, 1] wird in den Zeilen 7 und 8 definiert durch Angabe des Mittelpunkt- und des Radiusvektors. Die C-Datei muss zu einer Objektdatei ( shared object“, Endung ” so) compiliert werden, die anschließend mittels GAIO-Routinen in MATLAB eingebunden werden kann. Mit dem GNU C-Compiler des Betriebssystems LINUX geschieht dies durch folgenden Aufruf: gcc logmap.c -o logmap.so -shared Dabei ist logmap.c der Name der C-Datei, in der obiger Quellcode abgespeichert ist. 2.2 Der MATLAB-Code Folgender MATLAB-Code realisiert den Algorithmus zur approximativen Berechnung der Größe h∗ (T, A): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 m = Model(’logmap’); i = Integrator(’Map’); i.model = m; t = Tree([0.5],[0.5]); t.integrator = i; A_depth = 1; B_depth = 12; to_be_subdivided = 8; for k=0:B_depth-1, t.set_flags(’all’,to_be_subdivided); t.subdivide(to_be_subdivided); end p = Points(’MonteCarlo’,m.dim,500); B = t.matrix(p); R = comp_hgtm(A_depth,B_depth,B); h = eigs(R,1); Die Objektdatei logmap.so wird mit Hilfe des Modell-Objekts Model von GAIO eingebunden (Zeile 1). Dabei muss man darauf acht geben, dass diese im richtigen Verzeichnis liegt, bzw. durch den MATLAB-Befehl addpath das entsprechende Verzeichnis zu den Suchpfaden von MATLAB hinzufügen. Zur numerischen Auswertung eines dynamischen Systems, d.h. zur konkreten Berechnung von Werten, dient in GAIO das Integrator-Objekt (Zeile 2). Der 4 Konstruktor dieses Objekts erhält als Argument eine Zeichenkette (’Map’ im Falle einer Abbildung), die das gewünschte Integrationsverfahren angibt. Außerdem muss der Instanz dieses Objekts ein Modell zugewiesen werden (Zeile 3). Das GAIO-Objekt Tree verwaltet eine Boxzerlegung eines n-dimensionalen Quaders. Dem Konstruktor werden Mittelpunkt- und Radiusvektor des Quaders übergeben (Zeile 4). Im Falle der logistischen Abbildungen ist der zu partitionierende Quader das Intervall [0, 1], i.A. der gesamte Phasenraum des Systems, sofern es sich dabei um einen Quader handelt.3 Der Instanz t des Baum-Objekts muss die Instanz i des Integrator-Objekt zugewiesen werden (und damit implizit die Instanz m des Modell-Objekts) (Zeile 5). Dies ist notwendig zur Berechnung der Transitionsmatrix B mittels der Methode matrix des Baum-Objekts (Zeile 14). In Zeile 6 wird die Tiefe der Anfangspartition A festgelegt und in Zeile 7 die der verfeinerten Partition B, welche die Genauigkeit der Approximation an h∗ (T, A) bestimmt. Dabei bedeutet die Tiefe n eine Zerlegung in 2n Teilquader. Ist der Phasenraum ein Intervall, so sind dies 2n Teilintervalle derselben Länge. In den Zeilen 8-12 wird nun die besagte Zerlegung sukzessive bis zur Tiefe B_depth durchgeführt. Um zu überprüfen, ob das Bild eines Teilquaders andere Teilquader schneidet, berechnet GAIO die Bilder einer festen Anzahl von Testpunkten innerhalb dieses Teilquaders. Dazu wird der entsprechenden GAIO-Routine ein Punkte-Objekt übergeben, das wie in Zeile 13 initialisiert wird. Das erste Argument des Konstruktors gibt den Typ des Punktgenerators an, wobei der Typ ’MonteCarlo’ für eine zufällige Auswahl von Testpunkten steht. Das zweite Argument ist die Dimension des Phasenraums und das dritte die Anzahl der Testpunkte. In Zeile 14 wird schließlich durch den Aufruf der Methode matrix des BaumObjekts die zur Zerlegung B gehörige Transitionsmatrix B berechnet. Hier ist zu beachten, dass die Matrix B als sog. Sparse-Matrix zurückgegeben wird. Dabei handelt es sich um einen MATLAB-spezifischen Datentyp zum Abspeichern von Matrizen, deren Einträge zu großen Teilen 0 sind. Um Speicherplatz zu sparen, werden deshalb nur diejenigen Einträge der Matrix abgespeichert, die ungleich 0 sind. Genaueres dazu erfährt man in der MATLAB-Hilfe. 3 Sollte der Phasenraum des Systems kein Quader sein, so wird die Implementierung des Algorithmus ungleich komplizierter. Dieses Problem wurde allerdings im Rahmen des Praktikums nicht gelöst. 5 In Zeile 15 wird die berechnete Matrix B an die Routine comp_hgtm übergeben, die als Ergebnis die Transitionsmatrix R zurückliefert, deren maximaler Eigenwert (genauer: der Logarithmus davon) eine rigorose obere Schranke für h∗ (T, A) darstellt (siehe [1]). Hinter der Routine comp_hgtm verbirgt sich ein C-Programm mit ca. 600 Zeilen Quellcode, welches den in [1] beschriebenen Algorithmus zur Berechnung des Hypergraphen R unter Berücksichtigung der dort gegebenen Hinweise zur Implementierung desselbigen, realisiert. Ruft man stattdessen die Routine comp_hgtm_pc auf, so erhält man zusätzlich ein Protokoll über den schrittweisen Aufbau des Hypergraphen R und der Transitionsmatrix R, welches im Kommandofenster von MATLAB ausgegeben wird. Anhand dieses Protokolls lassen sich die einzelnen Schritte des Algorithmus - wie sie in [1] beschrieben werden - vollständig nachvollziehen. Ferner ist es hilfreich zum Auffinden von eventuell noch vorhandenen Fehlern. Falls die Routine beispielsweise mit einem Laufzeitfehler abbricht, lässt sich stets anhand des Protokolls nachvollziehen, an welcher Stelle im Aufbau des Hypergraphen das den Abbruch verursachende Problem auftritt. In Zeile 16 wird schließlich der maximale Eigenwert von R mittels des MATLAB-Befehls eigs berechnet und in der Variablen h abgelegt. Der Quelltext der Routinen comp_hgtm und comp_hgtm_pc befindet sich in gleichnamigen Dateien mit der Endung c im Verzeichnis gaio/matlab/. Änderungen am Quelltext sind problemlos vornehmbar, der geänderte Quelltext muss lediglich durch das MATLAB-Kommando mex gaio/matlab/comp_hgtm.c bzw. mex gaio/matlab/comp_hgtm_pc.c neu compiliert werden. 2.3 2.3.1 Die C-Routine comp hgtm Die Gateway-Routine Die Einbindung der C-Routine erfolgt mit Hilfe einer sog. MEX-Datei.4 Diese enthält den C-Quellcode, der die eigentlichen Berechnungen durchführt ( com” putational routine“), und eine sog. Gateway-Funktion ( gateway routine“), die ” als Schnittstelle zwischen C und MATLAB fungiert. Der Funktionskopf dieser Routine hat folgende Gestalt: void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] ) Dabei ist nrhs die Anzahl der Input-Argumente und nlhs die der OutputArgumente. prhs ist ein Zeiger auf die Input- und plhs ein Zeiger auf die 4 Informationen über das Erstellen von MEX-Dateien findet man in der MATLAB-Hilfe unter External Interfaces“. ” 6 Output-Argumente. Diese Funktion hat die Aufgabe die von MATLAB übergebenen Input-Argumente einzulesen, zu interpretieren und in der richtigen Art und Weise an die C-Routine zu übergeben, welche die eigentlichen Berechnungen durchführt. Anschließend muss das Ergebnis wieder in einer für MATLAB verständlichen Art und Weise zurückgegeben werden. Die Funktionen, die nötig sind, um diese Aufgaben zu erledigen, werden durch die Header-Datei mex.h eingebunden. Ihre Namen beginnen alle mit dem Präfix mx. Die Funktion mxGetScalar etwa dient dazu, ein Input-Argument, das als Skalar interpretiert werden soll, einzulesen, z.B.: a_depth = mxGetScalar(prhs[0]); Die Gateway-Routine in der Datei comp_hgtm.c liest zunächst die drei InputArgumente a_depth, b_depth und B ein und speichert sie in (gleichnamigen) globalen Variablen ab. Nachdem die Hauptroutine tree_build aufgerufen wurde, welche die Transitionsmatrix R berechnet, wird durch plhs[0] = mxCreateSparse(R.dim,R.dim,R.size,FALSE); eine Sparse-Matrix vom MATLAB-Typ als Output-Argument erzeugt und die berechnete Matrix R darin in der für MATLAB verständlichen Art abgespeichert. Dabei muss eine Konvertierung vorgenommen werden, da die Matrix R intern in einer Variable folgenden Typs abgespeichert wird, der sich vom MATLAB-Typ sparse unterscheidet: typedef struct { int dim; /*dimension of matrix*/ int size; /*length of the array rows, cols and entries*/ int *rows; /*rows*/ int *cols; /*columns*/ int *entries; /*entries*/ } SparseMatrix; Es werden nur die nichttrivialen Einträge der Matrix (d.h. diejenigen, die nicht 0 sind) gespeichert, und zwar in der Form Rrows[i],cols[i] = entries[i], wobei der Index i von 0 bis size-1 läuft. Der Vorteil des Typs SparseMatrix ist, dass er es ermöglicht, neue Matrixeinträge einfacher und schneller hinzuzufügen oder wieder zu entfernen, als dies beim MATLAB-Typ sparse der Fall wäre. Der Nachteil ist, dass die Suche nach einem bestimmten Matrixeintrag mehr Zeit in Anspruch nimmt. 2.3.2 Die node-Struktur Der Hypergraph R wird, wie in [1] vorgeschlagen, in Form eines Baumes mit variabler Anzahl von Kindern pro Knoten abgespeichert. Die Datenstruktur, die zum Abspeichern dieses Baumes (genauer: seiner Knoten) dient, hat folgende Form: 7 struct node { int i; int s; node *parent; node *first_child; node *brother; }; Der Integer i bezeichnet einen Knoten des Graphen G, der durch die Transitionsmatrix B gegeben ist, der Integer s gibt an, ob an diesem Knoten ein Pfad endet, d.h. ob dieser Knoten einen Hyperknoten markiert. Ist dieser Wert 0, so endet dort kein Pfad, ist er größer 0, so endet dort der mit s numerierte Pfad bzw. Hyperknoten. Die drei Zeiger parent, first_child und brother zeigen auf den Vater, das erste Kind (falls vorhanden) bzw. den nächstjüngeren Bruder (falls vorhanden) des Knotens. Die globale Variable hypernodenum zählt die im Baum abgespeicherten Hyperknoten bzw. Pfade (nicht die Knoten des Baumes!). Die Variable treeroot speichert den obersten Knoten (die Wurzel des Baumes) und die Variable R die Matrix R, die die Information über die Anzahl der Pfeile zwischen den einzelnen Hyperknoten des Hypergraphen R trägt. Die Funktion initnode dient zum Initalisieren eines Knotens. Die Bedeutung ihrer Argumente versteht sich von selbst: void initnode( node *thisnd, node *_parent, node *_brother, int _i, int _s ) Durch die Funktionen node_add_brother bzw. node_add_child wird einem Knoten thisnode entweder ein jüngerer Bruder oder ein Kind hinzugefügt: void node_add_brother( node *thisnd, int _i, int _s, node *(*nd) ) void node_add_child( node *thisnd, int _i, int _s, node *(*nd) ) 8 Der Zeiger nd wird auf den jüngeren Bruder bzw. das Kind gesetzt. Die rekursiv definierte Funktion node_search_arcless_hypernode durchsucht den Baum mit Wurzel thisnd nach einem Pfad (bzw. Hyperknoten), von dem noch keine Pfeile ausgehen. nd ist ein Zeiger auf den gefundenen Hyperknoten, der zurückgeliefert wird. Wird kein Hyperknoten ohne ausgehende Pfeile gefunden, so wird der Nullzeiger zurückgeliefert. void node_search_arcless_hypernode(node *thisnd, node *(*nd)) Die rekursiv definierte Funktion node_find_labelled_outgoing_arcs_upwards durchläuft von dem übergebenen Knoten thisnd aufwärts den Baum bis zur Wurzel und speichert dabei alle Knoten (d.h. deren i-Werte) in dem IntegerArray SET ab, welche von den durchlaufenen Knoten aus durch Pfeile erreicht werden, die das Label label tragen. In der Integer-Variablen setindex wird die Anzahl der erreichten Knoten abgespeichert, also die Länge des Arrays SET. void node_find_labelled_outgoing_arcs_upwards( node *thisnd, int *SET, int *setindex, int label ) Die rekursiv definierte Funktion node_is_path durchsucht den Baum mit der Wurzel thisnd nach einem gegebenen Pfad PATH der Länge pathsize. Wird dieser gefunden, so wird der Zeiger nd auf das Pfadende gesetzt. Die Funktion liefert TRUE oder FALSE zurück, je nachdem, ob der Pfad gefunden wurde oder nicht. int node_is_path( node *thisnd, int pathsize, int *PATH, node *(*nd) ) Die rekursiv definierte Funktion node_add_path fügt dem Baum mit der Wurzel thisnd einen neuen Pfad PATH der Länge pathsize hinzu. path_num ist die Nummer des Pfades (bzw. des entsprechenen Hyperknotens) und nd wird auf das Ende des Pfades gesetzt. Die Funktion liefert TRUE oder FALSE zurück, je nachdem, ob der Pfad erfolgreich hinzugefügt werden konnte oder nicht. int node_add_path( node *thisnd, int pathsize, 9 int *PATH, int path_num, node *(*nd) ) 2.3.3 Die Hauptroutine Die Funktion tree_build ist die Hauptroutine, welche die Transitionsmatrix R berechnet. Als Ergebnis liefert sie TRUE oder FALSE zurück, je nachdem, ob die Matrix R erfolgreich berechnet werden konnte oder nicht. int tree_build() Zunächst wird dem leeren Baum der erste Knoten hinzugefügt, der zugleich einen Hyperknoten markiert, welcher die einelementige Menge {1} enthält. Dies geschieht durch folgende Befehle: node_add_child(&treeroot,1,1,&helpnd); hypernodenum = 1; Die Hauptschleife, in der der Hypergraph vollständig erstellt wird, sieht - auf das Wesentliche reduziert - folgendermaßen aus: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 while (TRUE) { H = NULL; node_search_arcless_hypernode(&treeroot,&H); if (H == NULL) goto PRUNING; for (i=1; i<=max_label; i++) { sizeofsetH = 0; node_find_labelled_outgoing_arcs_upwards( H,setH,&sizeofsetH,i); if (sizeofsetH != 0) { if (!tree_add_hypernode(sizeofsetH, setH, H)) return FALSE; } } } Die Funktion node_search_arcless_hypernode in Zeile 3 sucht in dem Baum mit der Wurzel treerroot nach einem Hyperknoten, von dem noch keine Pfeile ausgehen. Wird ein solcher gefunden, so wird ein Zeiger (H) darauf zurückgegeben, andernfalls der Nullzeiger. Ist letzteres der Fall, so sorgt Zeile 4 dafür, dass die Schleife beendet wird. Die for-Schleife in Zeile 5 durchläuft die Labels, welche die Boxen der Partition A durchnummerieren. Die Funktion node_find_labelled_outgoing_arcs_upwards durchläuft von dem übergebenen Knoten aufwärts den Baum bis zur Wurzel und speichert dabei alle 10 Knoten (des Graphen G) in dem Integer-Array setH ab, welche von den durchlaufenen Knoten aus durch Pfeile erreicht werden, die das Label i tragen. In der Integer-Variablen sizeofsetH wird die Anzahl der erreichten Knoten abgespeichert, also die Länge des Arrays setH. Die Menge dieser Knoten bildet einen Kandidaten für einen neuen Hyperknoten, der nun durch die Funktion tree_add_hypernode in Zeile 10, falls nicht bereits vorhanden, dem Baum hinzugefügt wird. Nachdem die Schleife beendet wurde, müssen noch die überflüssigen Hyperknoten entfernt werden, also diejenigen, die nicht durch Pfeile erreicht werden. Da nur die Transitionsmatrix R interessiert, die die Informationen über die Pfeile im Hypergraphen R trägt, und nicht der Baum selbst, reicht es, die unnötigen Einträge der Matrix R zu entfernen. Nach Definition der Matrix R entspricht einem Hyperknoten, der nicht durch Pfeile erreichbar ist, eine Nullspalte in R. Also müssen lediglich alle Nullspalten und die zugehörigen Zeilen aus der Matrix R entfernt werden. Dies geschieht durch die Funktion remove_voids: do { R.dim = hypernodenum; HYPERNODES_REMOVED = remove_voids(); hypernodenum = R.dim; } while (HYPERNODES_REMOVED); 2.3.4 Die Funktion tree add hypernode Die Aufgabe der Funktion tree_add_hypernode mit dem Kopf int tree_add_hypernode( int setsize, int *SET, node *H ) ist es, einen neuen Hyperknoten, falls noch nicht vorhanden, dem Baum hinzuzufügen. Sie sieht im Wesentlichen folgendermaßen aus: 1 2 3 4 5 6 7 8 9 10 11 12 quicksort(SET,0,setsize-1); if (!node_is_path(&treeroot,setsize,SET,&nd2)) { nd = NULL; if (node_add_path(&treeroot,setsize,SET, hypernodenum+1,&nd)) { hypernodenum++; } else return FALSE; R.rows[R.size] = H->s; R.cols[R.size] = hypernodenum; R.entries[R.size] = 1; R.size++; if (R.size == MAX_MATRIXENTRIES) 11 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 { if (!reallocate_matrix_memory()) return FALSE; } } else { if (nd2 == NULL) return FALSE; isentry = is_entry(H->s,nd2->s); if (isentry >= 0) { R.entries[isentry]++; } else { R.rows[R.size] = H->s; R.cols[R.size] = nd2->s; R.entries[R.size] = 1; R.size++; if (R.size == MAX_MATRIXENTRIES) { if (!reallocate_matrix_memory()) return FALSE; } } } Zunächst wird in Zeile 1 die Liste der Knoten, die der neue Hyperknotenkandidat enthält, sortiert. Dann findet eine Fallunterscheidung statt, je nachdem, ob der entsprechende Pfad bereits im Baum enthalten ist oder nicht. Dies wird durch den Aufruf der Funktion node_is_path in Zeile 2 festgestellt. Ist der Pfad noch nicht enthalten, so wird er durch node_add_path in Zeile 4 hinzugefügt. Anschließend (Zeile 8-16) wird ein neuer Matrixeintrag erzeugt, der angibt, dass von dem übergebenen Knoten H zu dem neu hinzugefügten Hyperknoten ein Pfeil zeigt. Ist der Pfad bereits enthalten, so wird in den Zeilen 18-32 dem Hypergraphen ein zusätzlicher Pfeil zu dem entsprechenden Hyperknoten hinzugefügt durch Änderung eines bereits bestehenden Matrixeintrags bzw. durch Erstellen eines neuen. Dabei wird mit der Funktion reallocate_matrix_memory dynamisch Speicher für die Matrix R nachreserviert, falls dies nötig sein sollte. Bemerkung zum Speicher-Management: Speicher sollte in einer MEXDatei stets mit einer der Funktionen mxMalloc, mxCalloc oder mxRealloc angefordert werden statt mit malloc, calloc oder realloc. Die so reservierten Speicherbereiche werden nach dem Ausführen der Gateway-Routine automatisch wieder frei gegeben. Außerdem kann es passieren, dass MEX-Funktionen Speicherbereiche nicht respektieren, die mit den gewöhnlichen Speicherreservierungsbefehlen von C angefordert wurden. 12 3 Beispiele 3.1 Die logistische Famile Folgende mit MATLAB erzeugte Grafik stellt die von meinem Programm berechneten Entropie-Werte für die logistische Familie fa (x) = ax(1 − x) dar, aufgetragen gegen den Parameter a im Bereich [3.5, 4.0]. Die Variable A_depth wurde auf 1 und die Variable B_depth auf 12 gesetzt, die Anzahl der Testpunkte auf 500. Die Entropie h∗ (fa , A) wurde für 40 äquidistant verteilte Zwischenwerte im Intervall [3.5, 4.0] berechnet, und die Kurve stellt eine Interpolation dieser Werte dar. Ein Vergleich mit Abbildung 1 in [1] (S. 75) zeigt, dass das Programm vernünftige Ergebnisse liefert. Der zugehörige MATLAB-Code findet sich im Verzeichnis beispielegaio/new_ex/ in der Datei log_entropy.m. Die Modell-Datei logmap.c ist im Verzeichnis gaio/models/ abgespeichert. Die Rechenzeit für die Berechnung aller 40 Werte betrug zusammen ca. 10 Minuten. Die Rechenzeit der Routine comp_hgtm schwankte dabei zwischen 1 und 30 Sekunden. 1 0.9 0.8 0.7 0.6 0.5 0.4 0.3 0.2 0.1 0 3.5 3.2 3.55 3.6 3.65 3.7 3.75 3.8 3.85 3.9 3.95 4 Ein hyperbolischer linearer Torusautomorphismus Ferner habe ich das Programm für den hyperbolischen linearen Torusautomorphismus T (x, y) = (x + y, x) mod 1 aus Beispiel 3.2 in [1] getestet. Die hier berechneten Werte stimmen exakt mit den in [1] auf Seite 75 aufgelisteten überein.5 Die Tiefe A_depth wurde 1 gesetzt, die Anzahl der Testpunkte auf 100. Die Rechenzeit bei Tiefe B_depth = 12 betrug hier ca. 8.5 Minuten, wobei die Rechenzeit der GAIO-Routine tree.matrix im Vergleich zur HypergraphRoutine comp_hgtm vernachlässigbar war. 5 √ Der exakte Wert für h(T ) ist log 21 ( 5 + 1) ≈ log 1.6180. 13 LITERATUR Tiefe B_depth 4 6 8 10 12 Anzahl der Hyperknoten 32 144 448 1792 7472 Entropieschätzung h(B, A) log 1.8393 log 1.7494 log 1.6916 log 1.6583 log 1.6393 Die Modelldatei lineartorusmap.c ist im Verzeichnis gaio/models/ abgespeichert, der zugehörige MATLAB-Code in der Datei torusmap.m im Verzeichnis beispielegaio/new_ex/. Allgemeine Bemerkung zur Rechenzeit: Eine allgemeine Aussage über die Rechenzeit der Routine comp_hgtm zu machen, scheint sehr schwierig zu sein, wie man bereits an den starken Schwankungen sieht, die bei der logistischen Familie auftraten. Bereits eine kleine Änderung in der Struktur der Matrix B scheint sich hier massiv auswirken zu können. Es lässt sich auch keine allgemeine Aussage über das Verhältnis der von comp_hgtm beanspruchten Rechenzeit zu der von tree.matrix machen, da letztere im Gegensatz zur ersteren unmittelbar davon abhängt, wie kompliziert die betrachtete Abbildung ist (z.B. lässt sich eine lineare Funktion weitaus schneller auswerten als eine mit Sinustermen). Möglicherweise könnte man eine Verbesserung der Rechenzeiten erreichen, wenn man die Transitionsmatrix R auf eine intelligentere Art und Weise abspeichert, so dass die Suche nach einem bestimmten Matrixeintrag weniger Zeit in Anspruch nimmt. Literatur [1] Froyland, G., Junge, O., Ochs, G.: Rigorous computation of topological entropy with respect to a finite partition, Physika D 154, No.1-2, 68-84 (2001) [2] Katok, A., Hasselblatt, B.: Introduction to the Modern Theory of Dynamical Systems, Encyclopedia of Mathematics and its Applications 54. Cambridge: Cambridge Univ. Press (1995) [3] Dellnitz, M., Froyland, G., Junge, O.: The Algorithms Behind GAIO - Set Oriented Numerical Methods for Dynamical Systems, In: B. Fiedler (ed.): Ergodic Theory, analysis and efficient simulation of dynamical systems. Springer (2001) [4] Rasmussen, M.: Approximation von Attraktoren und Mannigfaltigkeiten nichtautonomer Systeme, Diplomarbeit, Universität Augsburg (2002)