图形拟合(一):矩阵运算函数库

图形拟合(一):矩阵运算函数库

目录

背景

基于矩阵运算实现几何图形拟合功能,但调研过JavaScript版本的数学运算库,几乎都不支持矩阵奇异值分解(SVD),故采用math-js重新封装了一个矩阵运算库。

代码

1. 按需引入math-js的基础函数

import {
  create,
  multiplyDependencies,
  sqrtDependencies,
  forEachDependencies,
  matrixDependencies,
  diagDependencies,
  invDependencies,
  transposeDependencies,
  eigsDependencies,
  sizeDependencies,
  matrixFromColumnsDependencies,
  columnDependencies,
  reshapeDependencies,
  countDependencies
} from 'mathjs';

const {
  multiply,
  sqrt,
  forEach,
  matrix,
  diag,
  inv,
  transpose,
  eigs,
  size,
  matrixFromColumns,
  column,
  reshape,
  count,
} = create({
  multiplyDependencies,
  sqrtDependencies,
  forEachDependencies,
  matrixDependencies,
  diagDependencies,
  invDependencies,
  transposeDependencies,
  eigsDependencies,
  sizeDependencies,
  matrixFromColumnsDependencies,
  columnDependencies,
  reshapeDependencies,
  countDependencies
}, {})

2. 封装常用矩阵运算方法

class Mat {
  constructor() {}

  // 初始化matrix 
  static matrix(row: number, col: number, _data: number[]) {
    const _d = _data.slice(0, row * col)
    return reshape(_d, [row, col]);
  }

  // 对角矩阵
  static diag(mat: any) {
    return diag(mat)
  }

  // 矩阵求逆
  static inv(mat: any) {
    return inv(mat);
  }

  // 矩阵转置
  static transpose(mat: any) {
    return transpose(mat);
  }

  // 矩阵乘法
  static multiply(...mats: any) {
    return multiply(...mats);
  }

  // 矩阵遍历
  static forEach(mat: any, cb: Function) {
    forEach(mat, cb);
  }

  // 矩阵SVD分解
  static svd(mat: any) {
    let A = matrix(mat);
    let s : any = size(mat)
    let c = count(mat)
    let A_T = transpose(A);
    let A_TA = multiply(A_T, A);
    let eigsA_TA : any = eigs(A_TA, 1e-8);
    let U = null
    let V = null
    let E = null
    let valA_TA : any = eigsA_TA.values
    let vecA_TA : any = eigsA_TA.vectors
    let lenValA_TA : any = size(valA_TA)
    lenValA_TA = lenValA_TA._data[0]
    let temp_e = new Array(c).fill(0);
    for (let i = lenValA_TA - 1; i >= 0; --i) {
      let r = lenValA_TA - 1 - i;
      temp_e[r * s[1] + r] = sqrt(valA_TA._data[i]);
    }
    E = reshape(matrix(temp_e), s)

    let temp_v = []
    let temp_u = []
    let diagE = diag(E)

    for (let i = lenValA_TA - 1; i >= 0; i-- ) {
      let col = column(vecA_TA, i)
      temp_v.push(col)
      temp_u.push(multiply(1 / diagE._data[lenValA_TA - 1 - i], A, col))
    }

    V = matrixFromColumns(...temp_v)
    U = matrixFromColumns(...temp_u)

    return {
      U,
      V,
      E
    }
  }

  // 矩阵反向回代
  static backSubst(W: any, U: any, Vt: any, b: any) {
    let Vtt = transpose(Vt)
    let Ut = transpose(U)
    let sV = size(Vtt)
    let sU = size(Ut)
    let invDiagW = inv(diag(diag(W)))
    let newDiag = reshape(invDiagW, [sV._data[1], sU._data[0]])
    return multiply(Vtt, newDiag, Ut, b)
  }

  // 矩阵求解
  static solve(A: any, b: any) {
    let {
      E,
      U,
      V
    } = Mat.svd(A)
    V = transpose(V);

    return Mat.backSubst(E, U, V, b);
  }
}