/*
* Encog(tm) Core v3.3 - Java Version
* http://www.heatonresearch.com/encog/
* https://github.com/encog/encog-java-core
* Copyright 2008-2014 Heaton Research, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For more information on Heaton Research copyrights, licenses
* and trademarks visit:
* http://www.heatonresearch.com/copyright
*/
package org.encog.mathutil.matrices;
import org.encog.mathutil.matrices.decomposition.LUDecomposition;
/**
* This class can perform many different mathematical operations on matrixes.
* The matrixes passed in will not be modified, rather a new matrix, with the
* operation performed, will be returned.
*/
public final class MatrixMath {
/**
* Add two matrixes.
*
* @param a
* The first matrix to add.
* @param b
* The second matrix to add.
* @return A new matrix of the two added.
*/
public static Matrix add(final Matrix a, final Matrix b) {
if (a.getRows() != b.getRows()) {
throw new MatrixError(
"To add the matrices they must have the same number of "
+ "rows and columns. Matrix a has " + a.getRows()
+ " rows and matrix b has " + b.getRows()
+ " rows.");
}
if (a.getCols() != b.getCols()) {
throw new MatrixError(
"To add the matrices they must have the same number "
+ "of rows and columns. Matrix a has "
+ a.getCols() + " cols and matrix b has "
+ b.getCols() + " cols.");
}
final double[][] aa = a.getData();
final double[][] bb = b.getData();
final double[][] result = new double[a.getRows()][a.getCols()];
for (int resultRow = 0; resultRow < a.getRows(); resultRow++) {
for (int resultCol = 0; resultCol < a.getCols(); resultCol++) {
result[resultRow][resultCol] = aa[resultRow][resultCol]
+ bb[resultRow][resultCol];
}
}
return new Matrix(result);
}
/**
* Copy from one matrix to another.
*
* @param source
* The source matrix for the copy.
* @param target
* The target matrix for the copy.
*/
public static void copy(final Matrix source, final Matrix target) {
final double[][] s = source.getData();
final double[][] t = target.getData();
for (int row = 0; row < source.getRows(); row++) {
for (int col = 0; col < source.getCols(); col++) {
t[row][col] = s[row][col];
}
}
}
/**
* Delete one column from the matrix. Does not actually touch the source
* matrix, rather a new matrix with the column deleted is returned.
*
* @param matrix
* The matrix.
* @param deleted
* The column to delete.
* @return A matrix with the column deleted.
*/
public static Matrix deleteCol(final Matrix matrix, final int deleted) {
if (deleted >= matrix.getCols()) {
throw new MatrixError("Can't delete column " + deleted
+ " from matrix, it only has " + matrix.getCols()
+ " columns.");
}
final double[][] newMatrix = new double[matrix.getRows()][matrix
.getCols() - 1];
final double[][] d = matrix.getData();
for (int row = 0; row < matrix.getRows(); row++) {
int targetCol = 0;
for (int col = 0; col < matrix.getCols(); col++) {
if (col != deleted) {
newMatrix[row][targetCol] = d[row][col];
targetCol++;
}
}
}
return new Matrix(newMatrix);
}
/**
* Delete a row from the matrix. Does not actually touch the matrix, rather
* returns a new matrix.
*
* @param matrix
* The matrix.
* @param deleted
* Which row to delete.
* @return A new matrix with the specified row deleted.
*/
public static Matrix deleteRow(final Matrix matrix, final int deleted) {
if (deleted >= matrix.getRows()) {
throw new MatrixError("Can't delete row " + deleted
+ " from matrix, it only has " + matrix.getRows()
+ " rows.");
}
final double[][] newMatrix = new double[matrix.getRows() - 1][matrix
.getCols()];
final double[][] d = matrix.getData();
int targetRow = 0;
for (int row = 0; row < matrix.getRows(); row++) {
if (row != deleted) {
for (int col = 0; col < matrix.getCols(); col++) {
newMatrix[targetRow][col] = d[row][col];
}
targetRow++;
}
}
return new Matrix(newMatrix);
}
/**
* Return a matrix with each cell divided by the specified value.
*
* @param a
* The matrix to divide.
* @param b
* The value to divide by.
* @return A new matrix with the division performed.
*/
public static Matrix divide(final Matrix a, final double b) {
final double[][] result = new double[a.getRows()][a.getCols()];
final double[][] d = a.getData();
for (int row = 0; row < a.getRows(); row++) {
for (int col = 0; col < a.getCols(); col++) {
result[row][col] = d[row][col] / b;
}
}
return new Matrix(result);
}
/**
* Compute the dot product for the two matrixes. To compute the dot product,
* both
*
* @param a
* The first matrix.
* @param b
* The second matrix.
* @return The dot product.
*/
public static double dotProduct(final Matrix a, final Matrix b) {
if (!a.isVector() || !b.isVector()) {
throw new MatrixError("To take the dot product, both matrices must be vectors.");
}
final double[][] aArray = a.getData();
final double[][] bArray = b.getData();
final int aLength = aArray.length == 1 ? aArray[0].length : aArray.length;
final int bLength = bArray.length == 1 ? bArray[0].length : bArray.length;
if (aLength != bLength) {
throw new MatrixError("To take the dot product, both matrices must be of the same length.");
}
double result = 0;
if (aArray.length == 1 && bArray.length == 1) {
for (int i = 0; i < aLength; i++) {
result += aArray[0][i] * bArray[0][i];
}
}
else if (aArray.length == 1 && bArray[0].length == 1) {
for (int i = 0; i < aLength; i++) {
result += aArray[0][i] * bArray[i][0];
}
}
else if (aArray[0].length == 1 && bArray.length == 1) {
for (int i = 0; i < aLength; i++) {
result += aArray[i][0] * bArray[0][i];
}
}
else if (aArray[0].length == 1 && bArray[0].length == 1) {
for (int i = 0; i < aLength; i++) {
result += aArray[i][0] * bArray[i][0];
}
}
return result;
}
/**
* Return an identity matrix of the specified size.
*
* @param size
* The number of rows and columns to create. An identity matrix
* is always square.
* @return An identity matrix.
*/
public static Matrix identity(final int size) {
if (size < 1) {
throw new MatrixError("Identity matrix must be at least of "
+ "size 1.");
}
final Matrix result = new Matrix(size, size);
final double[][] d = result.getData();
for (int i = 0; i < size; i++) {
d[i][i] = 1;
}
return result;
}
/**
* Return the result of multiplying every cell in the matrix by the
* specified value.
*
* @param a
* The first matrix.
* @param b
* The second matrix.
* @return The result of the multiplication.
*/
public static Matrix multiply(final Matrix a, final double b) {
final double[][] result = new double[a.getRows()][a.getCols()];
final double[][] d = a.getData();
for (int row = 0; row < a.getRows(); row++) {
for (int col = 0; col < a.getCols(); col++) {
result[row][col] = d[row][col] * b;
}
}
return new Matrix(result);
}
/**
* Return the product of the first and second matrix.
*
* @param a
* The first matrix.
* @param b
* The second matrix.
* @return The result of the multiplication.
*/
public static Matrix multiply(final Matrix a, final Matrix b) {
if (b.getRows() != a.getCols()) {
throw new MatrixError(
"To use ordinary matrix multiplication the number of "
+ "columns on the first matrix must mat the number of "
+ "rows on the second.");
}
final double[][] aData = a.getData();
final double[][] bData = b.getData();
final Matrix x = new Matrix(a.getRows(), b.getCols());
final double[][] c = x.getData();
final double[] bcolj = new double[a.getCols()];
for (int j = 0; j < b.getCols(); j++) {
for (int k = 0; k < a.getCols(); k++) {
bcolj[k] = bData[k][j];
}
for (int i = 0; i < a.getRows(); i++) {
final double[] arowi = aData[i];
double s = 0;
for (int k = 0; k < a.getCols(); k++) {
s += arowi[k] * bcolj[k];
}
c[i][j] = s;
}
}
return x;
}
/**
* Return the results of subtracting one matrix from another.
*
* @param a
* The first matrix.
* @param b
* The second matrix.
* @return The results of the subtraction.
*/
public static Matrix subtract(final Matrix a, final Matrix b) {
if (a.getRows() != b.getRows()) {
throw new MatrixError(
"To subtract the matrices they must have the same "
+ "number of rows and columns. Matrix a has "
+ a.getRows() + " rows and matrix b has "
+ b.getRows() + " rows.");
}
if (a.getCols() != b.getCols()) {
throw new MatrixError(
"To subtract the matrices they must have the same "
+ "number of rows and columns. Matrix a has "
+ a.getCols() + " cols and matrix b has "
+ b.getCols() + " cols.");
}
final double[][] result = new double[a.getRows()][a.getCols()];
final double[][] aa = a.getData();
final double[][] bb = b.getData();
for (int resultRow = 0; resultRow < a.getRows(); resultRow++) {
for (int resultCol = 0; resultCol < a.getCols(); resultCol++) {
result[resultRow][resultCol] = aa[resultRow][resultCol]
- bb[resultRow][resultCol];
}
}
return new Matrix(result);
}
/**
* Return the transposition of a matrix.
*
* @param input
* The matrix to transpose.
* @return The matrix transposed.
*/
public static Matrix transpose(final Matrix input) {
final double[][] transposeMatrix = new double[input.getCols()][input
.getRows()];
final double[][] d = input.getData();
for (int r = 0; r < input.getRows(); r++) {
for (int c = 0; c < input.getCols(); c++) {
transposeMatrix[c][r] = d[r][c];
}
}
return new Matrix(transposeMatrix);
}
/**
* Calculate the length of a vector.
*
* @param input
* The matrix to calculate the length of.
*
* @return Vector length.
*/
public static double vectorLength(final Matrix input) {
if (!input.isVector()) {
throw new MatrixError(
"Can only take the vector length of a vector.");
}
final double[] v = input.toPackedArray();
double rtn = 0.0;
for (final Double element : v) {
rtn += Math.pow(element, 2);
}
return Math.sqrt(rtn);
}
/**
* A private constructor.
*/
private MatrixMath() {
}
public static double determinant(Matrix m) {
return new LUDecomposition(m).det();
}
public static double[] multiply(Matrix a, double[] d) {
double[] p = new double[a.getRows()];
double[][] aData = a.getData();
for (int r = 0; r < a.getRows(); r++)
for (int i = 0; i < a.getCols(); i++)
p[r] += aData[r][i] * d[i];
return p;
}
}