Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
593 views
in Technique[技术] by (71.8m points)

matrix - call to gsl_blas_dgemm() isn't working as expected

I am working on a problem that requires solving 3 simultaneous equations of 3 variables. Reading:

https://www.mathsisfun.com/algebra/systems-linear-equations-matrices.html

I see that this can be solved by determining an inverse matrix of the matrix representation of the 3 equations. I found this C code that uses the GSL library at github for calculating inverse matrices:

https://gist.github.com/bjd2385/7f4685e703f7437e513608f41c65bbd7

(Many thanks to it's author, Mr. Doyle.)

I had read that if one multiplies a matrix by its inverse one should get an identity matrix (a matrix with 1.0s down the diagonal and 0.0s every where else). So I figured as a sanity check for the above github code, I could modify it to multiply the resultant inverse by the original matrix and display the result. If the result is an identity matrix, that validates the inverse calculation.

What I am finding is that, at least for the simple 2X2 matrix case, while the result of the inverse calculation looks correct, the subsequent matrix multiply is not resulting in an identity matrix.

I'm new to this GSL library, so perhaps I am just not calling the gsl_blas_dgemm() library function correctly to perform the matrix multiplication.

I've copied the modified code below:

/* A simple example of inverting a matrix using the gsl */
/* 1-26-2021, modified to sanity check result */

/* code doesn't seem to work */

#define HAVE_INLINE
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_blas_types.h>
#include <gsl/gsl_matrix_double.h>
#include <gsl/gsl_linalg.h>


gsl_matrix *invert_a_matrix(gsl_matrix *matrix);
void print_mat_contents(gsl_matrix *matrix);
void randomize_mat_contents(gsl_matrix *matrix);


static size_t size = 2;


        /************************************************************
         * PROCEDURE: invert_a_matrix
         * 
         * DESCRIPTION: Invert a matrix using GSL.
         *
         * RETURNS:
         *      gsl_matrix pointer
         */
gsl_matrix *
invert_a_matrix(gsl_matrix *matrix)
{
    gsl_permutation *p = gsl_permutation_alloc(size);
    int s;

    // Compute the LU decomposition of this matrix
    gsl_linalg_LU_decomp(matrix, p, &s);

    // Compute the  inverse of the LU decomposition
    gsl_matrix *inv = gsl_matrix_alloc(size, size);
    gsl_linalg_LU_invert(matrix, p, inv);

    gsl_permutation_free(p);

    return inv;
}


        /************************************************************
         * PROCEDURE: print_mat_contents
         * 
         * DESCRIPTION: Print the contents of a gsl-allocated matrix
         * 
         * RETURNS: 
         *      None.
         */
void
print_mat_contents(gsl_matrix *matrix)
{
    size_t i, j;
    double element;

    for (i = 0; i < size; ++i) {
        for (j = 0; j < size; ++j) {
            element = gsl_matrix_get(matrix, i, j);
            printf("%f ", element);
        }
        printf("
");
    }
}


        /************************************************************
         * PROCEDURE: randomize_mat_contents
         * 
         * DESCRIPTION: Overwrite entries in matrix with randomly
         *              generated values.
         *
         * RETURNS:
         *      None.
         */
void
randomize_mat_contents(gsl_matrix *matrix)
{
    size_t i, j;
    double random_value;
    double range = 1.0 * RAND_MAX;

    for (i = 0; i < size; ++i) {
        for (j = 0; j < size; ++j) {

            // generate a random value
            random_value = rand() / range;
    
            // set entry at i, j to random_value
            gsl_matrix_set(matrix, i, j, random_value);

        }
    }
}


int
main(void)
{
    srand(time(NULL));

    gsl_matrix *mat = gsl_matrix_alloc(size, size);

    // fill this matrix with random doubles
    randomize_mat_contents(mat);

    // let's see the original now
    printf("Original matrix:
");
    print_mat_contents(mat);

    printf("
");

    // compute the matrix inverse
    gsl_matrix *inverse = invert_a_matrix(mat);
    printf("Inverted matrix:
");
    print_mat_contents(inverse);

    printf("
");

    gsl_matrix *product = gsl_matrix_calloc(size, size);
    // if inverse is truly the inverse of mat, then mat * inverse should = identity matrix

    printf("product before:
");
    print_mat_contents(product);

    printf("
");

    int error;

    // neither of these results in an identity matrix, 8^(
    error = gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, inverse, mat, 0.0, product);
    // error = gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, mat, inverse, 0.0, product);
    // error = gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, inverse, mat, 0.0, product);
    if (error) {
        fprintf(stderr, "gsl_blas_dgemm returned %d
", error);
    }

    printf("inverse * mat:
");
    print_mat_contents(product);

    gsl_matrix_free(mat);
    gsl_matrix_free(inverse);
    gsl_matrix_free(product);

    return 0;
}

In summary:

Create a matrix with random contents, print it. Calculate its inverse, print the inverse. Call gsl_blas_dgemm() to multiply the matrix by its inverse, print what should be an identity matrix.

When compile, link and run the program on my ubuntu 20.04 laptop, I get this:

~/opengl/matrix_code/2by2_inverter$ gcc inverter.c -lgsl -lgslcblas -lm
~/opengl/matrix_code/2by2_inverter$ ./a.out 
Original matrix:
0.317588 0.113800 
0.280836 0.190114 

Inverted matrix:
6.689703 -4.004360 
-9.882010 11.175224 

product before:
0.000000 0.000000 
0.000000 0.000000 

inverse * mat:
-1.416400 0.402961 
6.743603 -0.124569 
~/opengl/matrix_code/2by2_inverter$ 

Now if I do the matrix multiply of the original matrix times its inverse "by hand" I find that the result is a 2X2 identity matrix:

Upper left corner: 0.317588*6.689703+0.113800*-9.882010 = .999996658 ~= 1.0
Upper right corner: 0.317588*-4.004360+0.113800*11.175224 = .000003808 ~= 0.0
Lower left corner: 0.280836*6.689703+0.190114*-9.882010 = .000000982 ~= 0.0
Lower right corner: 0.280836*-4.004360+0.190114*11.175224 = .999998091 ~= 1.0

Granted, the coordinates of the identity matrix are not exactly 1.0 and 0.0, but some error is expected. From this I conclude that the function invert_a_matrix() is doing the right thing, at least for a 2X2 matrix.

But try as I might I cannot figure out how to get the call to gsl_blas_dgemm() to produce the identity matrix.

Note that I installed the GSL libraries from the Ubuntu repository via:

~$ sudo apt-get install libgsl-dev

Any clues as to what I am doing wrong?

Thanks in advance

question from:https://stackoverflow.com/questions/65927354/call-to-gsl-blas-dgemm-isnt-working-as-expected

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I figured out the problem. The call to invert_a_matrix() modifies the passed in matrix. So by the time I got to the call to gsl_blas_dgemm(), I wasn't multiplying the inverse by the original matrix.

Fix was to allocate a copy of the original matrix before the call the invert_a_matrix() and pass the copy to gsl_blas_dgemm().


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...