19

I'm doing a function that multiplies 2 matrices.

The matrices will always have the same number of rows and columns. (2x2, 5x5, 23x23, ...)

When I print it, it doesn't work. Why?

For example, if I create two 2x2 matrices:

matrixA:

[1][2]

[3][4]

matrixB:

[5][6]

[7][8]

The result should be:

[19][22]

[43][50]

(http://ncalculators.com/matrix/2x2-matrix-multiplication-calculator.htm)

But, I get:

[19][undefined]

[22][indefined]

function multiplyMatrix(matrixA, matrixB) {
  var result = new Array(); //declare an array   

  //var numColsRows=$("#matrixRC").val();
  numColsRows = 2;

  //iterating through first matrix rows
  for (var i = 0; i < numColsRows; i++) {
    //iterating through second matrix columns
    for (var j = 0; j < numColsRows; j++) {
      var matrixRow = new Array(); //declare an array
      var rrr = new Array();
      var resu = new Array();
      //calculating sum of pairwise products
      for (var k = 0; k < numColsRows; k++) {
        rrr.push(parseInt(matrixA[i][k]) * parseInt(matrixB[k][j]));
      } //for 3
      resu.push(parseInt(rrr[i]) + parseInt(rrr[i + 1]));

      result.push(resu);
      //result.push(matrixRow);
    } //for 2
  } //for 1
  return result;
} // function multiplyMatrix
ℛɑƒæĿᴿᴹᴿ
  • 4,983
  • 4
  • 38
  • 58
Jordi 45454
  • 275
  • 2
  • 3
  • 11
  • have you tried to debug the code in a browser built-in debugger or perhaps in Firebug? – Aprillion Nov 29 '14 at 17:56
  • with your code, I get a different output than you claim you get - `multiplyMatrix([[1,2],[3,4]], [[5,6],[7,8]])` returns `[[19],[22],[NaN],[Nan]]` – Aprillion Nov 29 '14 at 18:04

14 Answers14

23

You're getting confused with your various temporary arrays. The undefined values are caused by out-of-bounds access on the line below your innermost loop.

I recommend that you stick to making a single array for the result of the multiplication. As you're probably aware, the hitch is that JavaScript doesn't allow you to initialize a multi-dimensional array. To make a two-dimensional array, you have to initialize a one-dimensional array, then iterate over its elements and initialize each one to a one-dimensional array.

function multiply(a, b) {
  var aNumRows = a.length, aNumCols = a[0].length,
      bNumRows = b.length, bNumCols = b[0].length,
      m = new Array(aNumRows);  // initialize array of rows
  for (var r = 0; r < aNumRows; ++r) {
    m[r] = new Array(bNumCols); // initialize the current row
    for (var c = 0; c < bNumCols; ++c) {
      m[r][c] = 0;             // initialize the current cell
      for (var i = 0; i < aNumCols; ++i) {
        m[r][c] += a[r][i] * b[i][c];
      }
    }
  }
  return m;
}

function display(m) {
  for (var r = 0; r < m.length; ++r) {
    document.write('&nbsp;&nbsp;'+m[r].join(' ')+'<br />');
  }
}

var a = [[8, 3], [2, 4], [3, 6]],
    b = [[1, 2, 3], [4, 6, 8]];
document.write('matrix a:<br />');
display(a);
document.write('matrix b:<br />');
display(b);
document.write('a * b =<br />');
display(multiply(a, b));
Michael Laszlo
  • 12,009
  • 2
  • 29
  • 47
20

You can use multiplyMatrices() function from: http://tech.pro/tutorial/1527/matrix-multiplication-in-functional-javascript it works like charm. Example (You can print a matrix with style in Chrome and Firefox console with console.table() ):

function multiplyMatrices(m1, m2) {
    var result = [];
    for (var i = 0; i < m1.length; i++) {
        result[i] = [];
        for (var j = 0; j < m2[0].length; j++) {
            var sum = 0;
            for (var k = 0; k < m1[0].length; k++) {
                sum += m1[i][k] * m2[k][j];
            }
            result[i][j] = sum;
        }
    }
    return result;
}

var m1 = [[1,2],[3,4]]
var m2 = [[5,6],[7,8]]

var mResult = multiplyMatrices(m1, m2)

/*In Google Chrome and Firefox you can do:*/

console.table(mResult) /* it shows the matrix in a table */

Result matrix in console.table()

Johann Echavarria
  • 9,695
  • 4
  • 26
  • 32
15

I know it's an old question but I recommend to switch to my answer.

My solution's got good performance because it uses Map Reduce functions

//The chosen one
function matrixDot (A, B) {
    var result = new Array(A.length).fill(0).map(row => new Array(B[0].length).fill(0));

    return result.map((row, i) => {
        return row.map((val, j) => {
            return A[i].reduce((sum, elm, k) => sum + (elm*B[k][j]) ,0)
        })
    })
}

var print = m => m.forEach(r => document.write(`&nbsp;&nbsp;${r.join(' ')}<br/>`)) 

var a = [[8, 3], [2, 4], [3, 6]]
var b = [[1, 2, 3], [4, 6, 8]]

document.write('matrix a:<br />');
print(a);
document.write('matrix b:<br />');
print(b);
document.write('a * b =<br />');
print(matrixDot(a,b));
Fernando Carvajal
  • 1,869
  • 20
  • 19
  • -1. This function is **over 70% slower on Safari than the accepted answer**. On Chrome/V8, the functions run in similar time. Run the benchmark yourself: https://jsperf.app/hepeyo – Nate Levin Apr 24 '23 at 01:39
7

For those interested in pure functional solution:

let MatrixProd = (A, B) =>
  A.map((row, i) =>
    B[0].map((_, j) =>
      row.reduce((acc, _, n) =>
        acc + A[i][n] * B[n][j], 0
      )
    )
  )

Testing code for your browser:

let A = [[8, 3], [2, 4], [3, 6]];
let B = [[1, 2, 3], [4, 6, 8]];
console.table(MatrixProd(A,B));
Jan Turoň
  • 31,451
  • 23
  • 125
  • 169
2

This version stores rows as temporaries thus reducing the effective amount of index lookups. By this benchmark the achieved performance is almost 2 times faster if compared to the version withou storing rows.

function multiply(a, b) {
    let aRows = a.length;
    let aCols = a[0].length;
    let bCols = b[0].length;
    let result = new Array(aRows); 
    for (let r = 0; r < aRows; ++r) {
        const row = new Array(bCols);
        result[r] = row;
        const ar = a[r];
        for (let c = 0; c < bCols; ++c) {
            let sum = 0.;     
            for (let i = 0; i < aCols; ++i) {
                sum += ar[i] * b[i][c];
            }
            row[c] = sum;  
        }
    }
    return result;
}

const m = multiply(
        [[8, 3], [2, 4], [3, 6]],
        [[1, 2, 3], [4, 6, 8]]
    );
console.log(m);
function display(m) {
    for (var r = 0; r < m.length; ++r) {
        document.write('&nbsp;&nbsp;'+m[r].join(' ')+'<br />');
    }
}

var a = [[8, 3], [2, 4], [3, 6]],
    b = [[1, 2, 3], [4, 6, 8]];
document.write('matrix a:<br />');
display(a);
document.write('matrix b:<br />');
display(b);
document.write('a * b =<br />');
display(multiply(a, b));
Duloren
  • 2,395
  • 1
  • 25
  • 36
2

this can also work with mathjs.

A = [[1, 2],
     [3, 4]]

B = [[5, 6],
     [7, 8]]

math.multiply(A, B)
David Contreras
  • 169
  • 1
  • 3
1

If you wanted to go the bonkers route, you could also possibly do something with the vertices transformation in WebGL facilities now available in some modern browsers.

Not really sure if this would work in the same way as one might approach vector transformation in OpenCL (**in fact they're type-equivalent / interoperable), but the general idea is:

  • adding your values to a buffer

  • "pretending" it's an array of vertices

  • transforming en-mass using the GPU engine

  • retrieving the revised values from the vector

(see demo here) http://www.html5rocks.com/en/tutorials/webgl/webgl_transforms/

Just an alternative to the usual loop-in-loop approach. And to be honest, a bit of a fiddle, given that OpenCL was designed for this kind of thing

Within the OpenCL 1.2 spec vertex buffers from OpenGL can be loaded and transformed using OpenCL (see. https://software.intel.com/en-us/articles/opencl-and-opengl-interoperability-tutorial)

  • this is awesome!! Not sure why this is down voted. Eventually i think all linear algebra operations should be done this way in the browser. – episodeyang Jul 24 '16 at 19:50
1

Here's my ES6 soulution with math error handling:

const matrixDot = (A, B) => {
  // Error handling
  const mx = [A, B];
  const cols = mx.map((matrix) => matrix[0].length);
  if (!mx.every((matrix, i) => matrix.every((row) => row.length === cols[i]))) {
    throw new Error(
      'All rows in a matrix must have the same number of columns'
    );
  } else if (cols[0] !== B.length) {
    throw new Error(
      'The number of columns in the 1st matrix must be equal to the number of rows in the 2nd matrix'
    );
  }

  // Calculations
  return A.map((rowA) =>
    B[0].map((_, xb) =>
      rowA.reduce((acc, itemA, yb) => acc + itemA * B[yb][xb], 0)
    )
  );
};

// Example
const A = [
  [3, 2, 5],
  [6, 4, 1],
];
const B = [
  [2, 6],
  [5, 3],
  [1, 4],
];
console.log(matrixDot(A, B));

Hope it helps somebody ;)

Alex Chebotarsky
  • 481
  • 5
  • 19
1

A little bit late for the party, but I think I got a good solution.

IMHO the main challenge that we find when trying to solve this is to relate MatrixA.row1 with MatrixB.col1 and that is one of the reasons, most of the solutions use something like MatrixB[0].length to get the number of columns for the resulting matrix, and, personally, I don't like this "workaround" (I don't have a good reason, I just don't like it). But I do like the combinations of map()s and reduce() as proposed by Jan Turoň.

Then, inspired by this Matrix Multiplication website I thought: If the resulting matrix is always MatrixA.number_of_rows by MatrixB.number_of_columns and the pain is basically to "iterate" through columns, why not transpose the second matrix?

Thank you hobs, for the transpose function.

The final result is the following (I am pretty happy with the result since the time for execution is pretty close to Duloren's solution):

You will notice that I couldn't get rid of the Matrix[0] thing because I needed this approach to transposing the matrix. Well...I am still happy with the result :)

const a = [
  [1, 2, 0],
  [6, 3, 8]
];

const b = [
  [4, 6],
  [1, 9],
  [4, 8]
];

function multiplyMatrix(a, b) {
  const tB = transpose(b);

  // Return the matrix (array of rows)
  return a.map((row_a) => {
  
    // Return the rows with the values (array of values where the length
    // will be the number of columns of 'b', which is the same as
    // the length of `tB` (transposed `b`))
    return tB.map((row_b) => {

      // Return the sum of the products, which is the final value itself
      // (therefore, defines the columns)
      return row_a.reduce((carry, value_of_a, index_of_column_of_a) => {
        
        // Because we transposed `b` the value in b that corresponds to a specific
        // value in `a` will have the same `column_index`.
        const corresponding_b = row_b[index_of_column_of_a];

        return carry + (value_of_a * corresponding_b);
      }, 0);
    });
  });
}

function transpose(m) {
  return Object.keys(m[0]).map(columnIndex => {
    return m.map(row => row[columnIndex])
  });
}


function printMatrix(m, h = '') {
  // console.table(m);
  
  // For those that don't support the console.table()
  let output = h + '\n--------\n';
  
  output += m.reduce((carry, row) => {
    return carry += row.join('    ') + '\n';
  },'');

  console.log(output);
}


printMatrix(a, 'A');
printMatrix(b, 'B');
printMatrix(multiplyMatrix(a, b), 'A times B');
console.log('==========');
printMatrix(transpose(b), 'Transposed B');
Italo
  • 11
  • 3
0

You can solve this problem with dynamic programming using Memoization. It is a term describing an optimization technique where you cache previously computed results, and return the cached result when the same computation is needed again.

        let mat1 = [[1, 2, 3], [2, 1, 2]];

        let mat2 = [[1, 2], [1, 2], [1, 2]];

        function matrixMulti(x, y) {
          let saveComputation = {};
          let finalMat = [],
               length=x.length,
               length1 =  y[0].length,
               length2 =  y.length;
          for (let i = 0; i < length; i++) {
            finalMat.push([]);
            for (let j = 0; j < length1; j++) {
              finalMat[i][j] = 0;
              for (let k = 0; k < length2; k++) {
    // check if we already computed this calculation or not
                if (saveComputation[y[k][j] + '*' + x[i][k]] || saveComputation[x[i][k] + '*' + y[k][j]]) {
                  finalMat[i][j] = finalMat[i][j] + saveComputation[y[k][j] + '*' + x[i][k]];
                } else {
// save if not computed
                  saveComputation[x[i][k] + '*' + y[k][j]] = x[i][k] * y[k][j]; // check format below how it is saved.
                  saveComputation[y[k][j] + '*' + x[i][k]] = x[i][k] * y[k][j];
                  finalMat[i][j] = finalMat[i][j] + saveComputation[y[k][j] + '*' + x[i][k]];
                }
              }
            }
          }

          console.log(finalMat);
        }

        matrixMulti(mat1, mat2);

For the above input value of saveComputation will be

{ '1*1': 1,
  '2*1': 2,
  '1*2': 2,
  '3*1': 3,
  '1*3': 3,
  '2*2': 4,
  '3*2': 6,
  '2*3': 6 }
Shumi Gupta
  • 1,505
  • 2
  • 19
  • 28
  • Do you have any test case with measurable benefits of this attitude? It raise the code accidental complexity, multiplying is a single machine instruction which is less costly than parsing the array key and lookup. My guess is that your solution is actually **slower** compared to classic one. – Jan Turoň Jan 31 '21 at 23:49
0

const getDot = (arrA, arrB, row, col) => {
    return arrA[row].map((val, i) => (val * arrB[i][col]))
  .reduce((valA, valB) => valA + valB);
}

const multiplyMatricies = (a, b) => {
    let matrixShape = new Array(a.length).fill(0)
      .map(() => new Array(b[0].length).fill(0));
        return matrixShape.map((row, i) =>
          row.map((val, j) => getDot(a, b, i, j)));
      }

    const arrA = [
      [1, 3, 0],
      [2, 1, 1]
    ];
    const arrB = [
      [1, 2, 0, 1],
      [2, 3, 1, 2],
      [1, 2, 1, 1]
    ];

    let product = multiplyMatricies(arrA, arrB);
    console.log("product:", product);
0

You can now achieve this using TensorFlow.js:

<!DOCTYPE html>
<html>
  <head>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@3.0.0/dist/tf.min.js"></script>
    <script>
      const matrix_1 = tf.tensor([[1,2],[3,4]]);
      const matrix_2 = tf.tensor([[5,6],[7,8]]);
      
      const matrix_result = tf.matMul(matrix_1, matrix_2);
      matrix_result.print()
    </script>
  </head>
  <body>
    <h1>Check the console log!</h1>
  </body>
</html>
anjanesh
  • 3,771
  • 7
  • 44
  • 58
0

UP =)

function multiplyMatrices(m1, m2) {
  const result = new Array(m1.length)
    .fill(0)
    .map(() => new Array(m2[0].length)
      .fill(0));

  return result
    .map((row, i) => row
      .map((_, j) => m1[i]
        .reduce((sum, e, k) => sum + (e * m2[k][j]), 0)));
}
Ivan
  • 1
  • Welcome to Stackoverflow. This question is asked more than 8 years ago and it has an accepted answer. Please add some details about the reason you are adding a new answer. – MD Zand Nov 30 '22 at 13:11
-8

npm install express

node server.js

var express = require('express');
var app = express();


var A=new Array(3);
var B=new Array(3);
var preA = [ 1, 2, 3, 4, 5, 6,7, 8, 9 ];
var preB = [ 1,1 ,1,2,2, 2,3, 3, 3 ];

//#########################preparing blank 3*3 matrix A and B###############
for(i=0;i<3;i++){
    A[i]=new Array(3);
    B[i]=new Array(3);
}



//#####################Assigning values to matrix places from predefine arrays preA and preB #####
var k=0;
for(i=0;i<3;i++){
    for(j=0;j<3;j++){

        A[i][j]=preA[k];
        B[i][j]=preB[k];
        k++;
    }
};


console.log('################################');
console.log('First matrix:');
console.log(A[0]);
console.log(A[1]);
console.log(A[2]);
console.log('');
console.log('################################');
console.log('Second matrix:');
console.log(B[0]);
console.log(B[1]);
console.log(B[2]);

//###################### multiplication logic as disscussed ################
var result =[];
 for (var i = 0; i < 3; i++) {
        result[i] = new Array(3);
        for (var j = 0; j < 3; j++) {
            var sum = 0;
            for (var k = 0; k < 3; k++) {
                sum += A[i][k] * B[k][j];
            }
            result[i][j] = sum;
        }
    }
console.log('');
console.log('################################');
console.log('################################');
console.log('After Multiplication');

console.log(result[0]);
console.log(result[1]);
console.log(result[2]);



app.listen(9999);
Community
  • 1
  • 1
EDdy
  • 27
  • 2
  • 3
  • 1
    lol, I love that your solution is to create a node.js server. You are operating on the assumption people have npm installed as well, which is not the case for some people (like me). idk if your algorithm actually works apart from node. I just thought it was funny you decided to make node a prerequisite to testing your algorithm. – Shmack Mar 18 '20 at 23:05