OptiVec logo
MatrixLib  



Site Index:

OptiVec home
VectorLib
CMATH
Download
Bestellung / Registrierung
Update
Support

MatrixLib


Inhaltsverzeichnis

  1. Einführung
     1.1 Matrix-Datentypen
     1.2 Präfixe von MatrixLib-Funktionen
     1.3 C/C++ -spezifisch
          1.3.1 MatObj, das objekt-orientierte Interface für Matrix-Funktionen
          1.3.2 Alternative Aufruf-Form von Matrix-Funktionen mit direkten Zeigern
     1.4 Pascal/Delphi-spezifisch
  2. Dynamische Allokation von Matrizen
  3. Initialisierung von Matrizen
  4. Datentyp-Umwandlungen
  5. Symmetrie-Operationen (Transposition, Rotation, Spiegelung),  Interpolation, Erweiterung, Verkleinerung, Extraktion sowie Initialisierung von Teilen von Matrizen
  6. Arithmetische Operationen einer einzelnen Zeile, Spalte oder der Diagonalen
  7. Operationen, die auf alle Zeilen oder alle Spalten gleichzeitig oder auf die Diagonale wirken; Schwerpunkt einer Matrix
  8. Operationen von zwei Zeilen oder zwei Spalten
  9. Arithmetik ganzer Matrizen: Addition und Multiplikation
10. Lineare Algebra
11. Eigenwerte und Eigenvektoren, Quadratwurzel
12. Zwei-dimensionale Fourier-Transform-Methoden
13. Datenanpassung (Fitting)
     13.1 Polynome
     13.2 Allgemeine lineare Modellfunktionen
     13.3 Nicht-lineare Modelle
     13.4 Anpassung von Mehrfach-Datensätzen
     13.5 Hilfsfunktionen für nicht-lineare Anpassungen
     13.6 Übersicht der Anpassungsfunktionen
14. Matrix-Eingabe und -Ausgabe
15. Graphische Darstellung von Matrizen
16. Alphabetische Referenz für MatrixLib


1. Einführung

Der MatrixLib-Teil von OptiVec baut auf den im VectorLib-Teil eingeführten Prinzipien auf. Sie sollten die Einführungskapitel der VectorLib-Dokumentation gelesen haben, bevor Sie hier mit MatrixLib fortsetzen.

Zurück zum MatrixLib-Inhaltsverzeichnis     OptiVec Home

1.1 Matrix-Datentypen

In Analogie zu den Vektor-Datentypen von VectorLib definiert MatrixLib die folgenden Matrix-Datentypen:
fMatrixmatrix of floats
dMatrixmatrix of doubles
eMatrixmatrix of extended (long double)
cfMatrixmatrix of fComplex (complex<float>)
cdMatrixmatrix of dComplex (complex<double>)
ceMatrixmatrix of eComplex (complex<extended>)
iMatrixmatrix of int
uMatrixmatrix of unsigned int
usw.für alle weiteren Ganzzahl-Datentypen
 
Die Speicher-Ordnung der Matrix-Elemente ist dieselbe wie in den zwei-dimensionalen Feldern des jeweiligen Compilers. Dies bedeutet, daß die MatrixLib-Versionen für C und C++ Matrizen zeilenweise speichern, während die Pascal/Delphi-Versionen mit spaltenweiser Ordnung arbeiten.

Wir empfehlen, in Ihren Programmen ausschließlich mit diesen dynamisch allozierten Matrizen zu arbeiten. Es ist aber möglich, statische Matrizen, die z.B. als
float MX[4][6]; (für C/C++), oder
MX: array[0..3][0..5] of Single; (für Pascal/Delphi)
definiert wurden, in allen MatrixLib-Funktionen zu verwenden. Die einzige Ausnahme stellen die multiLinfit-Funktionen und multiNonlinfit-Routionen dar.

Zurück zum MatrixLib-Inhaltsverzeichnis     OptiVec Home

1.2 Präfixe von MatrixLib-Funktionen

Jede MatrixLib-Funktion besitzt ein Präfix, das den Datentyp anzeigt, den die betreffende Funktion verarbeitet:
Präfix: Argumente:
MF_fMatrix, float und fVector
MD_dMatrix, double und dVector
ME_eMatrix, extended (long double) und eVector
MCF_cfMatrix, fComplex und cfVector
MCD_cdMatrix, dComplex und cdVector 
MCE_ceMatrix, eComplex und ceVector
MI_iMatrix, int und iVector
MU_uMatrix, unsigned int und uVector
 usw. für alle übrigen Ganzzahl-Datentypen
 
In einigen Fällen wird das Präfix um einen Code aus drei Buchstaben erweitert, der spezielle Matrix-Eigenschaften anzeigt:
  • MFdia_ bedeutet, daß die Funktion eine Diagonalmatrix erwartet, also eine quadratische Matrix mit Elementen ungleich 0 nur auf der Diagonalen. Da es für eine solche Matrix keinen Sinn macht, alle Nullen der Nicht-Diagonalelemente explizit zu speichern, wird eine Diagonalmatrix als Vektor gespeichert, der lediglich die Diagonalelemente enthält.
     
  • MFsym_ zeigt eine Funktion an, die eine symmetrische Matrix als Argument erwartet. Zur Zeit machen nur MFsym_eigenvalues sowie MFsym_sqrt Gebrauch von dieser Annahme.
     
  • MFtrd_ bedeutet eine Funktion einer Tridiagonalmatrix, also einer Quadratmatrix, die nicht-verschwindende Elemente nur auf der Diagonalen plus-minus einer Spalte besitzt. Eine tridiagonale Matrix wird als Matrix von drei Zeilen eingegeben. Diese drei Zeilen stellen die drei Vektoren dar, die tatsächlich Elemente ungleich Null besitzen. Die Original-Matrix
     
    d0u00···  
    l1d1u10··· 
    0l2d2u20···
       ···  
       lN-2dN-2uN-2
       0lN-1dN-1
     
    wird also komprimiert in die Form
     
    u0 u1 u2 ··· uN-2  *
    d0d1d2···dN-2dN-1 
    * l1l2···lN-2lN-1
     
    Die mit einem Stern markierten Elemente l0 und uN-1, bleiben undefiniert und werden niemals benutzt.

Zurück zum MatrixLib-Inhaltsverzeichnis     OptiVec Home

1.3 C/C++ -spezifisch:

1.3.1 MatObj, das objekt-orientierte Interface für Matrix-Funktionen

Analog zu den Vektor-Objekten von VecObj ist das objekt-orientierte Interface für die Matrix-Funktionen in den Include-Dateien <fMatObj.h>, <dMatObj.h> etc. implementiert, wobei jeder in OptiVec vorhandene Datentyp seine eigene Include-Datei erhält. Es sei aber daran erinnert, daß das gesamte Interface (für alle Vektor- und Matrix-Typen zusammen) durch Einschluß von <OptiVec.h> zur Verfügung gestellt wird.
Um irgendeine Vektor- oder Matrix-Graphikfunktion benutzen zu können, muß immer <OptiVec.h> eingeschlossen werden.

Ähnlich den Alias-Namen der Vektor-Objekte erhalten die Matrix-Objekte die Alternativ-Namen fMatObj, dMatObj usw., wobei wie immer der Datentyp durch die ersten ein oder zwei Buchstaben des Klassen-Namens angegeben wird.

Die Matrix-Konstruktoren sind:
matrix(); // kein Speicher zugewiesen; Dimensionen auf 0 gesetzt
matrix( unsigned ht, unsigned len ); // [ht * len] Matrix erzeugt
matrix( unsigned ht, unsigned len, T fill ); // desgleichen, aber mit "fill" initialisiert
matrix( matrix <T> init ); // erzeugt eine Kopie der Matrix "init"

Für die Matrix-Klassen sind die arithmetischen Operatoren
+    -    *    +=    -=    *=
definiert.
Während der Vektor-Vektor-Operator * sich auf die Element-weise Multiplikation bezieht, bedeuten der Operator * für Matrix-Matrix, Matrix-Vektor und Vektor-Matrix die "echte" Matrizen-Multiplikation.

Sollten Sie einmal in die Lage kommen, ein MatObj-Matrixobjekt mit einer "klassischen" C-MatrixLib-Funktion verarbeiten zu wollen, benutzen Sie bitte die Member-Funktionen
getHt() und getLen(), um die Dimensionen auszulesen, getMatrix für den Matrix-Zeiger vom Datentyp fMatrix usw., oder getM0, um den Vektor M[0] vom Datentyp fVector usw. zu erhalten.

Die Syntax aller MatObj-Funktionen wird unten bei allen MatrixLib-Funktionen mit angegeben, so weit sie von tMatObj gekapselt werden.

