package mikera.matrixx.decompose.impl.lu;
import mikera.matrixx.AMatrix;
import mikera.matrixx.Matrix;
import mikera.matrixx.Matrixx;
import mikera.vectorz.util.ErrorMessages;
public class DecomposeLUP {
public static Matrix createLUPInverse(AMatrix m) {
if (!m.isSquare()) { throw new IllegalArgumentException(
"Matrix must be square for inverse!"); }
int dims = m.rowCount();
Matrix am = new Matrix(m);
int[] rowPermutations = new int[dims];
// perform LU-based inverse on matrix
DecomposeLUP.decomposeLU(am, rowPermutations);
return DecomposeLUP.backSubstituteLU(am, rowPermutations);
}
/**
* Computes LU decomposition of a matrix, returns true if successful (i.e.
* if matrix is non-singular)
*/
private static void decomposeLU(Matrix am, int[] permutations) {
int dims = permutations.length;
double[] data = am.data;
double rowFactors[] = new double[dims];
calcRowFactors(data, rowFactors);
for (int col = 0; col < dims; col++) {
// Scan upper diagonal matrix
for (int row = 0; row < col; row++) {
int dataIndex = (dims * row) + col;
double acc = data[dataIndex];
for (int i = 0; i < row; i++) {
acc -= data[(dims * row) + i] * data[(dims * i) + col];
}
data[dataIndex] = acc;
}
// Find index of largest pivot
int maxIndex = 0;
double maxValue = Double.NEGATIVE_INFINITY;
for (int row = col; row < dims; row++) {
int dataIndex = (dims * row) + col;
double acc = data[dataIndex];
for (int i = 0; i < col; i++) {
acc -= data[(dims * row) + i] * data[(dims * i) + col];
}
data[dataIndex] = acc;
double value = rowFactors[row] * Math.abs(acc);
if (value > maxValue) {
maxValue = value;
maxIndex = row;
}
}
if (col != maxIndex) {
am.swapRows(col, maxIndex);
rowFactors[maxIndex] = rowFactors[col];
}
permutations[col] = maxIndex;
if (data[(dims * col) + col] == 0.0) { throw new IllegalArgumentException(
ErrorMessages.singularMatrix()); }
// Scale lower diagonal matrix using values on diagonal
double diagonalValue = data[(dims * col) + col];
double factor = 1.0 / diagonalValue;
int offset = dims * (col + 1) + col;
for (int i = 0; i < ((dims - 1) - col); i++) {
data[(dims * i) + offset] *= factor;
}
}
}
/**
* Utility function to calculate scale factors for each row
*/
private static void calcRowFactors(double[] data, double[] factorsOut) {
int dims = factorsOut.length;
for (int row = 0; row < dims; row++) {
double maxValue = 0.0;
// find maximum value in the row
for (int col = 0; col < dims; col++) {
maxValue = Math.max(maxValue, Math.abs(data[row * dims + col]));
}
if (maxValue == 0.0) { throw new IllegalArgumentException(
ErrorMessages.singularMatrix()); }
// scale factor for row should reduce maximum absolute value to 1.0
factorsOut[row] = 1.0 / maxValue;
}
}
private static Matrix backSubstituteLU(Matrix am, int[] permutations) {
int dims = permutations.length;
double[] dataIn = am.data;
// create identity matrix in output
Matrix result = new Matrix(Matrixx.createImmutableIdentityMatrix(dims));
double[] dataOut = result.data;
for (int col = 0; col < dims; col++) {
int rowIndex = -1;
// Forward substitution phase
for (int row = 0; row < dims; row++) {
int pRow = permutations[row];
double acc = dataOut[(dims * pRow) + col];
dataOut[(dims * pRow) + col] = dataOut[(dims * row) + col];
if (rowIndex >= 0) {
for (int i = rowIndex; i <= row - 1; i++) {
acc -= dataIn[(row * dims) + i]
* dataOut[(dims * i) + col];
}
} else if (acc != 0.0) {
rowIndex = row;
}
dataOut[(dims * row) + col] = acc;
}
// Back substitution phase
for (int row = 0; row < dims; row++) {
int irow = (dims - 1 - row);
int offset = dims * irow;
double total = 0.0;
for (int i = 0; i < row; i++) {
total += dataIn[offset + ((dims - 1) - i)]
* dataOut[(dims * ((dims - 1) - i)) + col];
}
double diagonalValue = dataIn[offset + irow];
dataOut[(dims * irow) + col] = (dataOut[(dims * irow) + col] - total)
/ diagonalValue;
}
}
return result;
}
}