14
votes

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
10
have you tried to debug the code in a browser built-in debugger or perhaps in Firebug? - Aprillion
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

10 Answers

21
votes

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));
17
votes

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()

12
votes

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));
4
votes

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));
2
votes

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));
1
votes

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)

1
votes

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 }
0
votes

Here's my ES6 soulution with math error handling:

const matrixDot = (A, B) => {
  // Math error handling
  const matrices = [A, B];
  const cols = matrices.map((item) => item[0].length);
  if (!matrices.every((item, i) => item.every((row) => row.length === cols[i])))
    return console.error('All rows in a matrix must have equal amount of columns');
  else if (cols[0] !== B.length)
    return console.error(
      'Amount of columns in the 1st matrix must match amount of rows in the 2nd matrix'
    );
  // Calculations
  return A.map((rowA) =>
    B[0].map((_, colBIndex) =>
      rowA.reduce((acc, itemA, rowBIndex) => acc + itemA * B[rowBIndex][colBIndex], 0)
    )
  );
};

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

Here's gist if you want to bookmark it

Hope it helps somebody ;)

0
votes

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);
-6
votes

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);