package org.matheclipse.commons.math.linear;
import org.apache.commons.math3.Field;
import org.apache.commons.math3.FieldElement;
import org.apache.commons.math3.linear.FieldMatrix;
/**
* Matrix class that wraps a FieldMatrix<T> matrix, which is transformed to reduced row echelon format.
*
* See: Wikipedia - Row echelon form.
*
* The code was adapted from: Rosetta Code - Reduced row echelon
* form
*/
public class FieldReducedRowEchelonForm> {
private static class RowColIndex {
int row;
int col;
RowColIndex(int r, int c) {
row = r;
col = c;
}
public String toString() {
return "(" + row + ", " + col + ")";
}
}
private final FieldMatrix rowReducedMatrix;
private FieldMatrix nullSpaceCache;
private int matrixRankCache;
private final T zero;
private final T one;
private final int numRows;
private final int numCols;
/**
* Constructor which creates row reduced echelon matrix from the given FieldMatrix<T> matrix.
*
* @param matrix
* matrix which will be transformed to a row reduced echelon matrix.
*
* @see #rowReduce()
*/
public FieldReducedRowEchelonForm(FieldMatrix matrix) {
this.rowReducedMatrix = matrix.copy();
this.numRows = matrix.getRowDimension();
this.numCols = matrix.getColumnDimension();
Field field = matrix.getField();
this.zero = field.getZero();
this.one = field.getOne();
this.matrixRankCache = -1;
this.nullSpaceCache = null;
rowReduce();
}
private RowColIndex findPivot(RowColIndex a) {
int first_row = a.row;
RowColIndex pivot = new RowColIndex(a.row, a.col);
RowColIndex current = new RowColIndex(a.row, a.col);
for (int i = a.row + 1; i < (numRows - first_row); i++) {
current.row = i;
if (rowReducedMatrix.getEntry(current.row, current.col).equals(one)) {
swapRow(current, a);
}
}
current.row = a.row;
for (int i = current.row; i < (numRows - first_row); i++) {
current.row = i;
if (!rowReducedMatrix.getEntry(current.row, current.col).equals(zero)) {
pivot.row = i;
break;
}
}
return pivot;
}
private T getCoordinate(RowColIndex a) {
return rowReducedMatrix.getEntry(a.row, a.col);
}
/**
* Get the row reduced echelon form of the matrix.
*
* See: Wikipedia - Row echelon form.
*
* @return
*/
public FieldMatrix getRowReducedMatrix() {
return rowReducedMatrix;
}
/**
* Swap the rows a.row and b.row in the matrix and swap the row values in the
* corresponding RowColIndex objects.
*
* @param a
* @param b
*/
private void swapRow(RowColIndex a, RowColIndex b) {
T[] temp = rowReducedMatrix.getRow(a.row);
rowReducedMatrix.setRow(a.row, rowReducedMatrix.getRow(b.row));
rowReducedMatrix.setRow(b.row, temp);
int t = a.row;
a.row = b.row;
b.row = t;
}
/**
* Test if the column a.col of the matrix contains only zero-elements starting with the a.row element.
*
* @param a
* @return
*/
private boolean isColumnZeroFromRow(RowColIndex a) {
for (int i = a.row; i < numRows; i++) {
if (!rowReducedMatrix.getEntry(i, a.col).equals(zero)) {
return false;
}
}
return true;
}
/**
* Test if the row a.row of the matrix contains only zero-elements.
*
* @param a
* @return
*/
private boolean isRowZeroes(int row) {
T[] temp = rowReducedMatrix.getRow(row);
for (int i = 0; i < numCols; i++) {
if (!temp[i].equals(zero)) {
return false;
}
}
return true;
}
/**
* Add the values of the row to.row to the product of the values of the row from.row * factor and
* assign the result values back to the row to.row.
*
* @param to
* @param from
* @param scalar
*/
private void multiplyAdd(RowColIndex to, RowColIndex from, T factor) {
T[] row = rowReducedMatrix.getRow(to.row);
T[] rowMultiplied = rowReducedMatrix.getRow(from.row);
for (int i = 0; i < numCols; i++) {
rowReducedMatrix.setEntry(to.row, i, row[i].add((rowMultiplied[i].multiply(factor))));
}
}
/**
* Get the nullspace of the row reduced matrix.
*
* See: Wikipedia - Kernel (linear algebra). Wikibooks - Null Spaces.
*
* @param minusOneFactor
* factor -1 for multiplying all elements of the free part of the reduced row echelon form matrix
* @return
*/
public FieldMatrix getNullSpace(T minusOneFactor) {
int rank = getMatrixRank();
int newRowDimension = rowReducedMatrix.getColumnDimension() - rank;
int newColumnDimension = rowReducedMatrix.getColumnDimension();
if (nullSpaceCache != null) {
return nullSpaceCache;
}
nullSpaceCache = rowReducedMatrix.createMatrix(newRowDimension, newColumnDimension);
getResultOfNullspace(minusOneFactor, rank);
return nullSpaceCache;
}
private void getResultOfNullspace(T minusOneFactor, int rank) {
// search free columns
boolean[] columns = new boolean[nullSpaceCache.getColumnDimension()];
int numberOfFreeColumns = 0;
for (int i = 0; i < rank; i++) {
if (!columns[i]) {
for (int k = i; k < rowReducedMatrix.getColumnDimension(); k++) {
if (rowReducedMatrix.getEntry(i, k).equals(zero)) {
columns[k] = true;
// free column
int offset = 0;
for (int j = 0; j < rank; j++) {
if (columns[j]) {
offset++;
}
nullSpaceCache.setEntry(numberOfFreeColumns, j + offset, rowReducedMatrix.getEntry(j, i));
}
numberOfFreeColumns++;
} else {
break;
}
}
}
}
// Let's take the rest of the 'free part' of the reduced row echelon form
int start = rank + numberOfFreeColumns;
int row = numberOfFreeColumns;
for (int i = start; i < nullSpaceCache.getColumnDimension(); i++) {
int offset = 0;
for (int j = 0; j < rank; j++) {
if (columns[j]) {
offset++;
}
nullSpaceCache.setEntry(row, j + offset, rowReducedMatrix.getEntry(j, i));
}
row++;
}
for (int i = start; i < nullSpaceCache.getColumnDimension(); i++) {
columns[i] = true;
}
// multiply matrix with scalar -1
nullSpaceCache = nullSpaceCache.scalarMultiply(minusOneFactor);
// append the 'one element' (typically as identity matrix)
row = 0;
for (int i = 0; i < columns.length; i++) {
if (columns[i]) {
nullSpaceCache.setEntry(row++, i, one);
}
}
}
/**
* Create the "reduced row echelon form" of a matrix.
*
* See: Wikipedia - Row echelon form.
*
* @return
*/
private FieldMatrix rowReduce() {
int maxRows = numRows;
RowColIndex pivot = new RowColIndex(0, 0);
int submatrix = 0;
for (int x = 0; x < numCols; x++) {
pivot = new RowColIndex(pivot.row, x);
// Step 1
// Begin with the leftmost nonzero column. This is a pivot column. The pivot position is at the top.
for (int i = x; i < numCols; i++) {
if (isColumnZeroFromRow(pivot) == false) {
break;
} else {
pivot.col = i;
}
}
// Step 2
// Select a nonzero entry in the pivot column with the highest absolute value as a pivot.
pivot = findPivot(pivot);
if (getCoordinate(pivot).equals(zero)) {
pivot.row++;
if (pivot.row >= maxRows) {
break;
}
continue;
}
// If necessary, interchange rows to move this entry into the pivot position.
// move this row to the top of the submatrix
if (pivot.row != submatrix) {
swapRow(new RowColIndex(submatrix, pivot.col), pivot);
}
// Force pivot to be 1
if (!getCoordinate(pivot).equals(one)) {
T scalar = getCoordinate(pivot).reciprocal();
scaleRow(pivot, scalar);
}
// Step 3
// Use row replacement operations to create zeroes in all positions below the pivot.
// belowPivot = belowPivot + (Pivot * -belowPivot)
for (int i = pivot.row; i < numRows; i++) {
if (i == pivot.row) {
continue;
}
RowColIndex belowPivot = new RowColIndex(i, pivot.col);
T complement = (getCoordinate(belowPivot).negate().divide(getCoordinate(pivot)));
multiplyAdd(belowPivot, pivot, complement);
}
// Step 5
// Beginning with the rightmost pivot and working upward and to the left, create zeroes above each pivot.
// If a pivot is not 1, make it 1 by a scaling operation.
// Use row replacement operations to create zeroes in all positions above the pivot
for (int i = pivot.row; i >= 0; i--) {
if (i == pivot.row) {
if (!getCoordinate(pivot).equals(one)) {
scaleRow(pivot, getCoordinate(pivot).reciprocal());
}
continue;
}
if (i == pivot.row) {
continue;
}
RowColIndex abovePivot = new RowColIndex(i, pivot.col);
T complement = (getCoordinate(abovePivot).negate().divide(getCoordinate(pivot)));
multiplyAdd(abovePivot, pivot, complement);
}
// Step 4
// Ignore the row containing the pivot position and cover all rows, if any, above it.
// Apply steps 1-3 to the remaining submatrix. Repeat until there are no more nonzero entries.
if ((pivot.row + 1) >= maxRows) { // || isRowZeroes(pivot.row + 1)) {
break;
}
submatrix++;
pivot.row++;
}
return rowReducedMatrix;
}
/**
* Get the rank of the row reduced matrix.
*
* See: Wikipedia - Rank (linear algebra).
*
* @return the rank of the matrix.
*/
public int getMatrixRank() {
if (rowReducedMatrix.getRowDimension() == 0 || rowReducedMatrix.getColumnDimension() == 0) {
return 0;
}
if (matrixRankCache < 0) {
matrixRankCache = 0;
int rows = rowReducedMatrix.getRowDimension() - 1;
for (int i = rows; i >= 0; i--) {
if (!isRowZeroes(i)) {
matrixRankCache = i + 1;
return matrixRankCache;
}
}
}
return matrixRankCache;
}
/**
* Multiply the x.row elements with the scalar factor.
*
* @param x
* @param d
*/
private void scaleRow(RowColIndex x, T factor) {
for (int i = 0; i < numCols; i++) {
rowReducedMatrix.multiplyEntry(x.row, i, factor);
}
}
}
Ads help maintain this website.