package de.skuzzle.polly.tools.math;
import de.skuzzle.polly.tools.math.Matrix.DimensionException;
/**
* This class provides several static methods for operations on {@link Matrix matrices}.
*
* @author Simon
*/
public class MatrixUtils {
/**
* Calculates the determinant of quadratic matrices. This is done by creating an
* internal copy and transforming it into an echelon form. The determinant is then
* calculated by building the product of the diagonal elements.
*
* @param matrix The matrix to calculate the determinant from.
* @return The determinant of the matrix.
* @throws DimensionException if the matrix is not quadratic.
*/
public static <K> K getDeterminant(Matrix<K> matrix) {
if (matrix.getM() != matrix.getN()) {
throw new DimensionException("illegal dimension");
}
Matrix<K> copy = new Matrix<K>(matrix);
K product = matrix.getField().getMultiplicativeNeutral();
toEchelon(copy);
for (int i = 0; i < matrix.getM(); ++i) {
product = matrix.getField().multiply(product, copy.get(i, i));
}
return product;
}
/**
* Determines whether the given matrix is invertible. This is the case if the
* determinant of the matrix is zero. For all non-quadratic matrices, this method
* will instantly return <code>false</code>. All other matrices are considered
* invertible if their determinants is different from zero.
*
* @param matrix The matrix to check.
* @return Whether the matrix is invertible.
*/
public static <K> boolean isInvertible(Matrix<K> matrix) {
if (matrix.getM() != matrix.getN()) {
return false;
}
K k = getDeterminant(matrix);
return !k.equals(matrix.getField().getAdditiveNeutral());
}
/**
* Checks whether the given matrix is the identical matrix. That is, the matrix must
* be quadratic and contain only zeros except for the diagonal elements which must be
* the one element of the underlying field. More specific, this method returns
* <code>true</code> if and only if the {@code matrix.getLeftId().equals(matrix}.
*
* @param matrix The matrix to check.
* @return Whether the given matrix is the identical matrix.
*/
public static <K> boolean isIdentity(Matrix<K> matrix) {
if (matrix.getN() != matrix.getM()) {
return false;
}
return matrix.getLeftId().equals(matrix);
}
public static void main(String[] args) {
Integer[][] m = {
{1, 2, 3},
{2, 3, 4},
{4, 5, 6}
};
Matrix<Integer> matrix = new Matrix<Integer>(m, Fields.integerModulo(7));
System.out.println(toGaussForm(matrix));
}
/**
* Tries to calculate the inverted matrix to a given matrix.
*
* @param matrix The matrix to invert.
* @return An inverted matrix if possible.
* @throws DimensionException if the given matrix is not quadratic.
*/
public static <K> Matrix<K> invert(Matrix<K> matrix) {
if (matrix.getN() != matrix.getM()) {
throw new DimensionException("illegal dimension");
}
Matrix<K> result = Matrix.merge(matrix, matrix.getLeftId());
toGaussForm(result);
return Matrix.splitRight(result, matrix.getN());
}
/**
* Transforms the given matrix into gaussian normal form. This is done in place, thus
* the elements in the given matrix will be changed by this algorithm.
*
* The matrix will be transformed into the following block form:<br/>
* {@code /E C\} <br/>
* {@code \0 0/} <br/>
*
* Where E is the identical matrix.
*
* @param matrix The matrix to transform.
* @return The same matrix instance as given by the parameter.
*/
public static <K> Matrix<K> toGaussForm(Matrix<K> matrix) {
if (matrix.isZero()) {
return matrix;
}
Field<K> f = matrix.getField();
// first step: produce echolon form
toEchelon(matrix);
// second step: produce a one in each row on the diagonal. As by step 1, below
// the diagonal only exists zeros. So the first element != 0 from the left on
// each row is the diagonal entry.
//
// algorithm:
// * for each row 'i'
// * find the first column 'j' that contains no zero
// * multiply the whole row with the additive inverse of the element at
// A[i, j]
for (int i = 0; i < matrix.getM(); ++i) {
K pivot = matrix.get(i, i);
K invPivot = f.getMultiplicativeInverse(pivot);
matrix.multiplyRowWith(invPivot, i);
}
// third step: produce zeros above the diagonal of ones
// algorithm:
// * start at the last row 'i'
// * find the first column 'j' that contains no zero (as by step 2, it must
// then contain a one)
// * if row consists of only zeros, continue with next row.
// * add k times row 'i' to all rows 'i1' above i with
// k = A[i, j] * A[i1, j]^-1 to produce a zero in A[i1, j]
for (int i = matrix.getM() - 1; i > 0; --i) {
for (int i1 = 0; i1 < i; ++i1) {
K a_i1 = matrix.get(i1, i);
K factor = f.getAdditiveInverse(a_i1);
matrix.addMultipleToRow(factor, i, i1);
}
}
return matrix;
}
/**
* <p>Creates an echolon form of the given matrix. That is, each element below the
* main diagonal of the matrix will be zero afterwards.</p>
*
* <p>This operation is done in place and thus changes the elements of the given
* matrix.</p>
*
* @param matrix The matrix to transform into echolon form.
* @return The same matrix instance as given by the parameter.
*/
public static <K> Matrix<K> toEchelon(Matrix<K> matrix) {
Field<K> f = matrix.getField();
// produce zeros below the diagonal
for (int i = 0; i < matrix.getM() - 1; ++i) {
for (int j = i + 1; j < matrix.getM(); ++j) {
K a_i = matrix.get(i, i);
K a_j = matrix.get(j, i);
K scalar = f.getAdditiveInverse(a_j);
scalar = f.multiply(scalar, f.getMultiplicativeInverse(a_i));
matrix.addMultipleToRow(scalar, i, j);
}
}
return matrix;
}
/**
* Calculates the rank of the given matrix by transforming a copy of it into
* gauss form and then counting the size {@code k} of the identity matrix in the
* upper left corner of the gaussian matrix.
*
* @param matrix The matrix which rank should be calculated.
* @return The rank of the matrix.
*/
public static <K> int rank(Matrix<K> matrix) {
K one = matrix.getField().getMultiplicativeNeutral();
Matrix<K> copy = new Matrix<K>(matrix);
toGaussForm(copy);
int i = 0;
for(; i < copy.getM() && copy.get(i, i).equals(one); ++i);
return i;
}
/*private static <K> Matrix<K> addToRow(
K scalar, int srcRow, int destRow, Matrix<K> matrix) {
// create a matrix to multiply with
Matrix<K> id = matrix.getRightId().set(destRow, srcRow, scalar);
return id.multiplyWith(matrix);
}
private static <K> Matrix<K> switchRows(int srcRow, int destRow, Matrix<K> matrix) {
K zero = matrix.getField().getAdditiveNeutral();
K one = matrix.getField().getMultiplicativeNeutral();
Matrix<K> id = matrix.getLeftId();
id.set(srcRow, srcRow, zero);
id.set(destRow, srcRow, one);
id.set(destRow, destRow, zero);
id.set(srcRow, destRow, one);
return id.multiplyWith(matrix);
} */
}