numerisches praktikum - fim.uni

Werbung
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)
Herunterladen