// Copyright (C) 2010 Steffen Rendle, Zeno Gantner // Copyright (C) 2011 Zeno Gantner, Chris Newell // // This file is part of MyMediaLite. // // MyMediaLite 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. // // MyMediaLite 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 MyMediaLite. If not, see <http://www.gnu.org/licenses/>. package org.mymedialite.datatype; import java.util.ArrayList; import java.util.List; import org.mymedialite.util.Random; /** * Utilities to work with matrices * * @version 2.03 */ public class MatrixExtensions { /** * Initializes one row of a double matrix with normal distributed (Gaussian) noise. * @param matrix the matrix to initialize * @param mean the mean of the normal distribution drawn from * @param stdev the standard deviation of the normal distribution * @param row the row to be initialized * @version 2.03 */ public static void rowInitNormal(Matrix<Double> matrix, int row, double mean, double stdev) { Random random = Random.getInstance(); for (int j = 0; j < matrix.dim2; j++) matrix.set(row, j, random.nextNormal(mean, stdev)); } /** * Initializes one column of a double matrix with normal distributed (Gaussian) noise. * @param matrix the matrix to initialize * @param mean the mean of the normal distribution drawn from * @param stdev the standard deviation of the normal distribution * @param column the column to be initialized */ public static void columnInitNormal(Matrix<Double> matrix, int column, double mean, double stdev) { Random random = Random.getInstance(); for (int i = 0; i < matrix.dim1; i++) matrix.set(i, column, random.nextNormal(mean, stdev)); } /** * Initializes a double matrix with normal distributed (Gaussian) noise. * @param matrix the matrix to initialize * @param mean the mean of the normal distribution drawn from * @param stdev the standard deviation of the normal distribution */ public static void initNormal(Matrix<Double> matrix, double mean, double stdev) { Random random = Random.getInstance(); for (int i = 0; i < matrix.dim1; i++) for (int j = 0; j < matrix.dim2; j++) matrix.set(i, j, random.nextNormal(mean, stdev)); } /** * Increments the specified matrix element by a double value. * @param matrix The matrix * @param i the row * @param j the column * @param v the value */ public static void inc(Matrix<Double> matrix, int i, int j, double v) { matrix.data[i * matrix.dim2 + j] = (Double) matrix.data[i * matrix.dim2 + j] + v; } /** * Increment the elements in one matrix by the ones in another * @param matrix1 the matrix to be incremented * @param matrix2 the other matrix */ public static void inc(Matrix<Double> matrix1, Matrix<Double> matrix2) { if (matrix1.dim1 != matrix2.dim1 || matrix1.dim2 != matrix2.dim2) throw new IllegalArgumentException("Matrix sizes do not match."); int dim1 = matrix1.dim1; int dim2 = matrix1.dim2; for (int x = 0; x < dim1; x++) for (int y = 0; y < dim2; y++) matrix1.data[x * dim2 + y] = (Double) matrix1.data[x * dim2 + y] + (Double)matrix2.data[x * dim2 + y]; } /** * Increments the specified matrix element by 1. * @param matrix the matrix * @param i the row * @param j the column */ public static void inc(Matrix<Integer> matrix, int i, int j) { matrix.data[i * matrix.dim2 + j] = (Integer) matrix.data[i * matrix.dim2 + j] + 1; } /** * Increment all entries of a matrix with a scalar. * @param matrix the matrix * @param d the number to increment with */ public static void inc(Matrix<Double> matrix, double d) { for (int x = 0; x < matrix.dim1; x++) for (int y = 0; y < matrix.dim2; y++) matrix.data[x * matrix.dim2 + y] = (Double) matrix.data[x * matrix.dim2 + y] + d; } /** * Compute the average value of the entries in a column of a matrix. * @param matrix the matrix * @param col the column ID * @return the average */ public static double columnAverage(Matrix<Double> matrix, int col) { if (matrix.dim1 == 0) throw new IllegalArgumentException("Cannot compute average of 0 entries."); double sum = 0; for (int x = 0; x < matrix.dim1; x++) sum += (Double)matrix.data[x * matrix.dim2 + col]; return sum / matrix.dim1; } /** * Compute the average value of the entries in a row of a matrix. * @param matrix the matrix * @param row the row ID * @return the average */ public static double rowAverage(Matrix<Double> matrix, int row) { if (matrix.dim2 == 0) throw new IllegalArgumentException("Cannot compute average of 0 entries."); double sum = 0; for (int y = 0; y < matrix.dim2; y++) sum += (Double)matrix.data[row * matrix.dim2 + y]; return sum / matrix.dim2; } /** * Multiply all entries of a matrix with a scalar. * @param matrix the matrix * @param d the number to multiply with */ public static void multiply(Matrix<Double> matrix, double d) { for (int x = 0; x < matrix.dim1; x++) for (int y = 0; y < matrix.dim2; y++) matrix.data[x * matrix.dim2 + y] = (Double)matrix.data[x * matrix.dim2 + y] * d; } /** * Compute the Frobenius norm (square root of the sum of squared entries) of a matrix. * See http://en.wikipedia.org/wiki/Matrix_norm * @param matrix the matrix * @return the Frobenius norm of the matrix */ public static double frobeniusNorm(Matrix<Double> matrix) { double squared_entry_sum = 0; for (int x = 0; x < matrix.dim1 * matrix.dim2; x++) squared_entry_sum += Math.pow((Double)matrix.data[x], 2); return Math.sqrt(squared_entry_sum); } /** * Compute the scalar product between a vector and a row of the matrix. * @param matrix the matrix * @param i the row ID * @param vector the numeric vector * @return the scalar product of row i and the vector */ public static double rowScalarProduct(Matrix<Double> matrix, int i, List<Double> vector) { if (i >= matrix.dim1) throw new IllegalArgumentException("i too big: " + i + ", dim1 is " + matrix.dim1); if (vector.size() != matrix.dim2) throw new IllegalArgumentException("wrong vector size: " + vector.size() + ", dim2 is " + matrix.dim2); Double result = 0.0; for (int j = 0; j < matrix.dim2; j++) result += (Double)(matrix.data[i * matrix.dim2 + j]) * vector.get(j); return result; } /** * Compute the scalar product between two rows of two matrices. * @param matrix1 the first matrix * @param i the first row ID * @param matrix2 the second matrix * @param j the second row ID * @return the scalar product of row i of matrix1 and row j of matrix2 */ public static Double rowScalarProduct(Matrix<Double> matrix1, int i, Matrix<Double> matrix2, int j) { if (i >= matrix1.dim1) throw new IllegalArgumentException("i too big: " + i + ", dim1 is " + matrix1.dim1); if (j >= matrix2.dim1) throw new IllegalArgumentException("j too big: " + j + ", dim1 is " + matrix2.dim1); if (matrix1.dim2 != matrix2.dim2) throw new IllegalArgumentException("wrong row size: " + matrix1.dim2 + " vs. " + matrix2.dim2); Double result = 0.0; for (int c = 0; c < matrix1.dim2; c++) result += (Double)(matrix1.data[i * matrix1.dim2 + c]) * (Double)(matrix2.data[j * matrix2.dim2 + c]); return result; } /** * Compute the difference vector between two rows of two matrices. * @param matrix1 the first matrix * @param i the first row ID * @param matrix2 the second matrix * @param j the second row ID * @return the difference vector of row i of matrix1 and row j of matrix2 */ public static List<Double> rowDifference(Matrix<Double> matrix1, int i, Matrix<Double> matrix2, int j) { if (i >= matrix1.dim1) throw new IllegalArgumentException("i too big: " + i + ", dim1 is " + matrix1.dim1); if (j >= matrix2.dim1) throw new IllegalArgumentException("wrong row size: " + matrix1.dim2 + " vs. " + matrix2.dim2); List<Double> result = new ArrayList<Double>(matrix1.dim2); for (int c = 0; c < matrix1.dim2; c++) result.set(c, (Double)matrix1.data[i * matrix1.dim2 + c] - (Double)matrix2.data[j * matrix2.dim2 + c]); return result; } /** * Compute the scalar product of a matrix row with the difference vector of two other matrix rows. * @param matrix1 the first matrix * @param i the first row ID * @param matrix2 the second matrix * @param j the second row ID * @param matrix3 the third matrix * @param k the third row ID * @return see summary */ public static double rowScalarProductWithRowDifference(Matrix<Double> matrix1, int i, Matrix<Double> matrix2, int j, Matrix<Double> matrix3, int k) { if (i >= matrix1.dim1) throw new IllegalArgumentException("i too big: " + i + ", dim1 is " + matrix1.dim1); if (j >= matrix2.dim1) throw new IllegalArgumentException("j too big: " + j + ", dim1 is " + matrix2.dim1); if (j >= matrix3.dim1) throw new IllegalArgumentException("j too big: " + k + ", dim1 is " + matrix3.dim1); if (matrix1.dim2 != matrix2.dim2) throw new IllegalArgumentException("wrong row size: " + matrix1.dim2 + " vs. " + matrix2.dim2); if (matrix1.dim2 != matrix3.dim2) throw new IllegalArgumentException("wrong row size: " + matrix1.dim2 + " vs. " + matrix3.dim2); double result = 0.0; for (int c = 0; c < matrix1.dim2; c++) result += (Double) matrix1.data[i * matrix1.dim2 + c] * ((Double)matrix2.data[j * matrix2.dim2 + c] - (Double)matrix3.data[k * matrix3.dim2 + c]); return result; } /** * Return the maximum value contained in a matrix. * @param m the matrix */ public static int maxInteger(Matrix<Integer> m) { int max = Integer.MIN_VALUE; for(Object o : m.data) max = Math.max(max, (Integer) o); return max; } /** * Return the maximum value contained in a matrix. * @param m the matrix */ public static double maxDouble(Matrix<Double> m) { double max = Double.MIN_VALUE; for(Object o : m.data) max = Math.max(max, (Double) o); return max; } /** * return the maximum value contained in a matrix. * @param m the matrix */ public static float maxFloat(Matrix<Float> m) { float max = Float.MIN_VALUE; for(Object o : m.data) max = Math.max(max, (Float) o); return max; } }