Die strikte Kontrolle der Matrix-Dimensionen auf Konsistenz führt zu einigen Besonderheiten gegenüber den "normalen" MF_-Funktionen:

  • Mit den MF_-Funktionen ist is möglich, eine große Matrix als Zwischenspeicher für kleinere Matrizen zu verwenden. Beispielsweise könnten eine [n * n]- und eine [m * n]-Matrix nacheinander denselben Speicherplatz nutzen, ohne daß eine Neu-Belegung von Speicher nötig wäre (m < n: eine oder mehrere Zeilen bleiben einfach ungenutzt). Dies ist nicht möglich mit MatObj, da hier die Matrix-Dimensionen gekapselt sind und daher keine Unterscheidung zwischen "tatsächlichen" und "genutzten" Dimensionen getroffen werden kann.
  • Es ist nicht einmal möglich, beispielsweise eine nicht-quadratische Matrix mit ihrer Transponierten zu überschreiben (selbst wenn Sie bereit wären, auf die Integrität der Zeilenzeiger zu verzichten, und obwohl, die Größe der Matrix natürlich gleichbleibt).
  • Alle MatObj-Funktionen mit dem Präfix Dia_... verlangen quadratische Matrizen (ht = len), während MF_Dia_...-Funktionen im Prinzip auch mit nicht-quadratischen Matrizen verwendet werden können, solange der Parameter "len" im Funktionsaufruf der jeweils kleineren Dimension entspricht.
  • Die multifit-Funktionen sind nicht in den Klassen gekapselt, da die structs VF_EXPERIMENT und MF_EXPERIMENT ohnehin Zeiger anstatt Objekten enthalten müssen.

1.3.2 Alternative Aufruf-Form von Matrix-Funktionen mit direkten Zeigern

In der C/C++-Version von MatrixLib existieren alle Funktionen, die Matrizen als Argumente erwarten, in einer zweiten Form. In dieser sind alle Matrix-Argumente ersetzt durch Zeiger auf das Element [0,0] der jeweiligen Matrix. Man kann diese zweite Form explizit verwenden, indem der Unterstrich des Funktions-Präfixes fortgelassen wird. Ein Aufruf von
MF_mulM( MC, MA, MB, htA, lenA, lenB );
ist äquivalent zu
MFmulM(&(MC[0][0]),&(MA[0][0]),&(MB[0][0]),htA,lenA,lenB); oder
MFmulM( MC[0], MA[0], MB[0], htA, lenA, lenB );
Tatsächlich besteht die Laufzeitversion aller MatrixLib-Routinen aus dieser zweiten Form, in die die in <MFstd.h> usw. definierten Maktros alle Aufrufe der ersten Form umwandeln.

Zurück zum MatrixLib-Inhaltsverzeichnis     OptiVec Home

1.4 Pascal/Delphi- spezifisch:

In der Pascal/Delphi-Version von MatrixLib werden die Matrizen aller Datentypen als Zeiger zu dem Element M[0][0] implementiert. Dies bedeutet, daß statische Matrizen (wie z.B. MX: array[0..5][0..5] of Single; ) an MatrixLib-Funktionen mit dem Adressen-Operator übergeben werden können:
MF_equ1( @(MX[0][0]), 6 );

Delphi 4 oder höher:
Ab Version 4 bietet Delphi dynamische Felder. Im eindimensionalen Fall werden diese als "array of Single", "array of Double", usw. deklariert. Die VecLib-unit enthält Kurzdefinizitionen für diese Felder als fArray, dArray, usw. Durch einfaches type-casting von fArray in fVector können diese mit allen VectorLib-Funktionen verwendet werden.

Im zweidimensionalen Fall ist die Situation leider etwas komplizierter. Dynamische 2-D-Felder werden in Delphi als "array of fArray", "array of dArray" usw. deklariert. Auch hierfür bietet die VecLib-unit Kurzdefinitionen: f2DArray, d2DArray, usw. Aus der beschriebenen Implementation zweidimensionaler Felder in Delphi ergibt sich, daß jede Zeile einer Matrix als separater Vektor gespeichert wird. Dies hat zur Folge, daß die Matrix-Elemente nicht mehr innerhalb eines zusammenhängenden Speicherbereiches abgelegt werden, was sich wiederum sehr ungünstig auf die Performance von Matrix-Funktionen auswirkt und verhindert, daß Delphi-2DArrays direkt in MatrixLib-Funktionen eingesetzt werden können. Stattdessen müssen sie in "echte" Matrizen kopiert werden mittels MF_2DArrayToMatrix usw., bevor sie an MatrixLib-Funktionen übergeben werden können. Die Rück-Umwandlung wird durch MF_MatrixTo2DArray bewerkstelligt.

Die folgenden Kapitel bieten eine kurze Übersicht aller MatrixLib-Funktionen, geordnet nach ihrer Funktionalität. Am Schluß dieser Datei enthält Kap. 16 eine detaillierte Beschreibung der einzelnen Funktionen in alphabetischer Ordnung.

Zurück zum MatrixLib-Inhaltsverzeichnis     OptiVec Home


2. Dynamische Allokation von Matrizen

MF_matrixeiner Matrix Speicherplatz zuweisen
MF_matrix0Speicherplatz zuweisen und alle Elemente gleich 0 setzen
M_freeSpeicherplatz einer Matrix freigeben (unabhängig vom Datentyp)
M_nfreeSpeicherplatz mehrerer Matrizen freigeben (ebenfalls unabhängig vom Datentyp; nur C/C++)
V_freeAllSpeicherplatz aller existierenden Vektoren und Matrizen freigeben
 
Die dynamisch allozierten Matrizen von OptiVec sind an 64-ByteGrenzen ausgerichtet und dadurch optimal angepaßt an die Cache-Organisation moderner Prozessoren.

Nur C/C++-Version:
Die dynamisch allozierten Matrizen von OptiVec können genau wie zweidimensionale statische Felder von C/C++ adressiert werden. Angenommen, Sie deklarieren z.B. eine fMatrix X; und eine Variable float a;, dann können Sie eine Zuweisung schreiben der Art:
a = MX[3][5];

Sowohl C/C++- als auch Pascal/Delphi-Version:
Es gibt zwei Funktionen zur Adressierung einzelner Elemente. Diese Funktionen bieten die einzige Möglichkeit der Adressierung in Pascal/Delphi und werden außerdem benötigt, um die in der Zeigerarithmetik älterer Borland C++-Versionen enthaltenen Fehler zu umgehen:

MF_PelementZeiger auf ein spezifisches Matrix-Element
MF_elementWert eines spezifischen Matrix-Elementes

Um einem spezifischen Matrix-Element einen Wert zuzuweisen, gebrauche man die Syntax
*(MF_Pelement( MX, ht, len, 3, 4 )) = 3.0; /* für C/C++*/
MF_Pelement( MX, ht, len, 3, 4 )^ := 3.0; (* für Pascal/Delphi *)

Zurück zum MatrixLib-Inhaltsverzeichnis     OptiVec Home


3. Initialisierung von Matrizen

Um alle Matrix-Elemente mit ein- und demselben Wert zu initialisieren, oder um dieselbe arithmetische Operation auf alle Matrix-Elemente gleichzeitig anzuwenden, kann man die entsprechende VectorLib-Funktion aufrufen. Dies ist möglich, da alle Matrizen tatsächlich als Vektoren gespeichert sind und einen zusammenhängenden Speicherbereich belegen. Man muß lediglich die erste Zeile der Matrix (anstelle der Matrix selbst) an die gewünschte Vektor-Funktion übergeben. Um beispielsweise alle Matrix-Elemente mit einer Konstanten C zu initialisieren, rufe man:
VF_equC( MA[0], ht*len, C ); /* C/C++ */
VF_equC( MA, ht*len, C ); (* Pascal/Delphi *)

Die gebräuchlichsten Operationen dieser Art sind auch explizit als Matrix-Funktionen in MatrixLib definiert, wie die Initialisierung mit 0, MF_equ0, oder die Multiplikation mit einer Konstanten, MF_mulC (siehe Kap. 9). Die typischen Matrix-Initialisierungen sind:
 
MF_equ0alle Elemente gleich 0 setzen
MF_equ1Einheitsmatrix: alle Diagonalelemente werden gleich 1.0 gesetzt, alle übrigen gleich 0
MF_equm1negative Einheitsmatrix: alle Diagonalelement sind gleich -1.0, alle übrigen gleich 0
MF_randommit Zufallszahlen füllen
MF_outerprodBildung einer Matrix als äußeres Produkt zweier Vektoren
MF_Row_equ0alle Elemente einer Zeile gleich 0 setzen
MF_Col_equ0alle Elemente einer Spalte gleich 0 setzen
MF_Dia_equ0alle Diagonalelemente gleich 0 setzen
MF_Row_equCalle Elemente einer Zeile gleich der Konstanten C setzen
MF_Col_equCalle Elemente einer Spalte gleich der Konstanten C setzen
MF_Dia_equCalle Diagonalelemente gleich der Konstanten C setzen
MF_Row_equVeinen Vektor in eine bestimmte Zeile kopieren
MF_Col_equVeinen Vektor in eine bestimmte Spalte kopieren
MF_Dia_equVeinen Vektor in die Diagonale kopieren
MF_Trd_equMeine kompakt gespeicherte tridiagonale Matrix in eine allgemeine Matrix kopieren
MF_equMeine Matrix zur Kopie einer anderen machen
MF_negeine Matrix zum Negativen einer anderen machen
MCF_conjkomplex-konjugierte Matrix
MCF_hermconjHermitesch konjugierte Matrix: MB = MAT*
MF_UequLUnterdiagonal- (engl. "lower-diagonal")Elemente durch Reflexion an der Diagonalen in die Überdiagonal- (engl. upper-diagonal) Elemente kopieren, um eine symmetrische Matrix zu erhalten
MF_LequUÜberdiagonal- in Unterdiagonal-Elemente kopieren
 
Zwei-dimensionale Fenster für räumliche Spektralanalyse werden geliefert durch:
MF_HannHann-Fenster
MF_ParzenParzen-Fenster
MF_WelchWelch-Fenster

Zurück zum MatrixLib-Inhaltsverzeichnis     OptiVec Home


4. Datatyp-Umwandlungen

Matrizen jeden Datentyps können in jeden anderen Datentyp ungewandelt werden. Ein paar Beispiele hierfür sollten genügen. Der Rest ergibt sich wohl von selbst.
M_FtoDfMatrix zu dMatrix
M_EtoDeMatrix zu dMatrix (mit Überlauf-Schutz)
M_CDtoCFcdMatrix zu cfMatrix (mit Überlauf-Schutz)
M_DtoEdMatrix zu eMatrix

Zurück zum MatrixLib-Inhaltsverzeichnis     OptiVec Home


5. Symmetrie-Operationen (Transposition, Rotation, , Spiegelung),
Interpolation, Erweiterung, Verkleinerung, Extraktion sowie Initialisierung von Teilen von Matrizen

MF_transposeMatrix transponieren: MB = MAT
MCF_hermconjHermitesch konjugierte Matrix: MB = MAT*
MF_rotate90Matrix 90° im Uhrzeigersinn rotieren
MF_rotate180Matrix 180° rotieren
MF_rotate270Matrix 270° im Uhrzeigersinn (bzw. 90 ° entgegen dem Uhrzeigersinn) rotieren
MF_Rows_revUmkehrung der Element-Reihenfolge innerhalb der Zeilen. Dies entspricht einer Spiegelung der Matrix an der Y-Achse
MF_Cols_revUmkehrung der Element-Reihenfolge innerhalb der Spalten. Dies entspricht einer Spiegelung der Matrix an der X-Achse
MF_Rows_reflectobere Hälfte (d.h. höhere Indizes) aller Zeilen gleich der am Mittelpunkt reflektierten unteren Hälfte setzen
MF_Cols_reflectobere Hälfte (d.h. höhere Indizes) aller Spalten gleich der am Mittelpunkt reflektierten unteren Hälfte setzen
MF_UequLUnterdiagonal- (engl. "lower-diagonal")Elemente durch Reflexion an der Diagonalen in die Überdiagonal- (engl. upper-diagonal) Elemente kopieren, um eine symmetrische Matrix zu erhalten
MF_LequUÜberdiagonal- in Unterdiagonal-Elemente kopieren
 
MF_polyinterpolPolynom-Interpolation
MF_ratinterpolrationale Interpolation
MF_natCubSplineInterpol"natürliche" kubische Spline-Interpolation
MF_equMblockeinen Block, d.h. eine aus benachbarten Zeilen bzw. Spalten bestehende Teilmatrix, extrahieren
MF_equMblockTdie Transponierte eines Blocks extrahieren
MF_submatrixTeilmatrix extrahieren, wobei die Abtast-Intervalle entlang der Zeilen bzw. Spalten von 1 verschieden sein dürfen
MF_block_equMMatrix in einen Block einer anderen (normalerweise größeren) Matrix kopieren
MF_block_equMTtransponierte Matrix in einen Block einer anderen (normalerweise größeren) Matrix kopieren
MF_submatrix_equMMatrix in eine Teilmatrix einer (normalerweise größeren) Matrix kopieren
MF_Row_extractEinzelne Zeile extrahieren und in einen Vektor kopieren
MF_Col_extractEinzelne Spalte extrahieren und in einen Vektor kopieren
MF_Dia_extractDiagonale extrahieren und in einen Vektor kopieren
MF_Trd_extracttridiagonale Matrix aus allgemeiner Matrix extrahieren
MF_Row_insertMatrix durch Einfügen einer Zeile erweitern
MF_Col_insertMatrix durch Einfügen einer Spalte erweitern
MF_Row_deleteMatrix durch Entfernen einer Zeile verkleinern
MF_Col_deleteMatrix durch Entfernen einer Spalte verkleinern
 
Man beachte, daß - im Gegensatz zu den VectorLib-Funktionen VF_insert und VF_delete - Einfügung und Entfernung von Zeilen oder Spalten nicht direkt auf die Eingabe-Matrix wirken. Stattdessen wird die mittels MF_Row_insert etc. erweiterte oder verkleinerte Eingabe-Matrix A als Ausgabe-Matrix B gespeichert. Beim Erweitern vergewissere man sich, daß MB groß genung ist, um die erweiterte Ausgabe-Matrix aufzunehmen! Im Normalfall bedeutet dies, daß man eine neue Matrix B zu allozieren hat, bevor man MF_???_insert aufrufen und eventuell anschließend MA mittels
M_free( MA );
verwerfen kann.
Wenn dieser Prozeß mehrfach wiederholt werden soll, ist es günstig, die Ineffizienz der wiederholten Allokation zu vermeiden. In diesem Fall kann MA gleich zu Beginn mit den Dimensionen alloziert werden, die schließlich erreicht werden sollen (oder, falls diese nicht von vornherein bekannt sind, mit willkürlich festgelegten Obergrenzen). Im Verlauf des Matrix-Aufbaus durch Zeilen- oder Spalten-Einfügung muß man dann die tatsächlich erreichten Dimensionen verfolgen und (anstelle der für die Allokation verwandten Obergrenzen!) in allen MatrixLib-Funktionsaufrufen angeben. Jetzt kann die Eingangsmatrix in jedem Aufruf von MF_???_insert durch die erweiterte Ausgabematrix überschrieben werden, wie z.B.:
MF_Row_insert( MA, MA, ++actualhtA, actuallenA, 0, X );

nur C++ mit dynamisch allozierten Matrizen:
Fall MA durch Spalten-Einfügung oder -Löschung von MB überschrieben wird, geht die Möglichkeit verloren, einzelne Matrix-Elemente als MB[i][j] anzusprechen. Der Grund hierfür ist, daß die Zeilen-Zeiger der Ausgabe-Matrix immer noch dieselben wie in der Eingabe-Matrix bleiben. Dann bieten nur noch MF_element und MF_Pelement Zugang zu den einzelnen Elementen. Falls auf der anderen Seite ausschließlich Zeilen eingefügt oder gelöscht werden, bleiben die Zeilen-Zeiger gültig.

Zurück zum MatrixLib-Inhaltsverzeichnis     OptiVec Home


6. Arithmetische Operationen einer einzelnen Zeile, Spalte oder der Diagonalen

MF_Row_negMultiplikation aller Elemente einer bestimmten Zeile mit -1
MF_Col_negMultiplikation aller Elemente einer bestimmten Spalte mit -1
MF_Row_addCeine Konstante zu allen Elementen einer bestimmten Zeile hinzuaddieren
MF_Col_addCeine Konstante zu allen Elementen einer bestimmten Spalte hinzuaddieren
MF_Dia_addCeine Konstante zu allen Elementen der Diagonalen hinzuaddieren
MF_Row_addVVektor-Elemente zu den korrespondierenden Elementen einer bestimmten Zeile hinzuaddieren
MF_Col_addVVektor-Elemente zu den korrespondierenden Elementen einer bestimmten Spalte hinzuaddieren
MF_Dia_addVVektor-Elemente zu den korrespondierenden Elementen der Diagonalen hinzuaddieren
 
Für die übrigen Funktionen dieser Familie sollten einige Beispiele genügen:
MF_Row_subCeine Konstante von allen Elementen einer bestimmten Zeile subtrahieren
MF_Col_subrCumgekehrte Subtraktion: Differenz zwischen Spalten-Elementen und einer Konstanten
MF_Dia_mulVDiagonal-Elements mit korrespondierenden Vector-Elementen multiplizieren
MF_Row_divVElemente einer bestimmten Zeile durch korrespondierende Vektor-Elemente dividieren
MF_Col_divrCumgekehrte Division: eine Konstante durch die einzelnen Elemente einer Spalte dividieren

Zurück zum MatrixLib-Inhaltsverzeichnis     OptiVec Home


7. Operationen, die auf alle Zeilen oder alle Spalten gleichzeitig oder auf die Diagonale wirken
Berechnung des Schwerpunktes

MF_Rows_maxMaxima der einzelnen Zeilen in einem Spaltenvektor speichern
MF_Cols_maxMaxima der einzelnen Spalten in einem Zeilenvektor speichern
MF_Dia_maxMaximum der Diagonalen als Skalar zurückgeben
MF_Rows_absmaxBetrags-Maxima der einzelnen Zeilen in einem Spaltenvektor speichern
MF_Cols_absmaxBetrags-Maxima der einzelnen Spalten in einem Zeilenvektor speichern
MF_Dia_absmaxBetrags-Maximum der Diagonalen als Skalar zurückgeben
MF_Rows_sumSummen über die einzelnen Zeilen in einem Spaltenvektor speichern
MF_Cols_sumSummen über die einzelnen Spalen in einem Zeilenvektor speichern
MF_Dia_sumSumme der Diagonalelemente
MF_Rows_prodProdukte über die einzelnen Zeilen in einem Spaltenvektor speichern
MF_Cols_prodProducte über die einzelnen Spalen in einem Zeilenvektor speichern
MF_Dia_prodProdukt der Diagonal-Elemente
MF_Rows_runsumlaufende Summe entlang der Zeilen
MF_Cols_runsumlaufende Summe entlang der Spalten
MF_Rows_runprod  laufendes Produkt entlang der Zeilen
MF_Cols_runprodlaufendes Produkt entlang der Spalten
MF_Rows_rotatealle Zeilen um eine bestimmte Anzahl von Positionen rotieren
MF_Cols_rotatealle Spalten um eine bestimmte Anzahl von Positionen rotieren
MF_Rows_reflectobere Hälfte (d.h. höhere Indizes) aller Zeilen gleich der am Mittelpunkt reflektierten unteren Hälfte setzen
MF_Cols_reflectobere Hälfte (d.h. höhere Indizes) aller Spalten gleich der am Mittelpunkt reflektierten unteren Hälfte setzen

