all files / algebra/src/ MatrixSpace.js

100% Statements 79/79
100% Branches 8/8
100% Functions 9/9
100% Lines 78/78
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204                               23×                 23×   23×   23× 23×   23×                       20×   20×                                             10× 10×   10× 23× 54× 54×   54×       10×             36×   36×               36× 19×       19×                     36×           23×   23× 10×     23×               23× 23×       23× 23×   23×   23× 46× 46× 46×       23× 46×     23×         23×          
var determinant = require('laplace-determinant')
var inherits = require('inherits')
var itemsPool = require('./itemsPool')
var matrixMultiplication = require('matrix-multiplication')
var multiDimArrayIndex = require('multidim-array-index')
var no = require('not-defined')
var operators = require('./operators.json')
var staticProps = require('static-props')
var TensorSpace = require('./TensorSpace')
var tensorContraction = require('tensor-contraction')
var toData = require('./toData')
 
/**
 * Space of m x n matrices
 *
 * ```
 * var R = algebra.R
 *
 * var R2x2 = algebra.MatrixSpace(R)(2)
 * ```
 *
 * @param {Object} Scalar
 *
 * @returns {Function} anonymous with signature (numRows[, numCols])
 */
 
function MatrixSpace (Scalar) {
  var contraction = tensorContraction.bind(null, Scalar.addition)
 
  /**
   * @param {Number} numRows
   * @param {Number} [numCols] defaults to a square matrix.
   *
   * @returns {Function} Matrix
   */
 
  return function (numRows, numCols) {
    // numCols defaults to numRows
    if (no(numCols)) numCols = numRows
 
    var isSquare = (numRows === numCols)
    var indices = [numRows, numCols]
 
    var AbstractMatrix = TensorSpace(Scalar)(indices)
 
    /**
     * Calculates the matrix trace.
     *
     * https://en.wikipedia.org/wiki/Trace_(linear_algebra)
     *
     * @param {Object|Array} matrix
     *
     * @returns {Object} scalar
     */
 
    function trace (matrix) {
      var matrixData = toData(matrix)
 
      return contraction([0, 1], indices, matrixData)
    }
 
    /**
     * Multiplies row by column to the right.
     *
     * @param {Object|Array} rightMatrix
     *
     * @returns {Object} matrix
     */
 
    function multiplication (leftMatrix, rightMatrix) {
      var leftMatrixData = toData(leftMatrix)
      var rightMatrixData = toData(rightMatrix)
 
      var rowByColumnMultiplication = matrixMultiplication(Scalar)(numCols)
 
      return rowByColumnMultiplication(leftMatrixData, rightMatrixData)
    }
 
    /**
     * Calculates the transpose of a matrix.
     *
     * @param {Object|Array} matrix
     *
     * @returns {Array} matrix
     */
 
    function transpose (matrix) {
      var matrixData = toData(matrix)
      var transposedData = []
 
      for (var i = 0; i < numRows; i++) {
        for (var j = 0; j < numCols; j++) {
          var index = multiDimArrayIndex([numRows, numCols], [i, j])
          var transposedIndex = multiDimArrayIndex([numCols, numRows], [j, i])
 
          transposedData[transposedIndex] = matrixData[index]
        }
      }
 
      return transposedData
    }
 
    /**
     * Matrix element.
     */
 
    function Matrix (data) {
      AbstractMatrix.call(this, data)
 
      staticProps(this)({
        numCols,
        numRows
      })
 
      function computeDeterminant () {
        var det = determinant(data, Scalar, numRows)
 
        return new Scalar(det)
      }
 
      if (isSquare) {
        staticProps(this)({
          trace: trace(data)
        })
 
        staticProps(this)({
          determinant: computeDeterminant,
          det: computeDeterminant
        })
      }
 
      function transposed () {
        var result = transpose(data)
        var VectorSpace = itemsPool.get('VectorSpace')
 
        if (numRows === 1) {
          var Vector = VectorSpace(Scalar)(numCols)
          return new Vector(result)
        } else {
          var Matrix = MatrixSpace(Scalar)(numCols, numRows)
          return new Matrix(result)
        }
      }
 
      staticProps(this)({
        transposed,
        tr: transposed
      })
    }
 
    inherits(Matrix, AbstractMatrix)
 
    if (isSquare) {
      Matrix.trace = trace
    }
 
    Matrix.prototype.multiplication = function (rightMatrix) {
      var leftMatrixData = this.data
      var result = multiplication(leftMatrixData, rightMatrix)
 
      var rightNumRows = numCols
      var rightNumCols = result.length / rightNumRows
 
      var Matrix = MatrixSpace(Scalar)(rightNumRows, rightNumCols)
 
      return new Matrix(result)
    }
 
    // Static operators.
 
    Matrix.multiplication = multiplication
    Matrix.transpose = transpose
 
    // Aliases
 
    Matrix.tr = Matrix.transpose
    Matrix.mul = Matrix.multiplication
 
    Matrix.prototype.mul = Matrix.prototype.multiplication
 
    operators.group.forEach((operator) => {
      operators.aliasesOf[operator].forEach((alias) => {
        Matrix[alias] = Matrix[operator]
        Matrix.prototype[alias] = Matrix.prototype[operator]
      })
    })
 
    operators.group.forEach((operator) => {
      Matrix[operator] = AbstractMatrix[operator]
    })
 
    staticProps(Matrix)({
      numCols,
      numRows
    })
 
    return Matrix
  }
}
 
itemsPool.set('MatrixSpace', MatrixSpace)
 
module.exports = MatrixSpace