Shop OBEX P1 Docs P2 Docs Learn Events
Matrix Multiplication — Parallax Forums

Matrix Multiplication

Hello,

I am trying to find an example of how to multiply two matrices (for example, two 3x3 matrices) in C. Does anyone know of any tutorials online that explain this? I know I have to use pointers but I'm having a hard time grasping how to do this task.

Thanks,
David

Comments

  • JasonDorieJasonDorie Posts: 1,930
    edited 2017-04-11 21:45
    This is the simplest (loop based, in C) form:
    void simple_mul(const float a[3][3], const float b[3][3], float out[3][3]) {
      int i,j,m,n;
      for(i=0;i<3;i++) {  // for each output row
        for(j=0;j<3;j++) {  // for each output column
          out[i][j] = 0.0f;  // zero the value being computed
          for(m=0;m<3;m++) {
            out[i][j] += a[i][m]*b[m][j];  // accumulate the row & column multiplies
          }
        }
      }
    }
    
    Expanded out, it looks more like this:
       void mult(const Matrix3D & a, const Matrix3D & b, Matrix3D & out)
        {
            out.M11 = a.M11 * b.M11 + a.M12 * b.M21 + a.M13 * b.M31;
            out.M12 = a.M11 * b.M12 + a.M12 * b.M22 + a.M13 * b.M32;
            out.M13 = a.M11 * b.M13 + a.M12 * b.M23 + a.M13 * b.M33;
            out.M21 = a.M21 * b.M11 + a.M22 * b.M21 + a.M23 * b.M31;
            out.M22 = a.M21 * b.M12 + a.M22 * b.M22 + a.M23 * b.M32;
            out.M23 = a.M21 * b.M13 + a.M22 * b.M23 + a.M23 * b.M33;
            out.M31 = a.M31 * b.M11 + a.M32 * b.M21 + a.M33 * b.M31;
            out.M32 = a.M31 * b.M12 + a.M32 * b.M22 + a.M33 * b.M32;
            out.M33 = a.M31 * b.M13 + a.M32 * b.M23 + a.M33 * b.M33;
      }
    
  • Heater.Heater. Posts: 21,230
    I would forget about using pointers. Just use two dimensional arrays. Work through the multiplication in the same way you did in high school. There are examples all over the net. For example this one:

    https://codeitaway.wordpress.com/2012/01/26/c-program-to-multiply-two-matrix-multiplication-of-3x3-matrix-in-c/

  • MicrocontrolledMicrocontrolled Posts: 2,461
    edited 2017-04-11 21:48
    I've not run it but I think this would work.
    for (int i=0;i<3;i++) {
      for (int j=0;j<3;j++) {
        for (int k=0;k<3;k++) {
          resultMatrix[i][j] += firstMatrix[i][k]*secondMatrix[j][k];
        }
      }
    }
    

    Each element in the product of matrices is the sum of the products of the row and corresponding column, so this produce the output of the sum of 2 3x3 matrices. Not very efficient with 3 nested loops, though.
  • All,

    Thank you for your replies. Apologies for getting back so late - been busy. Below is a piece of code I was working on when I posted my original question. It looks like I had something similar to your responses in terms of the algorithm. I think my main problem is passing matrices to a function - a matrix multiply function in this case. Do you have to use pointers to pass arrays to functions, and if so, how can I do this?
    /*
     Matrix Multiplication with Floats.c
     Output in matrix form.
     */
    
     #include "simpletools.h" // Include simple tools
    
     float p[3][3] = {
     {0.173, 1, 2},
     {3, 4, 5},
     {6, 7, 8.632}
     };
    
     float q[3][3] = {
     {0, 1, 2},
     {3, 4, 5},
     {6, 7, 8}
     };
    
     int main() // Main function
     {
     matrixMultiply(p, q);
     }
    
     matrixMultiply() // 3x3 matrixMultiply function
     {
     for(int i = 0; i < 3; i++)
     {
     for(int k = 0; k < 3; k++)
     {
     float sum = 0;
     for(int j = 0; j < 3; j++)
     {
     sum = sum + p[i][j] * q[j][k];
     if(j == 2)
     {
     print("%f ", sum);
     if (k==2)
     {
     print("\n");
     }
     }
     }
     }
    

    Respectfully,
    David
  • RaymanRayman Posts: 13,801
    This may be a prime example of why many scientist like Fortran and engineers Matlab... This kind of thing is built into both...

    But, they have a lot of baggage would wouldn't fit in a microcontroller...
  • Heater.Heater. Posts: 21,230
    Define your multiply something like:
    void matrixMultiply(float p[3][3], float q[3][3], float r[3][3])
    {
        for (int i=0;i<3;i++) {
            for (int j=0;j<3;j++) {
                for (int k=0;k<3;k++) {
                    r[i][j] += p[i][k] * q[j][k];
                }
            }
        }
    }
    [code]
    
    and call it as you have done:
    [code]
    float p[3][3] = {.....};    // First matrix 
    float q[3][3] = {.....};    // Second matrix
    float r[3][3] = {.....};     // Result matrix
    
    matrixMultiply(p, q, r);
    

    In C the name of an array is actually a pointer, so for

    int a[10];

    "a" is a pointer to int. int *.


  • Heater is correct. It's also possible to define the matrix as an array of [9] elements, instead of an array of [3][3] elements, and deal with the row/column addressing yourself, but that's a pain. I'm not suggesting you do that, but if you're looking at other people's code you might see that approach - it's pretty common.
  • All,

    Thank you so much for your help. I was able to get it working :) Microcontrolled mentioned that this approach is not efficient with 3 nested loops. Is there a "better" or more efficient way to do this? I may have some other questions as I work through some other matrix math functions. Thanks again! Here is the working code:
    /* Multiply two 3 by 3 matrices */
    #include "simpletools.h"
    
    float p[3][3] = {       // Define matrix p
      {0, 1, 2},
      {3, 4, 5},
      {6, 7, 8}
    };
    
    float q[3][3] = {       // Define matrix q
      {0, 1, 2},
      {3, 4, 5},
      {6, 7, 8}
    };
    
    float r[3][3] = {       // Zero matrix r
      {0, 0, 0},
      {0, 0, 0},
      {0, 0, 0}
    };
    
    float matrixMultiply(float p[3][3], float q[3][3], float r[3][3]);        // Declare matrixMultiply function
    
    int main(){
      
    matrixMultiply(p, q, r);        // Call matrixMultiply function
    
    // Print matrix r
    print("r =\n");       
    for(int i = 0; i < 3; i++){
      for(int k = 0; k < 3; k++){
        print("%f ", r[i][k]);
        }
        print("\n");
      }
    }       
     
    // matrixMultiply function - compute a 3 by 3 matrix
    float matrixMultiply(float p[3][3], float q[3][3], float r[3][3]){
      for(int i = 0; i < 3; i++){
        for(int k = 0; k < 3; k++){
          for(int j = 0; j < 3; j++){
            r[i][k] = r[i][k] + p[i][j] * q[j][k];
          }
        }
      }
    }
    

    Respectfully,
    David
  • Heater.Heater. Posts: 21,230
    edited 2017-05-02 15:28
    Well done.

    I'm sure you can imagine that the line:
        r[i][k] = r[i][k] + p[i][j] * q[j][k];
    

    is executed 3 * 3 * 3 = 27 times as all those loops go around.

    On each execution i, j, k have some value between 0 and 2.

    So one optimization would be to get rid of all the loops and write out that line 27 times with the correct numbers instead of i, j and k.

    That would get rid of all the instructions that do the loop jumping, incrementing and testing of i, j and k. It would just run through a straight line of 27 statements that are actually doing the work you want.

    This kind of optimization is known as "loop unrolling".
  • If you look at my first response, the function "simple_mul" is using nested loops, where the second version called "mult" is unrolled and written with no loops. The unrolled version is faster since the computer doesn't have to keep track of all the loop variables or dynamically figure out which matrix values you're working with for each iteration - that's all hard coded in the second version so you can just rip through the math.

    That said, when loops are simple enough, modern compilers will do a lot of loop unrolling for you, so you might not see much gain (in an optimized build, at least).
  • Heater and Jason,

    Thank you for your detailed responses. This forum is great for someone like me.

    Respectfully,
    David
  • This forum is great for someone like me.

    David, I'm sure I speak for many of us that it's a great place for us too !
    - Howard
  • Heater.Heater. Posts: 21,230
    This forum is a great place for everyone here. Novice to guru, everyone has questions about something sometime. It's give and take all around.
  • One of the reasons I like it here is because I learn a lot about hardware. I've been a software dev for a long time, so I can contribute that way, but for hardware I'm usually in over my head. :) It's nice to be able to contribute back to the community that I learned so much from.
Sign In or Register to comment.