Man beachte, daß die Multiplikation aller Zeilen oder aller Spalten mit ein- und demselben Vektor äquivalent zur Multiplikation mit einer Diagonal-Matrix ist. Diese Operation wird durch MF_mulMdia und MF_TmulMdia bewerkstelligt.
Zu allen oben aufgeführten Maximum-Funktionen (...max,  ...absmax) existieren die entsprechenden Minimum-Funktionen und sind als ...min bzw. ...absmin benannt.
Für komplexe Zahlen lassen sich verschiedene Kriterien zur Definition eines Maximums definieren. Die folgende Tabelle faßt die existierenden Maximum-Funktionen für komplexe Matrizen zusammen. Natürlich existieren auch hier die entsprechenden Minimum-Funktionen.
 

MCF_Rows_absmaxBetrags-Maxima der einzelnen Zeilen in einem (reellen) Spaltenvektor speichern
MCF_Cols_absmaxBetrags-Maxima der einzelnen Spalten in einem (reellen) Zeilenvektor speichern
MCF_Dia_absmaxBetrags-Maximum der Diagonale als (reellen) Skalarwert zurückgeben
MCF_Rows_absmaxReImbetragsmäßig größte Real- und Imaginärteile einzeln entlang der Zeilen suchen und als Real- bzw. Imaginärteile eines komplexen Spaltenvektors speichern
MCF_Cols_absmaxReImbetragsmäßig größte Real- und Imaginärteile einzeln entlang der Spalten suchen und als Real- bzw. Imaginärteile eines komplexen Zeilenvektors speichern
MCF_Dia_absmaxReImbetragsmäßig größten Real- und Imaginärteil einzeln entlang der Diagonale suchen und als Real- bzw. Imaginärteil einer komplexen Zahl zurückgeben
MCF_Rows_cabsmaxbezüglich Absolutwert definierte Maxima der einzelnen Zeilen in einem Spaltenvektor speichern
MCF_Cols_cabsmaxbezüglich Absolutwert definierte Maxima der einzelnen Spalten in einem Zeilenvektor speichern
MCF_Dia_cabsmaxbezüglich Absolutwert definiertes Maximum entlang der Diagonale suchen und als Skalar zurückgeben
MCF_Rows_maxReImgrößte Real- und Imaginärteile einzeln entlang der Zeilen suchen und als Real- bzw. Imaginärteile eines komplexen Spaltenvektors speichern
MCF_Cols_maxReImgrößte Real- und Imaginärteile einzeln entlang der Spalten suchen und als Real- bzw. Imaginärteile eines komplexen Zeilenvektors speichern
MCF_Dia_maxReImgrößten Real- und Imaginärteil einzeln entlang der Diagonale suchen und als Real- bzw. Imaginärteil einer komplexen Zahl zurückgeben
MCF_Rows_sabsmaxbezüglich der Summe |Re|+|Im| definierte Maxima der einzelnen Zeilen in einem Spaltenvektor speichern
MCF_Cols_sabsmaxbezüglich der Summe |Re|+|Im| definierte Maxima der einzelnen Spalten in einem Zeilenvektor speichern
MCF_Dia_sabsmaxbezüglich der Summe |Re|+|Im| definiertes Maximum entlang der Diagonale suchen und als Skalar zurückgeben

Zur Berechnung des Schwerpunktes einer Matrix stehen zwei Funktionen zur Verfügung:
 

MF_centerOfGravityIndSchwerpunkt einer Matrix in Form interpolierter Element-Indizes
MF_centerOfGravityVSchwerpunkt einer MZ-Matrix bei explizit gegebener X- und Y-Achse

Zurück zum MatrixLib-Inhaltsverzeichnis     OptiVec Home


8. Operationen von zwei Zeilen oder zwei Spalten

MF_Rows_exchange zwei Zeilen vertauschen
MF_Cols_exchangezwei Spalten vertauschen
MF_Rows_addeine Zeile zu einer anderen hinzuaddieren (Ziel += Quelle)
MF_Cols_addeine Spalte zu einer anderen hinzuaddieren
MF_Rows_subeine Zeile von einer anderen abziehen (Ziel -= Quelle)
MF_Cols_subeine Spalte von einer anderen abziehen
MF_Rows_Caddeine mit einem konstanten Faktor skalierte Zeile zu einer anderen Zeile hinzuaddieren (Ziel += C * Quelle)
MF_Cols_Caddeine mit einem konstanten Faktor skalierte Spalte zu einer anderen Spalte hinzuaddieren
MF_Rows_lincombLinearkombination zweier Zeilen
MF_Cols_lincombLinearkombination zweier Spalten

Zurück zum MatrixLib-Inhaltsverzeichnis     OptiVec Home


9. Arithmetik ganzer Matrizen: Addition und Multiplikation

a) Element-weise Operationen
MF_addMzwei Matrizen addieren
MF_addMTeine Matrix und die Transponierte einer weiteren Matrix addieren
MC = MA + MBT
MF_subMeine Matrix von einer anderen subtrahieren
MF_subMTtransponierte Matrix subtrahieren
MC = MA - MBT
MF_subrMTMatrix von der Transponierten einer anderen Matrix subtrahieren
MC = MBT - MA
MF_mulCalle Matrix-Elemente mit einer Konstanten multiplizieren
MCF_mulReCalle Elemente einer komplexen Matrix mit einer reellen Konstanten multiplizieren
MF_divCalle Matrix-Elemente durch eine Konstante dividieren
MCF_divReCalle Elemente einer komplexen Matrix durch eine reelle Konstante dividieren
MFs_addMskalierte Addition zweier Matrizen
MC = c * (MA + MB)
MFs_addMTskalierte Addition von einer Matrix und einer transponierten anderen
MC = c * (MA + MBT)
MFs_subMskalierte Subtraktion zweier Matrizen
MC = c * (MA - MB)
MFs_subMTskalierte Subtraktion von einer Matrix minus der Transponierten einer anderen
MC = c * (MA - MBT)
MFs_subrMTskalierte umgekehrte Subtraktion: transponierte Matrix minus eine andere
MC = c * (MBT - MA)
MF_lincombLinearkombination
MC = ca * MA + cb * MB

b) Matrix-Multiplikation

MF_mulVMatrix mit einem Spaltenvektor multiplizieren:
Y = MA * X
MF_TmulVtransponierte Matrix mit einem Spaltenvektor multiplizieren:
Y = MAT * X
VF_mulMZeilenvektor mit einer Matrix multiplizieren:
Y = X * MA
VF_mulMTZeilenvektor mit transponierter Matrix multiplizieren:
Y = X * MAT
MF_mulMMultiplikation zweier Matrizen:
MC = MA * MB
MF_mulMTeine Matrix mit der Transponierten einer anderen multiplizieren:
MC = MA * MBT
MF_TmulMtransponierte Matrix mit einer anderen Matrix multiplizieren:
MC = MAT * MB
MF_TmulMTtransponierte Matrix mit einer anderen transponierten Matrix multiplizieren:
MC = MAT * MBT
MCF_mulMHeine Matrix mit der Hermitesch-konjugierten Form einer anderen multiplizieren:
MC = MA * MBT *
MCF_HmulMHermitesch-konjugierte Form einer Matrix mit einer anderen Matrix multiplizieren:
MC = MAT * * MB

c) Multiplikation von allgemeinen Matrizen mit Diagonalmatrizen und umgekehrt
Die Diagonalmatrix wird an die jeweilige Funktion als Vektor übergeben, der nur aus den Diagonalelementen besteht.

MF_mulMdiaallgemeine Matrix mit Diagonalmatrix multiplizieren:
MC = MA * MBDia
MF_TmulMdiatransponierte allgemeine Matrix mit Diagonalmatrix multiplizieren:
MC = MAT * MBDia
MFdia_mulMDiagonalmatrix mit allgemeiner Matrix mutliplizieren:
MC = MADia * MB
MFdia_mulMTDiagonalmatrix mit transponierter allgemeiner Matrix mutliplizieren:
MC = MADia * MBT

Zurück zum MatrixLib-Inhaltsverzeichnis     OptiVec Home


10. Lineare Algebra

Es gibt vier Gruppen von Funktionen zur Linearen Algebra. Die erste besteht aus einfachen Funktionen, die benutzt werden können, ohne auf deren internen Ablauf Rücksicht zu nehmen. Die zweite Gruppe besteht aus spezifisch der LU-Faktorisierung (engl. LU decomposition, LUD) und ihren Anwendungen gewidmeten Funktionen. Die dritte Gruppe behandelt Cholesky-Faktorisierung und ihre Anwendungen. Die vierte Gruppe schließlich verwendet Singulärwert-Zerlegung (engl. Singular Value Decomposition, SVD). Die auf LUD basierenden Funktionen existieren auch für komplexe Matrizen, die übrigen nur für reelle.

Hier sind zunächst die einfachen Funktionen:

