All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.librec.math.structure.DenseMatrix Maven / Gradle / Ivy

// Copyright (C) 2014 Guibing Guo
//
// This file is part of LibRec.
//
// LibRec is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// LibRec is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with LibRec. If not, see .
//

package net.librec.math.structure;

import net.librec.common.LibrecException;
import net.librec.math.algorithm.Randoms;
import net.librec.math.algorithm.SVD;
import net.librec.util.StringUtil;

import java.io.Serializable;
import java.util.Arrays;

/**
 * Data Structure: dense matrix 
*

* A big reason that we do not adopt original DenseMatrix from M4J libraray is because the latter using one-dimensional * array to store data, which will often cause OutOfMemory exception due to the limit of maximum length of a * one-dimensional Java array. * * @author guoguibing */ public class DenseMatrix implements DataMatrix, Serializable { private static final long serialVersionUID = -2069621030647530185L; /** dimension */ public int numRows, numColumns, topN; /** read data */ public double[][] data; /** * Construct a dense matrix with specified dimensions * * @param numRows number of rows * @param numColumns number of columns */ public DenseMatrix(int numRows, int numColumns) { this.numRows = numRows; this.numColumns = numColumns; data = new double[numRows][numColumns]; } /** * Construct a dense matrix with specified dimensions * * @param numRows number of rows * @param numColumns number of columns * @param topN numnber of top N */ public DenseMatrix(int numRows, int numColumns, int topN) { this.numRows = numRows; this.numColumns = numColumns; this.topN = topN; data = new double[numRows][numColumns]; } /** * Construct a dense matrix by copying data from a given 2D array * * @param array data array */ public DenseMatrix(double[][] array) { this(array.length, array[0].length); for (int i = 0; i < numRows; i++) for (int j = 0; j < numColumns; j++) data[i][j] = array[i][j]; } /** * Construct a dense matrix by a shallow copy of a data array * * @param array the data array * @param numColumns number of columns * @param numRows number of rows */ public DenseMatrix(double[][] array, int numRows, int numColumns) { this.numRows = numRows; this.numColumns = numColumns; this.data = array; } /** * Construct a dense matrix by copying data from a given matrix * * @param mat input matrix */ public DenseMatrix(DenseMatrix mat) { this(mat.data); } /** * Make a deep copy of current matrix * * @return a cloned dense matrix */ public DenseMatrix clone() { return new DenseMatrix(this); } /** * Construct an identity matrix * * @param dim dimension * @return an identity matrix */ public static DenseMatrix eye(int dim) { DenseMatrix mat = new DenseMatrix(dim, dim); for (int i = 0; i < mat.numRows; i++) mat.set(i, i, 1.0); return mat; } /** * Initialize a dense matrix with small Guassian values
*

* NOTE: small initial values make it easier to train a model; otherwise a very small learning rate * may be needed (especially when the number of factors is large) which can cause bad performance. * * @param mean mean of the gaussian function * @param sigma sigma of the gaussian function */ public void init(double mean, double sigma) { for (int i = 0; i < numRows; i++) for (int j = 0; j < numColumns; j++) data[i][j] = Randoms.gaussian(mean, sigma); } /** * Initialize a dense matrix with small random values in (0, range) * * @param range max of the range */ public void init(double range) { for (int i = 0; i < numRows; i++) for (int j = 0; j < numColumns; j++) data[i][j] = Randoms.uniform(0, range); } /** * Initialize a dense matrix with small random values in (0, 1) */ public void init() { init(1.0); } /** * @return number of rows */ public int numRows() { return numRows; } /** * @return number of columns */ public int numColumns() { return numColumns; } /** * Return a copy of row data as a dense vector. * * @param rowId row id * @return a copy of row data as a dense vector */ public DenseVector row(int rowId) { return row(rowId, true); } /** * Return a vector of a specific row. * * @param rowId row id * @param deep whether to copy data or only shallow copy for executing speedup purpose * @return a vector of a specific row */ public DenseVector row(int rowId, boolean deep) { return new DenseVector(data[rowId], deep); } /** * Return a sub matrix of this matrix. * * @param rowStart the row index to start * @param rowEnd the row index to end * @param colStart the column index to start * @param colEnd the column index to end * @return a sub matrix of this matrix */ public DenseMatrix getSubMatrix(int rowStart, int rowEnd, int colStart, int colEnd) { if (rowStart >= rowEnd || colStart >= colEnd) { return null; } else { int r = rowEnd - rowStart + 1; int c = colEnd - colStart + 1; double[][] d = new double[r][c]; for (int i = rowStart; i <= rowEnd; i++) { for (int j = colStart; j <= colEnd; j++) { double a = data[i][j]; d[i - rowStart][j - colStart] = a; } } return new DenseMatrix(d); } } /** * Return a copy of column data as a dense vector. * * @param column column id * @return a copy of column data as a dense vector */ public DenseVector column(int column) { DenseVector vec = new DenseVector(numRows); for (int i = 0; i < numRows; i++) vec.set(i, data[i][column]); return vec; } /** * Compute mean of a column of the current matrix. * * @param column column id * @return mean of a column of the current matrix */ public double columnMean(int column) { double sum = 0.0; for (int i = 0; i < numRows; i++) sum += data[i][column]; return sum / numRows; } /** * @return the matrix norm-2 */ public double norm() { double res = 0; for (int i = 0; i < numRows; i++) for (int j = 0; j < numColumns; j++) res += data[i][j] * data[i][j]; return Math.sqrt(res); } /** * Inner product of two row vectors * * @param m the first matrix * @param mrow row of the first matrix * @param n the second matrix * @param nrow row of the second matrix * @return inner product of two row vectors */ public static double rowMult(DenseMatrix m, int mrow, DenseMatrix n, int nrow) { assert m.numColumns == n.numColumns; double res = 0; for (int j = 0, k = m.numColumns; j < k; j++) res += m.get(mrow, j) * n.get(nrow, j); return res; } /** * Inner product of two column vectors * * @param m the first matrix * @param mcol column of the first matrix * @param n the second matrix * @param ncol column of the second matrix * @return inner product of two column vectors */ public static double colMult(DenseMatrix m, int mcol, DenseMatrix n, int ncol) { assert m.numRows == n.numRows; double res = 0; for (int j = 0, k = m.numRows; j < k; j++) res += m.get(j, mcol) * n.get(j, ncol); return res; } /** * Dot product of row x col between two matrices. * * @param m the first matrix * @param mrow row id of the first matrix * @param n the second matrix * @param ncol column id of the second matrix * @return dot product of row of the first matrix and column of the second matrix * @throws LibrecException if {@code m.numColumns != n.numRows} */ public static double product(DenseMatrix m, int mrow, DenseMatrix n, int ncol) throws LibrecException { // assert m.numColumns == n.numRows; if ( m.numColumns != n.numRows) { throw new LibrecException("m.numColumns should equal to n.numRows"); } double res = 0; for (int j = 0; j < m.numColumns; j++) res += m.get(mrow, j) * n.get(j, ncol); return res; } /** * Return Kronecker product of two arbitrary matrices * * @param M a dense matrix * @param N an other dense matrix * @return Kronecker product of two arbitrary matrices */ public static DenseMatrix kroneckerProduct(DenseMatrix M, DenseMatrix N) { DenseMatrix res = new DenseMatrix(M.numRows * N.numRows, M.numColumns * N.numColumns); for (int i = 0; i < M.numRows; i++) { for (int j = 0; j < M.numColumns; j++) { double Mij = M.get(i, j); // Mij*N for (int ni = 0; ni < N.numRows; ni++) { for (int nj = 0; nj < N.numColumns; nj++) { int row = i * N.numRows + ni; int col = j * N.numColumns + nj; res.set(row, col, Mij * N.get(ni, nj)); } } } } return res; } /** * Return Khatri-Rao product of two matrices. * * @param M a dense matrix * @param N an other dense matrix * @return Khatri-Rao product of two matrices * @throws Exception if error occurs */ public static DenseMatrix khatriRaoProduct(DenseMatrix M, DenseMatrix N) throws Exception { if (M.numColumns != N.numColumns) throw new Exception("The number of columns of two matrices is not equal!"); DenseMatrix res = new DenseMatrix(M.numRows * N.numRows, M.numColumns); for (int j = 0; j < M.numColumns; j++) { for (int i = 0; i < M.numRows; i++) { double Mij = M.get(i, j); // Mij* Nj for (int ni = 0; ni < N.numRows; ni++) { int row = ni + i * N.numRows; res.set(row, j, Mij * N.get(ni, j)); } } } return res; } /** * Return Hadamard product of two matrices. * * @param M a dense matrix * @param N an other dense matrix * @return Hadamard product of two matrices * @throws Exception if The dimensions of two matrices are not consistent */ public static DenseMatrix hadamardProduct(DenseMatrix M, DenseMatrix N) throws Exception { if (M.numRows != N.numRows || M.numColumns != N.numColumns) throw new Exception("The dimensions of two matrices are not consistent!"); DenseMatrix res = new DenseMatrix(M.numRows, M.numColumns); for (int i = 0; i < M.numRows; i++) { for (int j = 0; j < M.numColumns; j++) { res.set(i, j, M.get(i, j) * N.get(i, j)); } } return res; } /** * @return the result of {@code A^T A} */ public DenseMatrix transMult() { DenseMatrix res = new DenseMatrix(numColumns, numColumns); for (int i = 0; i < numColumns; i++) { // inner product of row i and row k for (int k = 0; k < numColumns; k++) { double val = 0; for (int j = 0; j < numRows; j++) { val += get(j, i) * get(j, k); } res.set(i, k, val); } } return res; } /** * Matrix multiplication with a dense matrix * * @param mat a dense matrix * @return a dense matrix with results of matrix multiplication * @throws LibrecException if {@code this.numColumns != mat.numRows} */ public DenseMatrix mult(DenseMatrix mat) throws LibrecException { // assert this.numColumns == mat.numRows; if (this.numColumns != mat.numRows) { throw new LibrecException("this.numColumns should equal to mat.numRows"); } DenseMatrix res = new DenseMatrix(this.numRows, mat.numColumns); for (int i = 0; i < res.numRows; i++) { for (int j = 0; j < res.numColumns; j++) { double product = 0; for (int k = 0; k < this.numColumns; k++) product += data[i][k] * mat.data[k][j]; res.set(i, j, product); } } return res; } /** * Matrix multiplication with a sparse matrix * * @param mat a sparse matrix * @return a dense matrix with results of matrix multiplication * @throws LibrecException if {@code this.numColumns != mat.numRows} */ public DenseMatrix mult(SparseMatrix mat) throws LibrecException { if(this.numColumns != mat.numRows){ throw new LibrecException("numColumns should equal to numRows"); } DenseMatrix res = new DenseMatrix(this.numRows, mat.numColumns); for (int j = 0; j < res.numColumns; j++) { SparseVector col = mat.column(j); // only one-time computation for (int i = 0; i < res.numRows; i++) { double product = 0; for (VectorEntry ve : col) product += data[i][ve.index()] * ve.get(); res.set(i, j, product); } } return res; } /** * Do {@code matrix x vector} between current matrix and a given vector * * @param vec a given vector * @return a dense vector with the results of {@code matrix x vector} * @throws LibrecException if {@code this.numColumns != vec.size} */ public DenseVector mult(DenseVector vec) throws LibrecException { // assert this.numColumns == vec.size; if (this.numColumns != vec.size) { throw new LibrecException("this.numColumns should equal to vec.size"); } DenseVector res = new DenseVector(this.numRows); for (int i = 0; i < this.numRows; i++) res.set(i, row(i, false).inner(vec)); return res; } public DenseVector mult(SparseVector vec) { DenseVector res = new DenseVector(this.numRows); for (int i = 0; i < this.numRows; i++) { double product = 0; for (VectorEntry ve : vec) product += data[i][ve.index()] * ve.get(); res.set(i, product); } return res; } /** * Matrix multiplication of a sparse matrix by a dense matrix * * @param sm a sparse matrix * @param dm a dense matrix * @return a dense matrix with the results of matrix multiplication * @throws LibrecException if {@code sm.numColumns != dm.numRows} */ public static DenseMatrix mult(SparseMatrix sm, DenseMatrix dm) throws LibrecException { //assert sm.numColumns == dm.numRows; if (sm.numColumns != dm.numRows) { throw new LibrecException("sm.numColumns should equal to dm.numRows"); } DenseMatrix res = new DenseMatrix(sm.numRows, dm.numColumns); for (int i = 0; i < res.numRows; i++) { SparseVector row = sm.row(i); for (int j = 0; j < res.numColumns; j++) { double product = 0; for (int k : row.getIndex()) product += row.get(k) * dm.data[k][j]; res.set(i, j, product); } } return res; } /** * Get the value at entry [row, column] * @param column column index * @param row row index * @return value at entry [row, column] */ public double get(int row, int column) { return data[row][column]; } /** * Set a value to entry [row, column] * * @param row row index * @param column column index * @param val the value to be set */ public void set(int row, int column, double val) { if (topN < 0) { } else { data[row][column] = val; } } /** * Set a value to all entries * * @param val the value to be set */ public void setAll(double val) { for (int row = 0; row < numRows; row++) { for (int col = 0; col < numColumns; col++) { data[row][col] = val; } } } /** * Return the sum of data entries in a row * * @param row row index * @return the sum of data entries in a row */ public double sumOfRow(int row) { double res = 0; for (int col = 0; col < numColumns; col++) res += data[row][col]; return res; } /** * Return the sum of data entries in a column. * * @param col column index * @return the sum of data entries in a column */ public double sumOfColumn(int col) { double res = 0; for (int row = 0; row < numRows; row++) res += data[row][col]; return res; } /** * @return the sum of all data entries */ public double sum() { double res = 0; for (int row = 0; row < numRows; row++) { for (int col = 0; col < numColumns; col++) { res += data[row][col]; } } return res; } /** * Return a new matrix by scaling the current matrix. * * @param val a given value * @return a new matrix by scaling the current matrix */ public DenseMatrix scale(double val) { DenseMatrix mat = new DenseMatrix(numRows, numColumns); for (int i = 0; i < numRows; i++) for (int j = 0; j < numColumns; j++) mat.data[i][j] = this.data[i][j] * val; return mat; } /** * Return this matrix by scaling the current matrix. * * @param val a given value for scaling * @return this matrix by scaling the current matrix */ public DenseMatrix scaleEqual(double val) { for (int i = 0; i < numRows; i++) for (int j = 0; j < numColumns; j++) data[i][j] *= val; return this; } /** * Add a value to entry [row, column] * * @param val the value to be added * @param row row index * @param column column index */ public void add(int row, int column, double val) { data[row][column] += val; } /** * Do {@code A + B} matrix operation * * @param mat another matrix * @return a new matrix with results of {@code C = A + B} * @throws LibrecException if {@code numRows != mat.numRows} or * {@code numColumns != mat.numColumns} */ public DenseMatrix add(DenseMatrix mat) throws LibrecException { //assert numRows == mat.numRows; if (numRows != mat.numRows) { throw new LibrecException("numRows should be equal"); } //assert numColumns == mat.numColumns; if (numColumns != mat.numColumns) { throw new LibrecException("numColumns should be equal"); } DenseMatrix res = new DenseMatrix(numRows, numColumns); for (int i = 0; i < numRows; i++) for (int j = 0; j < numColumns; j++) res.data[i][j] = data[i][j] + mat.data[i][j]; return res; } /** * Do {@code A + B} matrix operation * * @param mat another matrix * @return this matrix with results of {@code A = A + B} * @throws LibrecException if {@code numRows != mat.numRows} or * {@code numColumns != mat.numColumns} */ public DenseMatrix addEqual(DenseMatrix mat) throws LibrecException { //assert numRows == mat.numRows; if (numRows != mat.numRows) { throw new LibrecException("numRows should be equal"); } //assert numColumns == mat.numColumns; if (numColumns != mat.numColumns) { throw new LibrecException("numColumns should be equal"); } for (int i = 0; i < numRows; i++) for (int j = 0; j < numColumns; j++) data[i][j] += mat.data[i][j]; return this; } /** * Do {@code A + B} matrix operation * * @param mat another matric * @return a matrix with results of {@code C = A + B} * @throws LibrecException if {@code numRows != mat.numRows} or * {@code numColumns != mat.numColumns} */ public DenseMatrix add(SparseMatrix mat) throws LibrecException { //assert numRows == mat.numRows; if (numRows != mat.numRows) { throw new LibrecException("numRows should be equal"); } //assert numColumns == mat.numColumns; if (numColumns != mat.numColumns) { throw new LibrecException("numColumns should be equal"); } DenseMatrix res = this.clone(); for (MatrixEntry me : mat) res.add(me.row(), me.column(), me.get()); return res; } /** * Do {@code A + B} matrix operation * * @param mat another matrix * @return this matrix with results of {@code A = A + B} * @throws LibrecException if {@code numRows != mat.numRows} or * {@code numColumns != mat.numColumns} */ public DenseMatrix addEqual(SparseMatrix mat) throws LibrecException { //assert numRows == mat.numRows; if (numRows != mat.numRows) { throw new LibrecException("numRows should be equal"); } //assert numColumns == mat.numColumns; if (numColumns != mat.numColumns) { throw new LibrecException("numColumns should be equal"); } for (MatrixEntry me : mat) data[me.row()][me.column()] += me.get(); return this; } /** * Do {@code A + c} matrix operation, where {@code c} is a constant. Each entries will be added by {@code c} * * @param val the value to be added * @return a new matrix with results of {@code C = A + c} */ public DenseMatrix add(double val) { DenseMatrix res = new DenseMatrix(numRows, numColumns); for (int i = 0; i < numRows; i++) for (int j = 0; j < numColumns; j++) res.data[i][j] = data[i][j] + val; return res; } /** * Do {@code A + c} matrix operation, where {@code c} is a constant. Each entries will be added by {@code c} * * @param val the value to be added * @return this matrix with results of {@code A = A + c} */ public DenseMatrix addEqual(double val) { for (int i = 0; i < numRows; i++) for (int j = 0; j < numColumns; j++) data[i][j] += val; return this; } /** * Do {@code A - B} matrix operation * * @param mat another matrix * @return a new matrix with results of {@code C = A - B} * @throws LibrecException if {@code numRows != mat.numRows} or * {@code numColumns != mat.numColumns} */ public DenseMatrix minus(DenseMatrix mat) throws LibrecException { //assert numRows == mat.numRows; if (numRows != mat.numRows) { throw new LibrecException("numRows should be equal"); } //assert numColumns == mat.numColumns; if (numColumns != mat.numColumns) { throw new LibrecException("numColumns should be equal"); } DenseMatrix res = new DenseMatrix(numRows, numColumns); for (int i = 0; i < numRows; i++) for (int j = 0; j < numColumns; j++) res.data[i][j] = data[i][j] - mat.data[i][j]; return res; } /** * Do {@code A - B} matrix operation * * @param mat another matrix * @return this matrix with results of {@code A = A - B} * @throws LibrecException if {@code numRows != mat.numRows} or * {@code numColumns != mat.numColumns} */ public DenseMatrix minusEqual(DenseMatrix mat) throws LibrecException { //assert numRows == mat.numRows; if (numRows != mat.numRows) { throw new LibrecException("numRows should be equal"); } //assert numColumns == mat.numColumns; if (numColumns != mat.numColumns) { throw new LibrecException("numColumns should be equal"); } for (int i = 0; i < numRows; i++) for (int j = 0; j < numColumns; j++) data[i][j] -= mat.data[i][j]; return this; } /** * Do {@code A - B} matrix operation * * @param mat another matrix * @return a matrix with results of {@code C = A - B} * @throws LibrecException if {@code numRows != mat.numRows} or * {@code numColumns != mat.numColumns} */ public DenseMatrix minus(SparseMatrix mat) throws LibrecException { //assert numRows == mat.numRows; if (numRows != mat.numRows) { throw new LibrecException("numRows should be equal"); } //assert numColumns == mat.numColumns; if (numColumns != mat.numColumns) { throw new LibrecException("numColumns should be equal"); } DenseMatrix res = this.clone(); for (MatrixEntry me : mat) res.add(me.row(), me.column(), -me.get()); return res; } /** * Do {@code A - B} matrix operation * * @param mat another matrix * @return this matrix with results of {@code C = A - B} * @throws LibrecException if {@code numRows != mat.numRows} or * {@code numColumns != mat.numColumns} */ public DenseMatrix minusEqual(SparseMatrix mat) throws LibrecException { //assert numRows == mat.numRows; if (numRows != mat.numRows) { throw new LibrecException("numRows should be equal"); } //assert numColumns == mat.numColumns; if (numColumns != mat.numColumns) { throw new LibrecException("numColumns should be equal"); } for (MatrixEntry me : mat) data[me.row()][me.column()] -= me.get(); return this; } /** * Do {@code A - c} matrix operation, where {@code c} is a constant. Each entries will be added by {@code c} * * @param val the value to for minus * @return a new matrix with results of {@code C = A - c} */ public DenseMatrix minus(double val) { DenseMatrix res = new DenseMatrix(numRows, numColumns); for (int i = 0; i < numRows; i++) for (int j = 0; j < numColumns; j++) res.data[i][j] = data[i][j] - val; return res; } /** * Do {@code A - c} matrix operation, where {@code c} is a constant. Each entries will be added by {@code c} * * @param val the value to for minus * @return this matrix with results of {@code A = A - c} */ public DenseMatrix minusEqual(double val) { for (int i = 0; i < numRows; i++) for (int j = 0; j < numColumns; j++) data[i][j] -= val; return this; } /** * @return the Cholesky decomposition of the current matrix */ public DenseMatrix cholesky() { if (this.numRows != this.numColumns) throw new RuntimeException("Matrix is not square"); int n = numRows; DenseMatrix L = new DenseMatrix(n, n); for (int i = 0; i < n; i++) { for (int j = 0; j <= i; j++) { double sum = 0.0; for (int k = 0; k < j; k++) sum += L.get(i, k) * L.get(j, k); double val = i == j ? Math.sqrt(data[i][i] - sum) : (data[i][j] - sum) / L.get(j, j); L.set(i, j, val); } if (Double.isNaN(L.get(i, i))) return null; } return L.transpose(); } /** * @return a transposed matrix of current matrix */ public DenseMatrix transpose() { DenseMatrix mat = new DenseMatrix(numColumns, numRows); for (int i = 0; i < mat.numRows; i++) for (int j = 0; j < mat.numColumns; j++) mat.set(i, j, this.data[j][i]); return mat; } /** * @return a covariance matrix of the current matrix */ public DenseMatrix cov() { DenseMatrix mat = new DenseMatrix(numColumns, numColumns); for (int i = 0; i < numColumns; i++) { DenseVector xi = this.column(i); xi = xi.minus(xi.mean()); mat.set(i, i, xi.inner(xi) / (xi.size - 1)); for (int j = i + 1; j < numColumns; j++) { DenseVector yi = this.column(j); double val = xi.inner(yi.minus(yi.mean())) / (xi.size - 1); mat.set(i, j, val); mat.set(j, i, val); } } return mat; } /** * Compute the inverse of a matrix by LU decomposition * * @return the inverse matrix of current matrix * @deprecated use {@code inv} instead which is slightly faster */ public DenseMatrix inverse() { if (numRows != numColumns) throw new RuntimeException("Only square matrix can do inversion"); int n = numRows; DenseMatrix mat = new DenseMatrix(this); if (n == 1) { mat.set(0, 0, 1.0 / mat.get(0, 0)); return mat; } int row[] = new int[n]; int col[] = new int[n]; double temp[] = new double[n]; int hold, I_pivot, J_pivot; double pivot, abs_pivot; // set up row and column interchange vectors for (int k = 0; k < n; k++) { row[k] = k; col[k] = k; } // begin main reduction loop for (int k = 0; k < n; k++) { // find largest element for pivot pivot = mat.get(row[k], col[k]); I_pivot = k; J_pivot = k; for (int i = k; i < n; i++) { for (int j = k; j < n; j++) { abs_pivot = Math.abs(pivot); if (Math.abs(mat.get(row[i], col[j])) > abs_pivot) { I_pivot = i; J_pivot = j; pivot = mat.get(row[i], col[j]); } } } if (Math.abs(pivot) < 1.0E-10) throw new RuntimeException("Matrix is singular !"); hold = row[k]; row[k] = row[I_pivot]; row[I_pivot] = hold; hold = col[k]; col[k] = col[J_pivot]; col[J_pivot] = hold; // reduce about pivot mat.set(row[k], col[k], 1.0 / pivot); for (int j = 0; j < n; j++) { if (j != k) { mat.set(row[k], col[j], mat.get(row[k], col[j]) * mat.get(row[k], col[k])); } } // inner reduction loop for (int i = 0; i < n; i++) { if (k != i) { for (int j = 0; j < n; j++) { if (k != j) { double val = mat.get(row[i], col[j]) - mat.get(row[i], col[k]) * mat.get(row[k], col[j]); mat.set(row[i], col[j], val); } } mat.set(row[i], col[k], -mat.get(row[i], col[k]) * mat.get(row[k], col[k])); } } } // end main reduction loop // unscramble rows for (int j = 0; j < n; j++) { for (int i = 0; i < n; i++) temp[col[i]] = mat.get(row[i], j); for (int i = 0; i < n; i++) mat.set(i, j, temp[i]); } // unscramble columns for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) temp[row[j]] = mat.get(i, col[j]); for (int j = 0; j < n; j++) mat.set(i, j, temp[j]); } return mat; } /** * NOTE: this implementation (adopted from PREA package) is slightly faster than {@code inverse}, especially when * {@code numRows} is large. * * @return the inverse matrix of current matrix */ public DenseMatrix inv() { if (this.numRows != this.numColumns) throw new RuntimeException("Dimensions disagree"); int n = this.numRows; DenseMatrix mat = DenseMatrix.eye(n); if (n == 1) { mat.set(0, 0, 1 / this.get(0, 0)); return mat; } DenseMatrix b = new DenseMatrix(this); for (int i = 0; i < n; i++) { // find pivot: double mag = 0; int pivot = -1; for (int j = i; j < n; j++) { double mag2 = Math.abs(b.get(j, i)); if (mag2 > mag) { mag = mag2; pivot = j; } } // no pivot (error): if (pivot == -1 || mag == 0) return mat; // move pivot row into position: if (pivot != i) { double temp; for (int j = i; j < n; j++) { temp = b.get(i, j); b.set(i, j, b.get(pivot, j)); b.set(pivot, j, temp); } for (int j = 0; j < n; j++) { temp = mat.get(i, j); mat.set(i, j, mat.get(pivot, j)); mat.set(pivot, j, temp); } } // normalize pivot row: mag = b.get(i, i); for (int j = i; j < n; j++) b.set(i, j, b.get(i, j) / mag); for (int j = 0; j < n; j++) mat.set(i, j, mat.get(i, j) / mag); // eliminate pivot row component from other rows: for (int k = 0; k < n; k++) { if (k == i) continue; double mag2 = b.get(k, i); for (int j = i; j < n; j++) b.set(k, j, b.get(k, j) - mag2 * b.get(i, j)); for (int j = 0; j < n; j++) mat.set(k, j, mat.get(k, j) - mag2 * mat.get(i, j)); } } return mat; } /** * @return Moore–Penrose pseudoinverse based on singular value decomposition (SVD) * * @throws LibrecException if error occurs during mult */ public DenseMatrix pinv() throws LibrecException { if (numRows < numColumns) { DenseMatrix res = this.transpose().pinv(); if (res != null) res = res.transpose(); return res; } SVD svd = this.svd(); DenseMatrix U = svd.getU(), S = svd.getS(), V = svd.getV(); // compute S^+ DenseMatrix SPlus = S.clone(); for (int i = 0; i < SPlus.numRows; i++) { double val = SPlus.get(i, i); if (val != 0) SPlus.set(i, i, 1.0 / val); } return V.mult(SPlus).mult(U.transpose()); } public SVD svd() { return new SVD(this); } /** * Set one value to a specific row. * * @param row row id * @param val value to be set */ public void setRow(int row, double val) { Arrays.fill(data[row], val); } /** * Set values of one dense vector to a specific row. * * @param row row id * @param vals values of a dense vector */ public void setRow(int row, DenseVector vals) { for (int j = 0; j < numColumns; j++) data[row][j] = vals.data[j]; } /** * Clear and reset all entries to 0. */ public void clear() { for (int i = 0; i < numRows; i++) setRow(i, 0.0); } @Override public String toString() { return StringUtil.toString(data); } /** * @return the data */ public double[][] getData() { return data; } @Override public int size() { return numRows * numColumns; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy