Matrixmultiplikation mit "Einstiegsspunkt" (Householder-Transformation)



  • Hallo Forum,

    in einem meiner Studienfächer habe ich für diese Woche eine Programmieraufgabe aufbekommen, nämlich eine Speicher-effiziente QR-Zerlegung mittels Householder-Transformation. Mein Code für eine formale QR-Zerlegung funktioniert bereits perfekt, nun würde ich das ganze gerne umschreiben, um Speicherplatz zu sparen. Im Moment scheitert es nur noch an einer Sache, nämlich einer speziellen Matrixmultiplikation wie folgt:

    Schema:

    1 0 0 0 0 0           * 0 0 0 
    0 1 0 0 0 0           * * 0 0
    0 0 * * * *     *     * * * *
    0 0 * * * *           * * * *
    0 0 * * * *           * * * *
    0 0 * * * *           * * * *
             H      *      A
    

    Wobei hier H für für die Householder-Matrix im dritten Schritt (k=2) steht und A meine zu zerlegende Matrix. Nun möchte ich eine Multiplikation durchführen, welche nach gewohnter Matrixmultiplikationsregel nur folgende Stellen berechnet und direkt in A speichert (gekennzeichnet mit einem a):

    * 0 0 0 
         * * 0 0 
    =    * * a a
         * * a a         
         * * a a     
         * * a a   
            A
    

    Meine Idee dazu war, k als einen "Einstiegspunkt" für die Multiplikation zu wählen. Mein Codeschnipsel für diese Multiplikation sieht folgendermaßen aus, liefert aber falsche Ergebnisse:

    double** HH_matrix_mult(double** H, double** A, int x, int m, int n){
    	//x als Einstiegspunkt der Multiplikation
    		int i, j, k;
    		double z;
    		for(j=x;j<n;j++){
    			for(i=x;i<m;i++){
    				z=0;
    				for(k=0;k<m;k++) z=z+H[i][k]*A[k][j];
    				A[i][j]=z;
    			}
    		}
    	return A;
    }
    

    Liegt in dem Code irgendwo ein Fehler? Habe ich allgemein einen Denkfehler gemacht? Ich bin das ganze Schema schon dreimal durchgegangen und finde keinen Fehler - sieht jemand von euch problematische Stellen?

    Das ganze ist in folgendem Code eingebettet:

    (Q wird noch zur Kontrolle berechnet, fliegt zum Schluss raus. Der Hintergrund der obigen Multiplikation ist, dass ich die vorderen Spalten von A unbeeinflusst lassen will, um dort die Vektoren speichern zu können, die zur Erzeugung der Householder-Matrizen dienen. Die dürfen natürlich nicht von einer Multiplikation durcheinander gebracht werden.)

    #include <stdio.h>
    #include <math.h>
    #include "matrix.h"
    #include "QR_dec.h"
    
    double norm_Euklid(double** V, int k, int m){
    	double z=0; int i;
    	for (i=k; i<m; i++) z = z + V[i][k]*V[i][k];
    	return sqrt(z);
    }
    
    double skalarprodukt(double* v, int m){
    	double z=0; int i;
    	for(i=0; i<m; i++)  z=z+v[i]*v[i];
    	return z;
    }
    
    double dyade(double* v, int i, int j){
    	return v[i]*v[j];
    }
    
    int signum(double x){
    	if(x>=0)
    		return  1;
    	else
    		return -1;
    }
    
    double** HH_matrix_mult(double** H, double** A, int x, int m, int n){
    	//x als Einstiegspunkt der Multiplikation
    		int i, j, k;
    		double z;
    		for(j=x;j<n;j++){
    			for(i=x;i<m;i++){
    				z=0;
    				for(k=0;k<m;k++) z=z+H[i][k]*A[k][j];
    				A[i][j]=z;
    			}
    		}
    	return A;
    }
    
    double** HH_matrix(double* v, int m){
    	double** H=matrix_init(m,m);
    	int i, j;
    	for(j=0;j<m;j++){
    		for(i=0;i<m;i++){
    				H[i][j]=(-1)*(2/skalarprodukt(v,m))*(dyade(v,i,j));
    				if(i==j) H[i][j]=H[i][j]+1;
    		}
    	}
    	return H;
    }
    
    double** QR_dec(double** A, int m, int n){
    // QR-Zerlegung nach HH
    // A mxn, m größergleich n, sinnvoll bei rg(A)=n
    		int i, k;
    		double a;
    		double*  s=vector_init(m);
    		double** H;
    		double** Q=matrix_init(m,m);
    //Initialisieren von Q_0:
    		for(k=0;k<m;k++){
    			for(i=0;i<m;i++){
    				if(i==k)
    					Q[i][k]=1;
    				else
    					Q[i][k]=0;
    			}
    		}
    		for(k=0; k<n; k++){
    				printf("\nQR_Dec Iterationsschritt: %i\n\n",k);
    			for(i=0;i<k;i++)  A[i][k]=0;
    			for(i=0;i<m;i++)  s[i]=A[i][k];
    			a=norm_Euklid(A,k,m)*signum(A[k][k]);
    			s[k]=s[k]+a;
    			for(i=k+1;i<m;i++) s[i]=s[i]/s[k]; s[k]=1;
    			H=HH_matrix(s,m);
    			s[k]=a;
    				printf("Q_%i:\n",k);
    			Q=matrix_mult(Q,H,m,m,m);
    				printf("Q_%i*A_%i:\n",k,k);
    			A=matrix_mult(H,A,m,n,m);
    			//A=HH_matrix_mult(H,A,k,m,n);
    			for(i=0;i<m;i++) A[i][k]=s[i];
    		}
    	return A;
    }
    

    Vielen Dank für einen Hinweis oder etwas Multiplikations-Theorie!



  • Kann es sein, dass du nach zuweisen des Ergebnisses in einen A-Wert diesen nochmal bei folgenden Berechnungen verwendest (und dann natürlich nicht mehr den Originalwert liest)?



  • Kann es sein, dass du nach zuweisen des Ergebnisses in einen A-Wert diesen nochmal bei folgenden Berechnungen verwendest (und dann natürlich nicht mehr den Originalwert liest)?

    Whoa, Tatsache! Ich dachte, mit der Deklaration von z hätte ich bereits entgegengewirkt; aber stimmt, für die nachfolgende Werte wird ja dennoch ein verändertes Element in A miteingerechnet. Vielen Dank für den Hinweis, scheint wirklich der Fehler zu sein. Scheinbar komme ich um eine Teilallokierung nicht vorbei.



  • Sorry, Doppelpost



  • Monophagus schrieb:

    Meine Idee dazu war, k als einen "Einstiegspunkt" für die Multiplikation zu wählen. Mein Codeschnipsel für diese Multiplikation sieht folgendermaßen aus, liefert aber falsche Ergebnisse:

    double** HH_matrix_mult(double** H, double** A, int x, int m, int n){
    	//x als Einstiegspunkt der Multiplikation
    		int i, j, k;
    		double z;
    		for(j=x;j<n;j++){
    			for(i=x;i<m;i++){
    				z=0;
    				for(k=0;k<m;k++) z=z+H[i][k]*A[k][j];
    				A[i][j]=z;
    			}
    		}
    	return A;
    }
    

    Liegt in dem Code irgendwo ein Fehler? Habe ich allgemein einen Denkfehler gemacht? Ich bin das ganze Schema schon dreimal durchgegangen und finde keinen Fehler - sieht jemand von euch problematische Stellen?

    Warum werden "double**" verwendet? Das ergibt keinen Sinn, auch wenn es Dir in den Funktionen das Leben vereinfacht. Als Hinweis schau Dir mal die BLAS bzw cBLAS an. Einfache "double*" sind besser (das ist flexibler und kompatibel mit fix deklarierten Arrays) und sparen schon einiges an Speicherplatz ein. Dann kann man R natürlich im packed format speichern, da es sich um eine obere Dreiecksmatrix handelt, wenn das denn erlaubt ist.

    Dann stellt sich die Frage, warum die Funktion eine "double**" zurückliefert. A ist ein Parameter und wird überschrieben, dann sollte man das auch so dokumentieren.


Anmelden zum Antworten