MF_solve Lösen eines linearen Gleichungs-Systems (unter Verwendung von LU-Faktorisierung)
MF_invMatrix invertieren
MF_detDeterminante einer Matrix
MFsym_solve Lösen eines symmetrischen linearen Gleichungs-Systems, das als positiv-definit angenommen wird (unter Verwendung von Cholesky-Faktorisierung; falls das System sich dabei als nicht-positiv-definit herausstellt, wird LU-Faktorisierung angewandt)
MFsym_invSymmetrische, als positiv-definit angenommene Matrix invertieren
MFsym_detDeterminante einer symmetrischen, als positiv-definit angenommenen Matrix
MF_solveBySVDLineares Gleichungssystem unter Verwendung von Singulärwert-Zerlegung (SVD) lösen
MF_safeSolve"sicheres" Lösen eines linearen Gleichungssystemes: zuerst wird Lösung über LUD versucht; schlägt diese fehl, wird SVD angewandt
 
Es folgen einige Funktionen zur expliziten LU-Faktorisierung und zur Behandlung von LU-faktorisierten Matrizen:
MF_LUdecompose Faktorisierung in die LU-Form
MF_LUDresultprüfen, ob MF_LUdecompose erfolgreich war
MF_LUDsetEditEditier-Schwelle für MF_LUdecompose setzen; dies kann zur Umgehung von Singularitäten dienen
MF_LUDgetEditaktuell eingestellte Editier-Schwelle lesen
MF_LUsolveLösung eines linearen Gleichungssystems bei Vorliegen der Matrix in LU-Form
MF_LUimproveGenauigkeit der über LU-Faktorisierung erzielten Lösung eines linearen Gleichungssystems iterativ verbessern
MF_LUinvBereits in LU-Form faktorisierte Matrix invertieren
MF_LUdetDeterminante einer bereits in LU-Form vorliegenden Matrix
 
Für positiv-definite Matrizen stehen folgende Funktionen zur Cholesky-Faktorisierung und zur Behandlung von Cholesky-faktorisierten Matrizen zur Verfügung:
MF_CholeskyLdecompose Cholesky-Faktorisierung in linke Dreiecksmatrix
MF_CholeskyRdecompose Cholesky-Faktorisierung in rechte Dreiecksmatrix
MF_CholeskyLsolveLösung eines positiv-definiten Gleichungssystems bei Vorliegen der Matrix in linker Dreiecks-Form
MF_CholeskyRsolveLösung eines positiv-definiten Gleichungssystems bei Vorliegen der Matrix in rechter Dreiecks-Form
MF_CholeskyLimproveGenauigkeit der über Cholesky-Faktorisierung in L-Form erzielten Lösung eines linearen Gleichungssystems iterativ verbessern
MF_CholeskyRimproveGenauigkeit der über Cholesky-Faktorisierung in R-Form erzielten Lösung eines linearen Gleichungssystems iterativ verbessern
MF_CholeskyLinvBereits durch Cholesky-Faktorisierung in L-Form vorliegende Matrix invertieren
MF_CholeskyRinvBereits durch Cholesky-Faktorisierung in R-Form vorliegende Matrix invertieren
MF_CholeskyDetDeterminante einer entweder in L- oder in R-Form vorliegenden Matrix
 
Singulärwert-Zerlegung (SVD) und verwandte Funktionen:
MF_SVdecompose Singulärwert-Zerlegung
MF_SVsort Sortierung von Singulärwerten in absteigender Reihenfolge unter entsprechender Um-Ordnung der linken und rechten Singulär-Vektoren
MF_SVsolveSingulärwert-zerlegtes Gleichungssystem lösen
MF_SVDsetEditEditier-Schwelle für Dimensions-Reduktion bei SV-Lösung setzen
MF_SVDgetEditaktuell eingestellte Editier-Schwelle für Dimensions-Reduktion bei SV-Lösung lesen

Zurück zum MatrixLib-Inhaltsverzeichnis     OptiVec Home


11. Eigenwerte und Eigenvektoren, Quadratwurzel

Zur Zeit wird nur der spezielle, aber häufigste Fall symmetrischer reeller Matrizen behandelt:
MFsym_eigenvalues  Eigenwerte mit oder ohne Eigenvektoren einer symmetrischen reellen Matrix
MFsym_sqrt  Quadratwurzel einer symmetrischen Matrix mit ausschließlich positiven Eigenwerten

Zurück zum MatrixLib-Inhaltsverzeichnis     OptiVec Home


12. Zwei-dimensionale Fourier-Transform-Methoden

Analog zu den ein-dimensionalen Fourier-Transform-Methoden von VectorLib bietet MatrixLib die folgenden Funktionen für den zwei-dimensionalen Fall:
MF_FFTtoCSchnelle Fourier-Transformation (engl.: Fast Fourier Transform, FFT) einer rellen Matrix in Vorwärts-Richtung; das Ergebnis ist eine komplexe Matrix
MF_FFTVorwärts- und Rückwärts-FFT reeller Matrizen; unter Verwendung von Symmetrie-Eigenschaften wird das komplexe Ergebnis in eine reelle Matrix derselben Dimensionen wie die Eingabe-Matrix gepackt
MCF_FFTVorwärts- und Rückwärts-FFT komplexer Matrizen
MF_convolveFaltung (engl.: convolution) mit einer räumlichen Impulsantwort-Funktion
MF_deconvolveEntfaltung (engl.: deconvolution)
MF_filterRäumliche Filterung
MF_autocorrRäumliche Autokorrelation
MF_xcorrRäumliche Kreuzkorrelation
MF_spectrumRaumfrequenz-Spektrum

Die ein-dimensionale Fourier-Transformation entlang der Zeilen oder diejenige entlang der Spalten ist mit folgenden Funktionen möglich:
MF_Rows_FFTFourier-Transformation der Zeilen
MF_Cols_FFTFourier-Transformation der Spalten

Zurück zum MatrixLib-Inhaltsverzeichnis     OptiVec Home


13. Datenanpassung (Fitting)

MatrixLib bietet eine große Bandbreite von Datenanpassungs- ("Fit"-) Routinen für verschiedene Klassen von Modellfunktionen. Die folgende Tabelle bietet zunächst einen Überblick über die vorhandenen Routinen, bevor die einzelnen Klassen von Modellfunktionen und ihre Behandlung detaillierter besprochen werden:
 
VF_linregress lineare Regression von X-Y-Daten mit gleicher Wichtung aller Datenpunkte
VF_linregresswW dasselbe mit ungleicher Wichtung
VF_polyfit Koeffizienten eines Polynoms an vorhandene X-Y-Daten anpassen
VF_polyfitwW dasselbe mit ungleicher Wichtung der Datenpunkte
VF_linfit Koeffizienten einer beliebigen, in ihren Parametern linearen Funktion an vorhandene X-Y-Daten anpassen
VF_linfitwW dasselbe mit ungleicher Wichtung der Datenpunkte
VF_nonlinfit Koeffizienten einer beliebigen, möglicherweise nicht-linearen Funktion an vorhandene X-Y-Daten anpassen
VF_nonlinfitwW dasselbe mit ungleicher Wichtung der Datenpunkte
VF_multiLinfit mehrere X-Y-Datensätze gleichzeitig an eine gemeinsame lineare Funktion anpassen
VF_multiLinfitwW dasselbe mit ungleicher Wichtung der Datenpunkte
VF_multiNonlinfit mehrere X-Y-Datensätze gleichzeitig an eine gemeinsame nicht-lineare Funktion anpassen
VF_multiNonlinfitwW dasselbe mit ungleicher Wichtung der Datenpunkte
MF_linfit Koeffizienten einer in ihren Parametern linearen Funktion an X-Y-Z-Daten anpassen
MF_linfitwW dasselbe mit ungleicher Wichtung der Datenpunkte
MF_nonlinfit Koeffizienten einer nicht-linearen Funktion an X-Y-Z-Daten anpassen
MF_nonlinfitwW dasselbe mit ungleicher Wichtung der Datenpunkte
MF_multiLinfit mehrere X-Y-Z-Datensätze gleichzeitig an eine gemeinsame lineare Funktion anpassen
MF_multiLinfitwW dasselbe mit ungleicher Wichtung der Datenpunkte
MF_multiNonlinfit mehrere X-Y-Z-Datensätze gleichzeitig an eine gemeinsame nicht-lineare Funktion anpassen
MF_multiNonlinfitwW  dasselbe mit ungleicher Wichtung der Datenpunkte

Im folgenden werden die verschiedenen Klassen von Modellfunktionen einzeln besprochen. Das einfachste Modell besteht aus einer Geraden:
Yi = a * Xi + b;
Die Anpassung von Y = f(X)-Daten an eine Gerade heißt "lineare Regression" und wird durch VF_linregress durchgeführt.
Die nächst-höhere Stufe von Modellfunktionen stellen Polynome dar, die im folgenden Absatz besprochen werden:

13.1 Polynome

