/*
* File name: MatrixBigDecimal.java (package eas.geometry.matrix.bigDecimal)
* Author(s): Lukas König, free internet code.
* Java version: 7.0
* Generation date: 15.02.2014
*
* (c) This file is protected by Creative Commons by-nc-sa license. Any
* altered or further developed versions of this file have to meet the
* agreements stated by the license conditions.
*
* In a nutshell
* -------------
* You are free:
* - to Share -- to copy, distribute and transmit the work
* - to Remix -- to adapt the work
*
* Under the following conditions:
* - Attribution -- You must attribute the work in the manner specified by the
* author or licensor (but not in any way that suggests that they endorse
* you or your use of the work).
* - Noncommercial -- You may not use this work for commercial purposes.
* - Share Alike -- If you alter, transform, or build upon this work, you may
* distribute the resulting work only under the same or a similar license to
* this one.
*
* + Detailed license conditions (Germany):
* http://creativecommons.org/licenses/by-nc-sa/3.0/de/
* + Detailed license conditions (unported):
* http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en
*
* This header must be placed in the beginning of any version of this file.
*/
package eas.math.matrix.bigDecimal;
import java.io.File;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.MathContext;
import java.util.ArrayList;
import java.util.Random;
import eas.math.matrix.bigDecimal.jama.JamaMatrixBigDecimal;
import eas.miscellaneous.StaticMethods;
import eas.miscellaneous.datatypes.Integer2D;
import eas.startSetup.ParCollection;
/**
* @author Lukas König, Thomas Darimont
*
* Matrix multiplication from Thomas Darimont:
* http://www.tutorials.de/forum/java/164941-n-x-n-matrizenmultiplikation-aber-wie.html
*/
public class MatrixBigDecimal implements Serializable {
private static final long serialVersionUID = 1700999592856415570L;
// public static final int GLOBAL_SCALE = NeuroTranslator.GENOME_LENGTH * 2 + 200;
public static int GLOBAL_SCALE = 800;
// public static int GLOBAL_SCALE = 20;
public static final int GLOBAL_ROUNDING_MODE = BigDecimal.ROUND_HALF_UP;
/**
* Die Felder der Matrix.
*/
private BigDecimal[][] matrix;
/**
* Zeilenüberschriften.
*/
private Integer2D[] zeilenUeberschr;
/**
* Spaltenüberschriften.
*/
private Integer2D[] spaltenUeberschr;
/* ************* Constructors ************* */
// /**
// * Generates matrix from text file.
// *
// * @param file The text file.
// */
// public MatrixBigDecimal(final File file) {
// List<String> dateiInhalt = StaticMethods.liesTextArray(file, null, true);
// ArrayList<ArrayList<String>> geleseneMatrix = new ArrayList<ArrayList<String>>();
//
// // Text aus Datei formatieren.
// for (String s : dateiInhalt) {
// String[] aufgeteilt = s.replace(" ", "").split("\\|");
// ArrayList<String> zeile = new ArrayList<String>();
// for (int i = 0; i < aufgeteilt.length; i++) {
// zeile.add(aufgeteilt[i]);
// }
// geleseneMatrix.add(zeile);
// }
//
// // Matrix erzeugen.
// int x = geleseneMatrix.get(1).size() - 3;
// int y = geleseneMatrix.size() - 2;
// this.matrix = new double[x][y];
// this.zeilenUeberschr = new Integer2D[this.getRowCount()];
// this.spaltenUeberschr = new Integer2D[this.getColumnCount()];
//
// // Einträge.
// for (int i = 0; i < this.getColumnCount(); i++) {
// for (int j = 0; j < this.getRowCount(); j++) {
// this.set(i, j, Double.parseDouble(geleseneMatrix.get(j + 2).get(i + 3)));
// }
// }
//
// // Überschriften.
// for (int i = 0; i < this.getColumnCount(); i++) {
// this.spaltenUeberschr[i] = Integer2D.parseInteger2D(geleseneMatrix.get(1).get(i + 3));
// }
// for (int j = 0; j < this.getRowCount(); j++) {
// this.zeilenUeberschr[j] = Integer2D.parseInteger2D(geleseneMatrix.get(j + 2).get(1));
// }
// }
/**
* Generates empty matrix of a certain size.
*
* @param x The number of columns.
* @param y The number of rows.
*/
public MatrixBigDecimal(final int x, final int y) {
this.matrix = new BigDecimal[x][y];
this.zeilenUeberschr = new Integer2D[this.getRowCount()];
this.spaltenUeberschr = new Integer2D[this.getColumnCount()];
}
/**
* Generates matrix copying an existing matrix.
*
* @param mat The existing matrix.
*/
public MatrixBigDecimal(final BigDecimal[][] mat) {
if (mat == null) {
throw new RuntimeException("Matrix darf nicht null sein.");
}
if (mat.length == 0) {
this.matrix = new BigDecimal[0][0];
}
this.matrix = new BigDecimal[mat.length][mat[0].length];
for (int i = 0; i < mat.length; i++) {
for (int j = 0; j < mat[0].length; j++) {
this.matrix[i][j] = mat[i][j];
}
}
this.zeilenUeberschr = new Integer2D[this.getRowCount()];
this.spaltenUeberschr = new Integer2D[this.getColumnCount()];
}
public MatrixBigDecimal(final MatrixBigDecimal matrix) {
this(matrix.matrix);
for (int i = 0; i < matrix.getColumnCount(); i++) {
this.spaltenUeberschr[i] = matrix.spaltenUeberschr[i];
}
for (int j = 0; j < matrix.getRowCount(); j++) {
this.zeilenUeberschr[j] = matrix.zeilenUeberschr[j];
}
}
/* ************* EO Constructors ************* */
public void storeMatrix(final File file, final ParCollection pars) {
String path = null;
if (file.getParent() != null) {
path = file.getParentFile().toString();
} else {
path = "";
}
StaticMethods.speichereTextDatei(
path,
file.getName(),
this.toStringFormatiert(),
pars);
}
public static MatrixBigDecimal rowSum(final MatrixBigDecimal mat) {
MatrixBigDecimal result = new MatrixBigDecimal(1, mat.getRowCount());
for (int j = 0; j < mat.getRowCount(); j++) {
for (int i = 0; i < mat.getColumnCount(); i++) {
result.set(0, j, result.get(0, j).add(mat.get(i, j)));
}
}
return result;
}
public static MatrixBigDecimal columnSum(final MatrixBigDecimal mat) {
MatrixBigDecimal result = new MatrixBigDecimal(mat.getColumnCount(), 1);
for (int j = 0; j < mat.getRowCount(); j++) {
for (int i = 0; i < mat.getColumnCount(); i++) {
result.set(i, 0, result.get(i, 0).add(mat.get(i, j)));
}
}
return result;
}
public String createMatlabString() {
String s = "";
int hoehe = this.getRowCount();
int breite = this.getColumnCount();
s += "[";
for (int i = 0; i < hoehe; i++) {
String semikolon = "";
if (i < hoehe - 1) {
semikolon = ";";
}
s += "[";
for (int j = 0; j < breite; j++) {
String komma = "";
if (j < breite - 1) {
komma = ",";
}
s += this.get(j, i) + komma;
}
s += "]" + semikolon;
}
s += "]";
return s;
}
public void revertRows() {
BigDecimal[][] newMatrix = new BigDecimal[this.matrix.length][this.matrix[0].length];
Integer2D[] newZeilen = new Integer2D[this.zeilenUeberschr.length];
for (int i = 0; i < newMatrix.length; i++) {
for (int j = 0; j < newMatrix[0].length; j++) {
newMatrix[newMatrix.length - i - 1][j] = this.matrix[i][j];
}
newZeilen[newMatrix.length - i - 1] = this.zeilenUeberschr[i];
}
this.zeilenUeberschr = newZeilen;
this.matrix = newMatrix;
}
public void revertColumns() {
BigDecimal[][] newMatrix = new BigDecimal[this.matrix.length][this.matrix[0].length];
Integer2D[] newSpalten = new Integer2D[this.spaltenUeberschr.length];
for (int i = 0; i < newMatrix.length; i++) {
for (int j = 0; j < newMatrix[0].length; j++) {
newMatrix[i][newMatrix[0].length - j - 1] = this.matrix[i][j];
newSpalten[newMatrix.length - j - 1] = this.spaltenUeberschr[j];
}
}
this.spaltenUeberschr = newSpalten;
this.matrix = newMatrix;
}
/**
* Performs a rounding operation on the values of all cells of the matrix.
*
* @param digits Number of digits after decimal point.
*/
public MatrixBigDecimal round(final int digits) {
if (this.matrix.length == 0) {
return this;
}
for (int i = 0; i < this.matrix.length; i++) {
for (int j = 0; j < this.matrix[0].length; j++) {
this.matrix[i][j].round(new MathContext(digits));
}
}
return this;
}
/**
* Matrix multiplication (by Thomas Darimont).
*
* @param other The matrix to multiply with.
*
* @return this x other.
*/
public void mult(final MatrixBigDecimal other) {
if (this.matrix[0].length != other.getColumnCount()) {
throw new IllegalArgumentException(
"Zeilenanzahl von andere muss der "
+ "Spaltenanzahl von this entsprechen");
}
BigDecimal[][] c = new BigDecimal[this.matrix.length][other.getRowCount()];
for (int i = 0; i < this.getColumnCount(); i++) {
for (int j = 0; j < other.getRowCount(); j++) {
for (int k = 0; k < this.getRowCount(); k++) {
if (c[i][j] == null) {
c[i][j] = BigDecimal.ZERO;
}
c[i][j] = c[i][j].add(this.get(i, k).multiply(other.get(k, j), new MathContext(GLOBAL_SCALE)));
}
}
}
this.matrix = c;
}
/**
* Raise to a power.
*
* @param n The power to raise to.
*/
public void pow(final int n) {
if (this.getColumnCount() != this.getRowCount()) {
throw new RuntimeException("Potenzieren nur bei quadratischen"
+ "Matrizen möglich");
}
MatrixBigDecimal b = new MatrixBigDecimal(this.matrix);
if (n == 0) {
this.einheitsMatrix();
}
for (int i = 1; i < n; i++) {
this.mult(b);
}
}
/**
* Adds other to this; Caution: this gets overwritten by result.
*
* @param other The matrix to add to this.
*
* @return this + other.
*/
public MatrixBigDecimal add(final MatrixBigDecimal other) {
if (this.getColumnCount() != other.getColumnCount()
|| this.getRowCount() != other.getRowCount()) {
throw new RuntimeException("Wrong dimension in this vs. other.");
}
for (int i = 0; i < this.getColumnCount(); i++) {
for (int j = 0; j < this.getRowCount(); j++) {
this.set(i, j, this.get(i, j).add(other.get(i, j)));
}
}
return this;
}
/**
* Subtracts other from this; Caution: this gets overwritten by result.
*
* @param other The matrix to subtract from this.
*
* @return this - other.
*/
public MatrixBigDecimal sub(final MatrixBigDecimal other) {
if (this.getColumnCount() != other.getColumnCount()
|| this.getRowCount() != other.getRowCount()) {
throw new RuntimeException("Summand ist unterschiedlich dimensioniert.");
}
for (int i = 0; i < this.getColumnCount(); i++) {
for (int j = 0; j < this.getRowCount(); j++) {
this.set(i, j, this.get(i, j).subtract(other.get(i, j)));
}
}
return this;
}
/**
* Generates identity matrix.
*/
public MatrixBigDecimal einheitsMatrix() {
for (int i = 0; i < this.getRowCount(); i++) {
for (int j = 0; j < this.getColumnCount(); j++) {
if (i == j) {
this.set(j, i, BigDecimal.ONE);
} else {
this.set(j, i, BigDecimal.ZERO);
}
}
}
return this;
}
@Override
public String toString() {
String s = "";
for (int i = 0; i < this.getRowCount(); i++) {
for (int j = 0; j < this.getColumnCount(); j++) {
s += this.get(j, i) + ",";
}
s = s.substring(0, s.length() - 1);
s += ";";
}
s = s.substring(0, s.length() - 1);
return s;
}
public String toStringFormatiert(){
String s = "\n";
int[] laengen = new int[this.getColumnCount()];
int zeilUebLaenge = 0;
// Breite der Spalten festlegen - Spaltenüberschriften durchsuchen.
for (int j = 0; j < this.spaltenUeberschr.length; j++) {
if ((this.spaltenUeberschr[j] + "").length() > laengen[j]) {
laengen[j] = (this.spaltenUeberschr[j] + "").length();
}
}
// Breite der Spalten festlegen - Übrige Zellen durchsuchen.
for (int i = 0; i < this.getRowCount(); i++) {
if ((this.zeilenUeberschr[i] + "").length() > zeilUebLaenge) {
zeilUebLaenge = (this.zeilenUeberschr[i] + "").length();
}
for (int j = 0; j < this.getColumnCount(); j++) {
if ((this.matrix[j][i] + "").length() > laengen[j]) {
laengen[j] = (this.matrix[j][i] + "").length();
}
}
}
// Linke obere Zelle.
s += " | ";
for (int k = 0; k < zeilUebLaenge; k++) {
s += " ";
}
// Obere Spalte.
s += " || ";
for (int j = 0; j < this.spaltenUeberschr.length; j++) {
s += this.spaltenUeberschr[j];
for (int k = 0; k < laengen[j]
- (this.spaltenUeberschr[j] + "").length(); k++) {
s += " ";
}
s += " | ";
}
s += "\n";
// Weitere Zellen.
for (int i = 0; i < this.getRowCount(); i++) {
s += " | ";
s += this.zeilenUeberschr[i];
for (int k = 0; k < zeilUebLaenge - (this.zeilenUeberschr[i] + "").length(); k++) {
s += " ";
}
s += " || ";
for (int j = 0; j < this.getColumnCount(); j++) {
s += this.matrix[j][i];
for (int k = 0; k < laengen[j]
- (this.matrix[j][i] + "").length(); k++) {
s += " ";
}
s += " | ";
}
s += "\n";
}
return s;
}
public String toPlainString() {
String s = "\n";
int[] laengen = new int[this.getColumnCount()];
// Breite der Spalten festlegen - Übrige Zellen durchsuchen.
for (int i = 0; i < this.getRowCount(); i++) {
for (int j = 0; j < this.getColumnCount(); j++) {
if ((this.matrix[j][i] + "").length() > laengen[j]) {
laengen[j] = (this.matrix[j][i] + "").length();
}
}
}
// Weitere Zellen.
for (int i = 0; i < this.getRowCount(); i++) {
for (int j = 0; j < this.getColumnCount(); j++) {
s += this.matrix[j][i];
for (int k = 0; k < laengen[j]
- (this.matrix[j][i] + "").length(); k++) {
s += " ";
}
s += "\t";
}
s += "\n";
}
return s;
}
public BigDecimal get(final int i, final int j) {
return this.matrix[i][j];
}
public void set(final int i, final int j, final BigDecimal wert) {
if (i >= 0 && i < this.getColumnCount()
&& j >= 0 && j < this.getRowCount()) {
this.matrix[i][j] = wert;
} else {
StaticMethods.logWarning(
"Matrix field ("
+ i
+ "/"
+ j
+ ") does not exist. Value \""
+ wert
+ "\" not set.",
null);
throw new RuntimeException();
}
}
public int getColumnCount() {
return this.matrix.length;
}
public int getRowCount() {
if (this.matrix.length == 0) {
return 0;
}
return this.matrix[0].length;
}
public Integer2D getRowTitle(final int j) {
return this.zeilenUeberschr[j];
}
public Integer2D getColumnTitle(final int i) {
return this.spaltenUeberschr[i];
}
public void setColumnTitle(final int i, final Integer2D wert) {
this.spaltenUeberschr[i] = wert;
}
public void setRowTitle(final int j, final Integer2D wert) {
this.zeilenUeberschr[j] = wert;
}
public MatrixBigDecimal transpone() {
MatrixBigDecimal mat = new MatrixBigDecimal(this.getRowCount(), this.getColumnCount());
for (int i = 0; i < this.getColumnCount(); i++) {
mat.setRowTitle(i, this.getColumnTitle(i));
for (int j = 0; j < this.getRowCount(); j++) {
mat.setColumnTitle(j, this.getRowTitle(j));
mat.set(j, i, this.get(i, j));
}
}
this.matrix = mat.matrix;
this.spaltenUeberschr = mat.spaltenUeberschr;
this.zeilenUeberschr = mat.zeilenUeberschr;
return this;
}
public MatrixBigDecimal erzeugeSpaltenMatrix(final int[] spalten) {
MatrixBigDecimal result = new MatrixBigDecimal(spalten.length, this.getRowCount());
for (int k = 0; k < spalten.length; k++) {
result.setColumnTitle(k, this.getColumnTitle(spalten[k]));
for (int j = 0; j < this.getRowCount(); j++) {
result.setRowTitle(j, this.getColumnTitle(j));
result.set(k, j, this.get(spalten[k], j));
}
}
return result;
}
public MatrixBigDecimal erzeugeZeilenMatrix(final int[] zeilen) {
MatrixBigDecimal result = new MatrixBigDecimal(this.getColumnCount(), zeilen.length);
for (int k = 0; k < zeilen.length; k++) {
result.setRowTitle(k, this.getRowTitle(zeilen[k]));
for (int j = 0; j < this.getColumnCount(); j++) {
result.setColumnTitle(j, this.getColumnTitle(j));
result.set(j, k, this.get(j, zeilen[k]));
}
}
return result;
}
private BigDecimal getRandomValue() {
return new BigDecimal(rand.nextDouble() * (randomMaxValue - randomMinValue) + randomMinValue);
}
public MatrixBigDecimal randomize() {
for (int i = 0; i < this.getColumnCount(); i++) {
for (int j = 0; j < this.getRowCount(); j++) {
this.set(i, j, getRandomValue());
}
}
return this;
}
public BigDecimal maxValue() {
BigDecimal max = new BigDecimal(Double.NEGATIVE_INFINITY);
for (int i = 0; i < this.getColumnCount(); i++) {
for (int j = 0; j < this.getColumnCount(); j++) {
if (this.get(i, j).compareTo(max) > 0) {
max = this.get(i, j);
}
}
}
return max;
}
public BigDecimal minValue() {
BigDecimal min = new BigDecimal(Double.POSITIVE_INFINITY);
for (int i = 0; i < this.getColumnCount(); i++) {
for (int j = 0; j < this.getColumnCount(); j++) {
if (this.get(i, j).compareTo(min) < 0) {
min = this.get(i, j);
}
}
}
return min;
}
/**
* Sum of all non-zero rows gets 1, relations are preserved rowwise.
*
* @param matrix The matrix to normalize.
*/
public static void normalizeRowwise(
final MatrixBigDecimal matrix) {
for (int j = 0; j < matrix.getRowCount(); j++) {
BigDecimal summe = new BigDecimal(0);
for (int i = 0; i < matrix.getColumnCount(); i++) {
summe.add(matrix.get(i, j));
}
if (summe.compareTo(BigDecimal.ZERO) != 0) {
for (int i = 0; i < matrix.getColumnCount(); i++) {
matrix.set(i, j, matrix.get(i, j).divide(summe, GLOBAL_SCALE, GLOBAL_ROUNDING_MODE));
}
}
}
}
public static ArrayList<BigDecimal> parseArray(String array) {
String[] strArr = array.replace(" ", "").split(",");
ArrayList<BigDecimal> doubListe = new ArrayList<BigDecimal>(strArr.length);
for (String s : strArr) {
doubListe.add(new BigDecimal(s));
}
return doubListe;
}
/**
* Parses strings of the form:
*
* "0.0,1.2,2.9; 1.1,2.3,1.4; 2.2,3.3,-4.4; 5.4,-6.4,7.6"
* ==>
* 0.0 1.2 2.9
* 1.1 2.3 1.4
* 2.2 3.3 -4.4
* 5.4 -6.4 7.6
*
* @param matrix
* @return
*/
@SuppressWarnings("unchecked")
public static MatrixBigDecimal parseMatrix(final String matrix) {
String[] rows = matrix.replace(" ", "").replace("\n", "").split(";");
ArrayList<BigDecimal> rowParsed;
ArrayList<BigDecimal>[] matrixList = new ArrayList[rows.length];
int maxColumns = 0;
int z = 0;
BigDecimal[][] matrixArray;
for (String row : rows) {
rowParsed = MatrixBigDecimal.parseArray(row);
if (rowParsed.size() > maxColumns) {
maxColumns = rowParsed.size();
}
matrixList[z] = rowParsed;
z++;
}
matrixArray = new BigDecimal[maxColumns][rows.length];
for (int i = 0; i < matrixArray.length; i++) {
for (int j = 0; j < matrixArray[0].length; j++) {
matrixArray[i][j] = matrixList[j].get(i);
}
}
return new MatrixBigDecimal(matrixArray);
}
/**
* <code>This</code> is not changed.
*
* @return Another matrix that is the inverse (or pseudo-inverse) of <code>this</code>.
*/
public MatrixBigDecimal inverse() {
JamaMatrixBigDecimal a = new JamaMatrixBigDecimal(this.matrix);
JamaMatrixBigDecimal i = a.inverse(MatrixBigDecimal.GLOBAL_ROUNDING_MODE);
MatrixBigDecimal inv = new MatrixBigDecimal(i.getArray());
return inv;
}
/**
* Adds a zero row at the end of the matrix (headings are set null).
*/
public void addRow() {
this.addRow(this.getRowCount(), false);
}
/**
* Adds a zero row at the specified position (headings are set null).
* If the position is larger than the number of row, the matrix is filled
* with zero rows at the end to match.
*
* @param position The position to add a new row.
*/
public void addRow(int position, boolean fillWithRandomValues) {
if (position > this.getRowCount()) {
this.addRow(position - 1, fillWithRandomValues);
}
MatrixBigDecimal newMatrix = new MatrixBigDecimal(this.getColumnCount(), this.getRowCount() + 1);
int buffer = 0;
for (int j = 0; j < this.getRowCount(); j++) {
if (j >= position) {
buffer = 1;
}
for (int i = 0; i < this.getColumnCount(); i++) {
newMatrix.set(i, j + buffer, this.get(i, j));
}
}
if (fillWithRandomValues) {
for (int i = 0; i < this.getColumnCount(); i++) {
newMatrix.set(i, position, this.getRandomValue());
}
} else {
for (int i = 0; i < this.getColumnCount(); i++) {
newMatrix.set(i, position, BigDecimal.ZERO);
}
}
this.matrix = newMatrix.matrix;
this.spaltenUeberschr = newMatrix.spaltenUeberschr;
this.zeilenUeberschr = newMatrix.zeilenUeberschr;
}
private double randomMaxValue = 1;
private double randomMinValue = 1;
private Random rand = new Random();
public void setRandomParameters(double randMaxValue, double randMinValue, Random rand) {
this.rand = rand;
this.randomMaxValue = randMaxValue;
this.randomMinValue = randMinValue;
}
/**
* Adds a zero row at the end of the matrix (headings are set null).
*/
public void addColumn() {
this.addColumn(this.getColumnCount(), false);
}
/**
* Adds a zero row at the specified position (headings are set null).
* If the position is larger than the number of row, the matrix is filled
* with zero rows at the end to match.
*
* @param position The position to add a new row.
*/
public void addColumn(int position, boolean fillWithRandomValues) {
if (position > this.getColumnCount()) {
this.addColumn(position - 1, fillWithRandomValues);
}
MatrixBigDecimal newMatrix = new MatrixBigDecimal(this.getColumnCount() + 1, this.getRowCount());
int buffer = 0;
for (int i = 0; i < this.getColumnCount(); i++) {
if (i >= position) {
buffer = 1;
}
for (int j = 0; j < this.getRowCount(); j++) {
newMatrix.set(i + buffer, j, this.get(i, j));
}
}
if (fillWithRandomValues) {
for (int i = 0; i < this.getRowCount(); i++) {
newMatrix.set(position, i, this.getRandomValue());
}
} else {
for (int i = 0; i < this.getColumnCount(); i++) {
newMatrix.set(i, position, BigDecimal.ZERO);
}
}
this.matrix = newMatrix.matrix;
this.spaltenUeberschr = newMatrix.spaltenUeberschr;
this.zeilenUeberschr = newMatrix.zeilenUeberschr;
}
}