package com.compomics.util.math.matrix;
import java.util.ArrayList;
/**
* Implementation of a matrix for double objects. Warning: all indexes start
* from 0.
*
* @author Marc Vaudel
*/
public class DoubleMatrix {
// @TODO: add javadoc
private int nLines = 0;
private ArrayList<ArrayList<Double>> content;
public DoubleMatrix() {
content = new ArrayList<ArrayList<Double>>();
}
public DoubleMatrix(int nColumns) {
content = new ArrayList<ArrayList<Double>>(nColumns);
}
public DoubleMatrix(DoubleMatrix matrix) {
nLines = matrix.getNLines();
for (ArrayList<Double> column : matrix.getColumns()) {
ArrayList<Double> newColumn = new ArrayList<Double>(column);
content.add(newColumn);
}
}
public void addColumn(ArrayList<Double> column) {
if (column == null) {
throw new IllegalArgumentException("Attempting to add null column to matrix.");
}
int columnsize = column.size();
if (columnsize == 0) {
throw new IllegalArgumentException("Attempting to add empty column to matrix");
}
if (nLines == 0) {
nLines = columnsize;
} else if (columnsize != nLines) {
throw new IllegalArgumentException("Impossible to add column of length " + column.size() + " in matrix of length " + nLines + ".");
}
content.add(column);
}
public void addLine(ArrayList<Double> line) {
if (line == null) {
throw new IllegalArgumentException("Attempting to add null line to matrix.");
}
int lineSize = line.size();
if (lineSize != getNColumns()) {
throw new IllegalArgumentException("Impossible to add line of length " + lineSize + " in matrix of width " + getNColumns() + ".");
}
for (int i = 0; i < getNColumns(); i++) {
ArrayList<Double> column = content.get(i);
column.add(line.get(i));
}
nLines++;
}
public void setLine(int lineIndex, ArrayList<Double> line) {
if (line == null) {
throw new IllegalArgumentException("Attempting to add null line to matrix.");
}
int lineSize = line.size();
if (lineSize != getNColumns()) {
throw new IllegalArgumentException("Impossible to add line of length " + lineSize + " in matrix of width " + getNColumns() + ".");
}
if (lineIndex >= nLines) {
throw new IllegalArgumentException("Impossible to add line at index " + lineIndex + " in matrix of length " + nLines + ".");
}
for (int i = 0; i < getNColumns(); i++) {
ArrayList<Double> column = content.get(i);
column.set(lineIndex, line.get(i));
}
}
public void setColumn(int columnIndex, ArrayList<Double> column) {
if (column == null) {
throw new IllegalArgumentException("Attempting to add null column to matrix.");
}
int columnSize = column.size();
if (columnSize != getNLines()) {
throw new IllegalArgumentException("Impossible to add line of length " + columnSize + " in matrix of width " + getNLines() + ".");
}
if (columnIndex >= getNColumns()) {
throw new IllegalArgumentException("Impossible to add line at index " + columnIndex + " in matrix of length " + getNColumns() + ".");
}
content.set(columnIndex, column);
}
public int getNColumns() {
return content.size();
}
public int getNLines() {
return nLines;
}
public boolean isSquare() {
return getNLines() == getNColumns();
}
public ArrayList<Double> getColumn(int columnIndex) {
return content.get(columnIndex);
}
public ArrayList<ArrayList<Double>> getColumns() {
return new ArrayList<ArrayList<Double>>(content);
}
public ArrayList<ArrayList<Double>> getLines() {
ArrayList<ArrayList<Double>> lines = new ArrayList<ArrayList<Double>>(nLines);
for (int i = 0; i < nLines; i++) {
lines.add(getLine(i));
}
return lines;
}
public ArrayList<Double> getLine(int lineIndex) {
if (lineIndex < 0 || lineIndex >= nLines) {
throw new IllegalArgumentException("Invalid index " + lineIndex + " for matrix of size " + nLines + ".");
}
ArrayList<Double> result = new ArrayList<Double>(nLines);
for (ArrayList<Double> column : content) {
result.add(column.get(lineIndex));
}
return result;
}
/**
* Returns the value of the matrix at given indexes. 0 is the first index.
*
* @param lineIndex the line index
* @param columnIndex the column index
* @return the value of the matrix at given indexes, 0 is the first index
*/
public Double getValueAt(int lineIndex, int columnIndex) {
return content.get(columnIndex).get(lineIndex);
}
public void setValueAt(int lineIndex, int columnIndex, Double value) {
if (columnIndex >= content.size()) {
throw new IllegalArgumentException("Column index " + columnIndex + " larger than matrix capacity " + content.size() + ".");
}
if (lineIndex >= nLines) {
throw new IllegalArgumentException("Line index " + columnIndex + " larger than matrix capacity " + nLines + ".");
}
content.get(columnIndex).set(lineIndex, value);
}
public static DoubleMatrix transpose(DoubleMatrix matrix) {
int nLines = matrix.getNLines();
DoubleMatrix transposedMatrix = new DoubleMatrix(nLines);
for (int i = 0; i < nLines; i++) {
transposedMatrix.addColumn(matrix.getLine(i));
}
return transposedMatrix;
}
public boolean equals(DoubleMatrix anotherMatrix) {
if (anotherMatrix.getNColumns() != getNColumns() || anotherMatrix.getNLines() != getNLines()) {
return false;
}
for (int i = 0; i < getNLines(); i++) {
for (int j = 0; j < getNColumns(); j++) {
if (!getValueAt(i, j).equals(anotherMatrix.getValueAt(i, j))) {
return false;
}
}
}
return true;
}
public static DoubleMatrix getIdentityMatrix(int n) {
DoubleMatrix identityMatrix = new DoubleMatrix(n);
for (int i = 0; i < n; i++) {
ArrayList<Double> column = new ArrayList<Double>(n);
for (int j = 0; j < n; j++) {
if (i == j) {
column.add(1.0);
} else {
column.add(0.0);
}
}
identityMatrix.addColumn(column);
}
return identityMatrix;
}
public double getTrace() {
if (!isSquare()) {
throw new IllegalArgumentException("Attempting to estimate the trace on a non-square matrix (" + getNLines() + " lines, " + getNColumns() + " columns).");
}
double result = 0;
for (int i = 0; i < nLines; i++) {
result += getValueAt(i, i);
}
return result;
}
public DoubleMatrix getSubMatrix(int lineStart, int lineStop, int columnStart, int columnStop) {
if (lineStop < lineStart) {
throw new IllegalArgumentException("End line index smaller than start index.");
}
if (columnStop < columnStart) {
throw new IllegalArgumentException("End column index smaller than start index.");
}
if (columnStop + 1 == columnStart) {
return new DoubleMatrix();
}
DoubleMatrix subMatrix = new DoubleMatrix(columnStop + 1 - columnStart);
for (int i = columnStart; i <= columnStop; i++) {
ArrayList<Double> column = getColumn(i);
ArrayList<Double> subColumn = new ArrayList<Double>(column.subList(lineStart, lineStop + 1));
subMatrix.addColumn(subColumn);
}
return subMatrix;
}
public void appendColumns(DoubleMatrix columns) {
if (columns.getNLines() != getNLines()) {
throw new IllegalArgumentException("Attempting to appennd columns with different number of lines.");
}
for (ArrayList<Double> column : columns.getColumns()) {
ArrayList<Double> newColumn = new ArrayList<Double>(column);
addColumn(newColumn);
}
}
public void appendLines(DoubleMatrix lines) {
if (lines.getNColumns() != getNColumns()) {
throw new IllegalArgumentException("Attempting to appennd lines with different number of columns.");
}
for (ArrayList<Double> line : lines.getLines()) {
ArrayList<Double> newLine = new ArrayList<Double>(line);
addLine(newLine);
}
}
public double getDeterminant() {
if (!isSquare()) {
throw new IllegalArgumentException("Attempting to estimate the determinant on a non-square matrix (" + getNLines() + " lines, " + getNColumns() + " columns).");
}
if (nLines == 0) {
throw new IllegalArgumentException("Attempting to estimate the determinant on an empty matrix.");
}
if (nLines == 1) {
return getValueAt(0, 0);
} else if (nLines == 2) {
return getValueAt(0, 0) * getValueAt(1, 1) - getValueAt(0, 1) * getValueAt(1, 0);
} else {
double determinant = 0;
for (int i = 0; i < getNLines(); i++) {
int line = i + 1;
System.out.println(line + " in " + nLines);
DoubleMatrix subMatrix;
if (i == nLines - 1) {
subMatrix = getSubMatrix(0, nLines - 2, 1, nLines - 1);
} else {
subMatrix = getSubMatrix(i + 1, nLines - 1, 1, nLines - 1);
if (i > 0) {
DoubleMatrix topMatrix = getSubMatrix(0, i - 1, 1, nLines - 1);
subMatrix.appendLines(topMatrix);
}
}
determinant += Math.pow(-1, i) * getValueAt(i, 0) * subMatrix.getDeterminant();
}
return determinant;
}
}
/**
* Returns a score based on the non-diagonal values. Score is 0 for
* identity, 1 for the (1) matrix.
*
* @return a score based on the non-diagonal values
*/
public double getNonDiagonalScore() {
if (!isSquare()) {
throw new IllegalArgumentException("The non diagonal score can only be computed on square matrices (" + getNLines() + " lines, " + getNColumns() + " columns).");
}
if (nLines < 2) {
return 0;
}
double score = 0;
for (int i = 0; i < nLines; i++) {
for (int j = 0; j < nLines; j++) {
if (i != j) {
int distance = Math.abs(i - j);
int nValues = nLines - distance;
score += getValueAt(i, j) / nValues;
}
}
}
return score / nLines;
}
public void linePermutation(int line1, int line2) {
ArrayList<Double> tempLine = getLine(line2);
setLine(line2, getLine(line1));
setLine(line1, tempLine);
}
public void columnPermutation(int column1, int column2) {
ArrayList<Double> tempColumn = getColumn(column2);
setColumn(column2, getColumn(column1));
setColumn(column1, tempColumn);
}
}