Polynome sind Funktionen der Form
Yi = a0 + a1Xi + a2Xi2 ... anXin
Ebenso wie lineare Regression ist auch die Anpassung an Polynome nur für Y-X-Daten (mittels VF_polyfit), nicht aber für MZ=f(X,Y)-Daten in OptiVec enthalten. Bei der Anpassung an Polynome möchte man die Koeffizienten ai bis zu einer bestimmten Grenze, dem Grad n des Polynoms bestimmen. In der einfachsten Form werden alle Koeffizienten als freie Parameter behandelt, also ohne die Möglichkeit, einzelne von vornherein bekannte Koeffizienten "einzufrieren." Polynom-Anpassung ist sinnvoll für Grade zwischen 2 und 4. Mit Polynomen über 5. Grades kann man praktisch alle Eingabe-Daten anpassen - das Resultat wird aber im günstigsten Falle ungenau und im schlimmsten Falle unbrauchbar sein, da bereits geringfügiges experimentelles Rauschen das Gleichgewicht der höheren Terme empfindlich stört. Falls Polynome höherer Grade tatsächlich benötigt werden, sollte man gründlich prüfen, welche Terme eliminiert werden können. Diese Aufgabe führt uns von den einfachen Polynomen zur nächsthöheren Klasse von Modellfunktionen, nämlich:

13.2 Allgemeine Lineare Modell-Funktionen

Im allgemeinen linearen Fall müssen Sie Ihre Modellfunktion selbst schreiben und als Argument an die Anpassungs-Routine übergeben. Das Wort "linear" bedeutet, daß die Modellfunktion aus einer linearen Kombination von Basisfunktionen besteht, deren jede linear von den anzupassenden Parametern abhängt:
y = a0f0(x) + a1f1(x) + a2f2(x)...
Die einzelnen Funktionen fi(x) können nicht-linear von x abhängen, aber nicht von den Koeffizienten ai. Beispielsweise ist
y = a0sin(x)+ a1cos(x)
eine korrekte lineare Modellfunktion, nicht aber
y = sin(a0x)+ cos(a1x)
da die Koeffizienten ai innerhalb der nicht-linearen Basisfunktionen auftreten.
 
 
Für allgemeine lineare Anpassungen muß die Modellfunktion in der Weise formuliert werden, daß sie die einzelnen Basisfunktionen für jeden x-Wert des Datensatzes ausrechnet. Das obige Beispiel mit sinus- und cosinus-Funktionen würde also in C/C++ geschrieben werden:

void LinModel( fVector BasFuncs, float x, unsigned nfuncs )
{
  BasFuncs[0] = sin(x);
  BasFuncs[1] = cos(x);
}

Man beachte, daß die Koeffizienten ai in der Modellfunktion gar nicht auftauchen. Das Argument nfuncs (das im obigen Beispiel ignoriert wird) ermöglicht es, eine variable Anzahl von Basisfunktionen zu verwenden. Man kann außerdem Anpassungsparameter "an" und "aus" schalten, d.h. zwischen "freien" und "eingefrorenen" Parametern wechseln. Hierfür übernimmt die Routine VF_linfit einen "Status"-Vektor als Argument. Für alle Elemente des Parameter-Vektors, die als frei behandelt werden sollen, muß der entsprechende "Status"-Eintrag auf 1 gesetzt werden. Setzt man Statusi gleich 0, so wird der entsprechende Parameter ai auf seinem Anfangswert eingefroren. Eingefrorene Werte müssen vor Aufruf von VF_linfit initialisiert werden.
Intern verwendet VF_linfit einen SVD-Algorithmus, um eine Lösung selbst für (nahezu) singuläre lineare Gleichungssysteme zu erhalten. Koeffizienten ai, deren Signifikanz unter einer bestimmten Schwelle Thresh liegt, werden dabei gleich 0 anstatt Unendlich gesetzt. Die Schwelle kann durch Aufruf von VF_setLinfitNeglect modifiert werden. Um den aktuell eingestellten Schwellenwert zu lesen, rufe man VF_getLinfitNeglect.

Nachdem bereits oben bemerkt wurde, daß Funktionen wie
y = sin(a0x)+ cos(a1x)
ihre eigene Behandlung erfordern, erreichen wir die letzte und allgemeinste Klasse von Modellfunktionen:

13.3 Nicht-lineare Modelle

Wie oben beschrieben wurde, verlaufen Anpassungen an lineare Modellfunktionen (einschließlich der einfachen Polynome) intern über die direkte Lösung eines Systems gekoppelter linearer Gleichungen. Dies wiederum besteht im wesentlichen aus einer einfachen Matrix-Inversion. Die Situation für nicht-lineare Modell-Funktionen stellt sich völlig anders dar: Es existiert keine geschlossene Lösung, und das Anpassungsproblem kann nur iterativ gelöst werden. Dadurch wird die Anpassung an nicht-lineare Modellfunktionen wesentlich rechenzeitaufwendiger (um 3-5 Größenordnungen!) als die Anpassung an lineare Modelle. Die OptiVec-Routinen zur Anpassung an nicht-lineare Modelle basieren auf zwei Algorithmen:
  • Der Levenberg-Marquardt-Methode und der
  • Downhill-Simplex-Methode nach Nelder und Mead.
Als Ausgangspunkt bietet sich normalerweise eine Kombination dieser beiden an (man wähle FitOptions.LevelOfMethod = 3, s.u.).
Die grundsätzliche Verschiedenheit zwischen linearer und nicht-linearer Datenanpassung spiegelt sich auch in der erforderlichen Formulierung der Modellfunktion wider. Im Unterschied zum linearen Fall werden hier nicht die einzelnen Basisfunktionen berechnet. Vielmehr wird die Modell-Funktion von VF_nonlinfit mit einem ganzen X-Vektor als Eingabe-Argument aufgerufen und hat den ganzen Y-Vektor zu berechnen. Für das obige nicht-lineare sinus- und cosinus-Beispiel könnte man die Modellfunktion in C/C++ schreiben:

void NonlinModel( fVector Y, fVector X, ui size, fVector A)
{
  for( ui i=0; i<size; i++ )
  Y[i] = sin( A[0]*X[i] ) + cos( A[1]*X[i] );
}

Aus der beschriebenen Arbeitsweise der Anpassungs-Routine wird klar, dass sie den größten Teil der Rechenzeit in Ihrer Modellfunktion (und deren Ableitungen, siehe unten) verbringen wird. Daher sollte diese weitestmöglich mit Hilfe der in OptiVec bereitgestellten Vektor-Funktionen optimiert werden. Hierzu könnten Sie vor dem Aufruf von VF_nonlinfit einen fVector YHelp bereitstellen (entweder global mittels VF_vector alloziert oder bei kleineren Vektoren auch lokal auf dem Stack). Dann können Sie schreiben:

void OptNonlinModel( fVector Y, fVector X, ui size, fVector A)
{
  VFx_sin( YHelp, X, size, A[0], 0.0, 1.0 );
  VFx_cos( Y,     X, size, A[1], 0.0, 1.0 );
  VF_addV( Y, Y, YHelp, size );
}

Der Parameter-Vektor "A", den Sie als Argument an VF_nonlinfit übergeben, wird im Verlauf des Anpassungs-Prozesses immer weiter verbessert. Beim Aufruf von VF_nonlinfit muss "A" mit bereits möglichst "guten" Werten der Parameter initialisiert sein. Je besser Sie diese "raten", umso schneller wird VF_nonlinfit konvergieren. Raten Sie gar zu schlecht, kann die Konvergenz wesentlich langsamer oder im schlimmsten Fall auch überhaupt nicht erreicht werden.

Alle nonlinfit-Funktionen geben den Anpassungstest-Wert (für Anpassung nach kleinsten Fehlerquadraten: c2, chi-Quadrat; für Anpassung nach kleinstem Absolut-Fehler: |c|, chiabs) des besten Parameter-Satzes "A" zurück.
Um die Anpassung durchführen zu können, benötigen diese Funktionen nicht nur die Modellfunktion selbst, sondern auch noch deren partielle Ableitungen nach den einzelnen Koeffizienten. Hierfür dient das Argument "Derivatives", das an alle nonlinfit-Funktionen übergeben werden muss. Falls Sie die partiellen Ableitungen in analytischer Form kennen, sollte Derivatives auf eine Funktion zeigen, die diese berechnet. Kennen Sie sie nicht, setzen Sie beim Funktionsaufruf Derivatives = NULL (nil in Pascal/Delphi). Im letzteren Fall werden alle Ableitungen numerisch innerhalb der Routinen bestimmt. In Fällen, wo Sie zwar einige, aber nicht alle der partiellen Ableitungen von Y nach den Koeffizienten ai kennen, können Sie dY / dai selbst berechnen, wo immer Sie die analytische Formel kennen, und VF_nonlinfit_autoDeriv für die übrigen Koeffizienten aufrufen. Um diese Möglichkeiten zu demonstrieren, sei hier erneut unser nicht-lineares sinus- und cosinus-Beispiel bemüht:

void DerivModel( fVector dYdAi, fVector X, ui size, unsigned iPar, fVector A, VF_NONLINFITWORKSPACE *ws )
{
  switch( iPar )
  {
    case 0:
      for( ui i=0; i<size; i++ )
        dYdAi[i] = X[i] * cos( A[0]*X[i] );
      break;
    case 1: /* wir kennen diese Ableitung zwar; aber nehmen wir einmal an, dies wäre nicht der Fall: */
      VF_nonlinfit_autoDeriv( dYdAi, X, size:UIntSize; ipar, A, ws );
  }
}

Bevor Sie allerdings VF_nonlinfit_autoDeriv tatsächlich einsetzen, sollten Sie alle Anstrengungen unternehmen, eine eventuell existierende analytische Lösung herauszufinden: Die für die numerische Ableitung notwendige Zahl von Funktionsaufrufen kann Ihre Anpassung leicht um einen Faktor von 3 bis 10 verlangsamen! Ohnehin sollten, ähnlich wie oben die Modellfunktion, auch deren Ableitungen möglichst mit Hilfe von Vektorfunktionen programmiert werden. Die optimierte Version von DerivModel lautet dann wie folgt:

void OptDerivModel( fVector dYdAi, fVector X, ui size, unsigned iPar, fVector A, VF_NONLINFITWORKSPACE *ws )
{
  switch( iPar )
  {
    case 0:
      VFx_cos( dYdAi, X, size, A[0], 0.0, 1.0 );
      VF_mulV( dYdAi, dYdAi, X, size ); break;
    case 1:
      VFx_sin( dYdAi, X, size, A[1], 0.0, -1.0 );
      VF_mulV( dYdAi, dYdAi, X, size );
  }
}

Vollständige Beispiele für Anpassung an Polynome, allgemeine lineare und allgemeine nicht-lineare Modelle sind in den Dateien FITDEMO.CPP, FITDEMOW.CPP, FITDEMOB.BPR, FITDEMO.PAS und FITDEMO.DPR enthalten.

Die nichtlinearen Anpassungs-Routinen von OptiVecsind sehr vielseitig einstellbar mit Hilfe von Optionen, die durch Aufruf der Funktion V_setNonlinfitOptions gewählt werden können. Um die aktuell eingestellten Optionen zu lesen, rufe man V_getNonlinfitOptions.
Alle Optionen sind in eine Struktur (struct in C/C++, record in Pascal/Delphi) namens VF_NONLINFITOPTIONS zusammengefaßt. Diese besteht aus den folgenden Feldern:
 

int FigureOfMerit;0: kleinste Fehlerquadrate als Qualitätskriterium der Anpassung
1: "robuste" Anpassung, die auf betragsmäßig minimale Abweichung hin optimiert
(Standard: 0)
float AbsTolChi;absolute Änderung von c2 (Standard: EPSILON)
float FracTolChi;relative Änderung von c2 (Standard: SQRT_EPSILON)
float AbsTolPar;absolute Änderung aller Parameter (Standard: SQRT_MIN)
float FracTolPar;relative Änderung aller Parameter (Standard: SQRT_EPSILON)

Die vier xxxTolChi und xxxTolPar-Parameter beschreiben die Konvergenz-Kriterien: Wenn die in aufeinanderfolgenden Iterationen erreichten Verbesserungen kleiner als nach Maßgabe dieser Parameter verlangt ausfällt, signalisiert dies erreichte Konvergenz. Nicht-anwendbare Kriterien sollten auf 0.0 gesetzt werden. Mindestens ein Konvergenz-Kriterium muß aber ungleich 0 sein!

unsigned HowOftenFulfill;Wie oft hintereinander soll eine der obigen Begingungen erfüllt sein, bevor Konvergenz als erreicht betrachtet wird? (Standard: 3)
unsigned LevelOfMethod;1: Levenberg-Marquardt-Methode,
2: Downhill Simplex-Methode nach Nelder und Mead,
3: beide Methoden abwechselnd;
man addiere hierzu 4, um auch einen "Ausbruch" aus lokalen Minima zu versuchen, in denen die Routine ansonsten "hängenbleiben" würde, ohne jemals das globale Minimum zu erreichen;
0: keine Anpassung, nur c2 (und Covar) berechnen
(Standard: 1)
unsigned LevMarIterations;max. Anzahl erfolgreicher LevMar -Iterationen während eines Durchgangs (Standard: 100)
unsigned LevMarStarts;Anzahl von LevMar-Neustarts pro Durchgang (Standard: 2)
float LambdaStart,
LambdaMin,
LambdaMax,
LambdaDiv,
LambdaMul;
Behandlung des LevMar-Parameters Lambda (nicht verändern, außer Sie kennen sich mit dem Algorithmus aus!)
unsigned DownhillIterations;max. Anzahl erfolgreicher Downhill-Simplex-Iterationen während eines Durchgangs (Standard: 200)
float DownhillReflection,
DownhillContraction,
DownhillExpansion;
Behandlung der Form-Anpassung des Simplex in der Downhill-Simplex-Methode (nicht verändern, außer Sie kennen sich mit dem Algorithmus aus!)
unsigned TotalStarts;max. Anzahl von LevMar/Downhill-Paaren (Standard: 16)
fVector UpperLimits,
        LowerLimits;
obere und/oder untere Grenzen für die Parameter festlegen. Standard für beides: NULL or nil
void (*Restrictions)(void);Zeiger zu einer benutzerdefinierten Funktion, die Beschränkungen der Parameter implementiert, die nicht als einfache Unter- und Obergrenzen formuliert werden können. Diese Funktion muss den ganzen Parameter-Vektor prüfen und die Parameter nach Bedarf editieren. Standard: NULL or nil.
Ein Beispiel für den Einsatz von Restrictions wäre ein Modell, in dem die Parameter Ai in (auf- oder absteigender) Ordnung sein müssen. Ihre Restrictions-Funktion würde dann VF_sort aufrufen, um diese Ordnung durch eventuellen Austausch von Ai-Einträgen zu garantieren. Von solchen doch sehr speziellen Fällen abgesehen, ist es aber generell besser, Restrictions gar nicht zu nutzen, sondern die nonlinfit-Routine ihre Arbeit ohne "Einmischung" tun zu lassen.

VF_NONLINFITWORKSPACE

Wie oben beschrieben, benötigen alle nicht-linearen Anpassungsfunktionen einen Satz von internen Variablen, der zusammengefasst als struct (Delphi: record) VF_NONLINFITWORKSPACE über seine Adresse an die jeweilige Funktion übergeben und wiederum von dieser verwendet wird, um alle benötigten Daten an Unter- und Hilfsfunktionen zu übergeben. Dieser Variablen-Satz muss nicht initialisiert werden; er enthält keine nutzbaren Informationen für den Anwender. Bei den Funktionen der MF_nonlinfit-Familie wird analog ein (von VF_NONLINFITWORKSPACE leicht verschiedener) Variablen-Satz als MF_NONLINFITWORKSPACE benötigt.
Man schreibe also in C/C++:

VF_NONLINFITWORKSPACE ws;
VF_NONLINFITOPTIONS fopt;
V_getNonlinfitOptions( &fopt );
// an dieser Stelle "fopt" nach Bedarf modifizieren...
VF_nonlinfit( ParValues, AStatus, nParameters, X, Y, sz, ModelFunc, DerivativeFuncs, &ws, &fopt );

oder in Delphi:
ws: VF_NONLINFITWORKSPACE;
fopt: V_NONLINFITOPTIONS;
V_getNonlinfitOptions( @fopt );
// an dieser Stelle "fopt" nach Bedarf modifizieren...
VF_nonlinfit( ParValues, AStatus, nParameters, X, Y, sz, @ModelFunc, @DerivativeFuncs, @ws, @fopt );

Wird beim Aufruf von VF_nonlinfit der entsprechende Paremeter gleich NULL / nil gesetzt, so erzeugt und verwendet die aufgerufene Funktion ihren eigenen Workspace.

13.4 Anpassung von Mehrfach-Datensätzen

Zusätzlich zu den Funktionen für einfache Datensätze gibt es Routinen für die simultane Anpassung mehrerer Datensätze an eine gemeinsame Modellfunktion. Angenommen, ein Physiker oder Chemiker mißt eine Geschwindigkeitskonstante k. Er hat dieselbe kinetische Messung zehn Mal unter geringfügig variierenden Bedingungen durchgeführt. Alle diese Messungen haben natürlich dasselbe k, aber andere Parameter (wie z.B. Amplitude und Zeit-Nullpunkt) werden sich von Experiment zu Experiment unterscheiden. Nun ist der übliche Weg, eine Anpassung für jeden dieser zehn Datensätze durchzuführen und anschließend über die resultierenden k's zu mitteln.
Dieses Vorgehen ist aber problematisch: Man weiß nicht genau, welche Wichtung den einzelnen Messungen zukommen soll und wie die überlappenden Fehlerbreiten der einzelnen Anpassungen zu behandeln sind. Man könnte sich wünschen, stattdessen alle Datensätze gleichzeitig anzupassen und hierbei dasselbe k für alle Datensätze zu verlangen, jedem einzelnen aber seine eigene Amplitude, Zeit-Nullpunkt etc. zuzugestehen. Dies ist exakt, was die multiLinfit- und multiNonlinfit-Funktionen von MatrixLib leisten. Man kann Mehrfach-Datensätze sowohl an lineare als auch an nicht-lineare Modellfunktionen anpassen, und zwar für Y = f(X)- ebenso wie für MZ = f(X,Y)-Daten.
Die multiLinfit- und multiNonlinfit-Funktionen benötigen die Eingabe-Daten in Form von Strukturen VF_EXPERIMENT für X-Y-Daten und MF_EXPERIMENT für X-Y-ZDaten. In C/C++ hat VF_EXPERIMENT die folgenden Felder:
 
fVector X, Y;X und Y-Vektoren: unabhängige Variable x und gemessene Daten y=f(x)
fVector InvVar;Kehrwert der Varianzen der einzelnen Meßwerte (wird nur für die "gewichteten" Varianten benötigt)
ui size;Zahl der x-y-Meßwerte
float WeightOfExperiment;Gewicht, daß dem gesamten Experiment zugewiesen werden soll (wiederum nur für die "gewichteten" Varianten benötigt)
 
MF_EXPERIMENT hat in C/C++ die folgenden Felder:
 
fVector X, Y;X und Y-Vektoren (unabhängige Variablen x und y)
fMatrix Z;gemessene z=f(x,y)-Daten
fMatrix InvVar; Kehrwert der Varianzen der einzelnen Meßwerte (wird nur für die "gewichteten" Varianten benötigt)
unsigned htZ, lenZ;Matrix-Dimensionen
float WeightOfExperiment;Gewicht, daß dem gesamten Experiment zugewiesen werden soll (wiederum nur für die "gewichteten" Varianten benötigt)
 
In Pascal/Delphi sind VF_EXPERIMENT und MF_EXPERIMENT wie folgt definiert:

type VF_EXPERIMENT = record
  X, Y, InvVar: fVector;
  size: UInt;
  WeightOfExperiment: Single;
end;
type PVF_EXPERIMENT = ^VF_EXPERIMENT;
 
type MF_EXPERIMENT = record
  X, Y: fVector;
  MZ, MInvVar: fMatrix;
  htZ, lenZ: UInt;
  WeightOfExperiment: Single;
end;
type PMF_EXPERIMENT = ^MF_EXPERIMENT;

Sowohl in VF_EXPERIMENT als auch in MF_EXPERIMENT sollten InvVar und WeightOfExperiment nur für die gewichteten Varianten der multifit-Funktionen gesetzt werden.

13.5 Hilfs-Funktionen für nichtlineare Anpassungen

Da nichtlineare Anpassungen - vor allem mit Mehrfach-Datensätzen - sich ziemlich langwierig gestalten und äußerst zeitaufwendig werden können (manchmal viele Stunden, selbst mit modernen Rechnern!), wurde eine Anzahl von Funktionen aufgenommen, die es erlauben, den Verlauf nichtlinearer Anpassungen zu verfolgen. Die Namen dieser Funktionen werden stets von der Anpassungs-Funktion abgeleitet, zu der sie gehören. Z.B. heißen die Hilfsfunktionen zu VF_nonlinfit:
 
VF_nonlinfit_getBestValuesbislang bester Parameter-Satz
VF_nonlinfit_getFOMbislang bester Anpassungstest-Wert (c2, chi-Quadrat)
VF_nonlinfit_getTestDiraktuelle Test-Richtung (+1 für aufwärts, -1 für abwärts) während "Ausbruchs"-Versuchen (LevelOfMethod größer als 3, siehe die obige Beschreibung von VF_NONLINFITOPTIONS)
VF_nonlinfit_getTestParIndex des derzeit bezüglich "Ausbruchs" geprüften Parameters
VF_nonlinfit_getTestRunIndes des aktuellen "Ausbruchs"-Durchlaufs (für jeden Fit-Parameter wird ein Testdurchlauf durchgeführt)
VF_nonlinfit_stopAnpassung nach Vervollständigung des laufenden Levenberg-Marquardt- oder Downhill-Simplex-Zyklus beenden
 
Für die übrigen nicht-linearen Anpassungs-Funktion ergeben sich die Namen der Hilfsfunktionen durch Ersetzen des Präfixes "VF_nonlinfit_" durch den jeweiligen Fit-Funktions-Namen wie z.B. VF_nonlinfitwW_getBestValues,   MF_multiNonlinfit_stop usw.

Es gibt zwei Möglichkeiten, diese Funktionen aufzurufen. Erstens können sie aus der ja vom Benutzer zu schreibenden Modellfunktion heraus angesprochen werden. Sie könnten hierfür beispielsweise einen Zähler in Ihrer Modellfunktion installieren, der alle hundert Aufrufe das erreichte c2, den Parametersatz usw. ausliest. Der zweite Weg steht nur Multithread-Programmen offen: Ein Thread führt die Anpassung aus, und ein weiterer ist für den Aufruf der obigen Hilfs-Funktionen zuständig, um den Fortgang der Anpassung zu überprüfen.
Beide Wege verlangen, dass der verwendete VF_NONLINFITWORKSPACE global zugänglich ist. Wenn eine Nonlinfit-Funktion aus mehreren Threads gleichzeitig aufgerufen wird, gibt es natürlich ein Problem, vor allem, wenn der Aufruf der Hilfsfunktionen aus der Modellfunktion heraus erfolgen soll: Woher soll die Modellfunktion "wissen", von welchem Thread heraus sie aufgerufen wurde? Die Lösung: Man speichere einen Thread-Index in den einzelnen Parameter-Vektoren A. Dies könnte in Ihrem Hauptprogramm beispielsweise so ähnlich aussehen wie:

    float A1[11], A2[11], ...;
    int AStatus1[11], AStatus2[11], ...;
    VF_NONLINFITWORKSPACE ws1, ws2, ...;
    ....
    VF_random( A1, 10, 1, -1.0, +1.0 ); // Startwerte
    VI_equC( AStatus1, 10, 1 ); // die ersten 10 Parameter sollen angepasst werden
    A1[10] = 1; // Thread-Index = 1
    AStatus1[10] = 0; // der Thread-Index bleibt natürlich eingefroren
    ... Thread #1 erzeugen mit VF_nonlinfit und A1, AStatus1, ws1 als Argumenten
 
    VF_random( A2, 10, 1, -1.0, +1.0 ); // Startwerte des 2. Satzes
    VI_equC( AStatus2, 10, 1 ); // wieder die ersten 10 Parameter anpassen
    A2[10] = 2; // Thread-Index = 2
    AStatus2[10] = 0; // Thread-Index wieder eingefroren
    ... Thread #2 erzeugen mit VF_nonlinfit und A2, AStatus2, ws2 als Argumenten

In Ihrer Modellfunktion hätten Sie dann etwas in der Art von:

void ModelFunc( fVector Y, fVector X, ui size, fVector A )
{
    static unsigned counter1=0, counter2=0, ...;
    float CurrentFOM;
    ....
    switch( (unsigned)(A[10]) )
    {
        case 1: if( (++counter1 % 1024) == 0 )
                {   CurrentFOM = VF_nonlinfit_getFOM( ws1 );
                    printf( "\nCount 1: %u, FOM: %f", counter1, CurrentFOM );
                } break;
        case 1: if( (++counter2 % 1024) == 0 )
                {   CurrentFOM = VF_nonlinfit_getFOM( ws2 );
                    printf( "\nCount 2: %u, FOM: %f", counter2, CurrentFOM );
                } break;
                ....
    }
    ....
}

Zurück zum MatrixLib-Inhaltsverzeichnis     OptiVec Home

14. Matrix-Eingabe und -Ausgabe

Die Ein- und Ausgabefunktionen für Matrizen sind analog den entsprechenden Vektor-Funktionen
MF_cprint Matrix auf dem Bildschirm ausgeben. Falls nötig, werden Zeilen am Bildschirmrand abgeschnitten. Falls mehr Zeilen auszugeben sind als auf einen Schirm passen, wird die Matrix auf mehrere Seiten aufgeteilt. (nur für Delphi)
MF_printMatrix auf dem Bildschirm ausgeben (ohne Abschneiden von Zeilen und ohne Aufteilung auf mehrere Seiten; nur für Delphi)
MF_fprintMatrix im ASCII-Format in einen Stream schreiben
MF_storeim Binärformat speichern
MF_recallim Binärformat einlesen
MF_writeim ASCII-Format in einen Stream schreiben
MF_readim ASCII-Format von einem Stream einlesen

Zurück zum MatrixLib-Inhaltsverzeichnis     OptiVec Home


15. Graphische Darstellung von Matrizen

Echte 3D-Darstellungs-Funktionen sind künftigen Versionen vorbehalten. Zur Zeit ist lediglich die Darstellung mittels Umsetzung von numerischen Werten in Farbtöne enthalten (engl.: color density plots). Hierbei wird ein Farbwert durch lineare Interpolation zwischen zwei als mincolor und maxcolor spezifizierten Farben gewonnen.
 
MF_xyzAutoDensityMap Farbton-Plot für z=f(x,y) mit automatischer Skalierung der x und y-Achsen und der Farbton-Skala zwischen mincolor und maxcolor
MF_xyzDataDensityMap Farbton-Plot für z=f(x,y) in ein bereits existierendes Koordinatensystem unter Verwendung der durch einen früheren Aufruf einer AutoDensityMap-Funktion berechneten Farbton-Skala
MF_zAutoDensityMapFarbton-Plot für z=f(i,j) mit automatischer Skalierung der x und y-Achsen und der Farbton-Skala zwischen mincolor und maxcolor. i und j sind die Matrix-Indizes in x und y-Richtung
MF_zDataDensityMapFarbton-Plot für z=f(i,j) in ein bereits existierendes Koordinatensystem unter Verwendung der durch einen früheren Aufruf einer AutoDensityMap-Funktion berechneten Farbton-Skala
M_setDensityBoundsFestsetzen einer Farbton-Skala
M_setDensityMapBoundsFestsetzen einer Farbton-Skala und Zeichnung eines X-Y-Koordinatensystems
M_findDensityMapBoundsBerechnung einer Farbton-Skala und Zeichnung eines X-Y-Koordinatensystems, wobei sichergestellt wird, daß die Rasterlinien des Koordinatensystems jeweils exakten (und nicht nur gerundeten) Zahlenwerten entsprechen

Zurück zum MatrixLib-Inhaltsverzeichnis     OptiVec Home

Copyright © 1996-2020 OptiCode – Dr. Martin Sander Software Development

Letzte Aktualisierung: 5. August 2020