/*
* Concept profile generation tool suite
* Copyright (C) 2015 Biosemantics Group, Erasmus University Medical Center,
* Rotterdam, The Netherlands
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package org.erasmusmc.math.matrix;
import java.util.ArrayList;
import java.util.List;
import org.erasmusmc.math.space.Space;
import org.erasmusmc.math.vector.ArrayVector;
import org.erasmusmc.math.vector.Vector;
public class RowArrayMatrix<R, C> extends ArrayMatrix<R, C> {
public RowArrayMatrix(Space<R> rowSpace, Space<C> columnSpace) {
super(rowSpace, columnSpace);
}
public RowArrayMatrix(Matrix<R, C> matrix) {
super(matrix);
}
public void set(R row, C column, double value) {
values[rowSpace.indexOfObject(row)][columnSpace.indexOfObject(column)] = value;
}
public double get(R row, C column) {
return values[rowSpace.indexOfObject(row)][columnSpace.indexOfObject(column)];
}
public void set(Matrix<R, C> matrix) {
int rows = rowSpace.getDimensions();
int columns = columnSpace.getDimensions();
for (int i = 0; i < rows; i++) {
double[] rowValues = values[i];
Vector<C> rowVector = matrix.getRow(rowSpace.objectForIndex(i));
for (int j = 0; j < columns; j++)
rowValues[j] = rowVector.get(columnSpace.objectForIndex(j));
}
}
public void setSpaces(Space<R> rowSpace, Space<C> columnSpace) {
this.rowSpace = rowSpace;
this.columnSpace = columnSpace;
values = new double[rowSpace.getDimensions()][columnSpace.getDimensions()];
}
public Vector<C> getRow(R row) {
return new ArrayVector<C>(getColumnSpace(), values[rowSpace.indexOfObject(row)]);
}
public List<Vector<C>> getRows() {
List<Vector<C>> result = new ArrayList<Vector<C>>();
ArrayMatrixSecondSpaceCursor arrayMatrixSecondSpaceCursor = new ArrayMatrixSecondSpaceCursor<C, R>(columnSpace, rowSpace);
while (arrayMatrixSecondSpaceCursor.isValid()) {
result.add(arrayMatrixSecondSpaceCursor.get());
arrayMatrixSecondSpaceCursor.next();
}
return result;
}
public MatrixCursor<R, C> getRowCursor() {
return new ArrayMatrixSecondSpaceCursor<C, R>(columnSpace, rowSpace);
}
public MatrixCursor<C, R> getColumnCursor() {
return new ArrayMatrixFirstSpaceCursor<C, R>(columnSpace, rowSpace);
}
protected double sqr(double a) {
return a * a;
}
protected double pythagoras(double a, double b) {
double absa, absb;
absa = Math.abs(a);
absb = Math.abs(b);
if (absa > absb)
return absa * Math.sqrt(1.0 + sqr(absb / absa));
else
return absb == 0.0 ? 0.0 : absb * Math.sqrt(1.0 + sqr(absa / absb));
}
protected double transferSign(double a, double b) {
return b >= 0 ? Math.abs(a) : -Math.abs(a);
}
public void tridiagonalSymmetric(ArrayVector d, ArrayVector e) {
int i, j, k, l;
double scale, hh, h, g, f;
int rows = getRowSpace().getDimensions();
for (i = rows - 1; i >= 1; i--) {
l = i - 1;
h = scale = 0.0;
if (l > 0) {
for (k = 0; k <= l; k++)
scale += Math.abs(values[i][k]);
if (scale == 0.0)
e.values[i] = values[i][l];
else {
for (k = 0; k <= l; k++) {
values[i][k] /= scale;
h += sqr(values[i][k]);
}
f = values[i][l];
g = f >= 0.0 ? -Math.sqrt(h) : Math.sqrt(h);
e.values[i] = scale * g;
h -= f * g;
values[i][l] = f - g;
f = 0.0;
for (j = 0; j <= l; j++) {
values[j][i] = values[i][j] / h;
g = 0.0;
for (k = 0; k <= j; k++)
g += values[j][k] * values[i][k];
for (k = j + 1; k <= l; k++)
g += values[k][j] * values[i][k];
e.values[j] = g / h;
f += e.values[j] * values[i][j];
}
hh = f / (h + h);
for (j = 0; j <= l; j++) {
f = values[i][j];
g = e.values[j] - hh * f;
e.values[j] = g;
for (k = 0; k <= j; k++)
values[j][k] -= f * e.values[k] + g * values[i][k];
}
}
}
else
e.values[i] = values[i][l];
d.values[i] = h;
}
d.values[0] = 0.0;
e.values[0] = 0.0;
for (i = 0; i < rows; i++) {
l = i - 1;
if (d.values[i] != 0.0) {
for (j = 0; j <= l; j++) {
g = 0.0;
for (k = 0; k <= l; k++)
g += values[i][k] * values[k][j];
for (k = 0; k <= l; k++)
values[k][j] -= g * values[k][i];
}
}
d.values[i] = values[i][i];
values[i][i] = 1.0;
for (j = 0; j <= l; j++) {
values[j][i] = 0;
values[i][j] = 0;
}
}
}
public void eigenvectorsQLdiagonal(ArrayVector d, ArrayVector e) {
int m, l, iter, i, k;
double s, r, p, g, f, dd, c, b;
int rows = getRowSpace().getDimensions();
for (i = 1; i < rows; i++)
e.values[i - 1] = e.values[i];
e.values[rows - 1] = 0.0;
for (l = 0; l < rows; l++) {
iter = 0;
do {
for (m = l; m < rows - 1; m++) {
dd = Math.abs(d.values[m]) + Math.abs(d.values[m + 1]);
if ((double) (Math.abs(e.values[m]) + dd) == dd)
break;
}
if (m != l) {
// if (iter++ == 30)
iter++;
g = (d.values[l + 1] - d.values[l]) / (2.0 * e.values[l]);
r = pythagoras(g, 1.0);
g = d.values[m] - d.values[l] + e.values[l] / (g + transferSign(r, g));
s = c = 1.0;
p = 0.0;
for (i = m - 1; i >= l; i--) {
f = s * e.values[i];
b = c * e.values[i];
e.values[i + 1] = r = pythagoras(f, g);
if (r == 0.0) {
d.values[i + 1] -= p;
e.values[m] = 0.0;
break;
}
s = f / r;
c = g / r;
g = d.values[i + 1] - p;
r = (d.values[i] - g) * s + 2.0 * c * b;
d.values[i + 1] = g + (p = s * r);
g = c * r - b;
for (k = 0; k < rows; k++) {
f = values[k][i + 1];
values[k][i + 1] = s * values[k][i] + c * f;
values[k][i] = c * values[k][i] - s * f;
}
}
if (r == 0.0 && i >= l)
continue;
d.values[l] -= p;
e.values[l] = g;
e.values[m] = 0.0;
}
} while (m != l);
}
}
public void eigenSort(ArrayVector eigenValues) {
int i, j, k;
double p;
int rows = getRowSpace().getDimensions();
for (i = 0; i < rows - 1; i++) {
p = eigenValues.values[k = i];
for (j = i + 1; j < rows; j++)
if (eigenValues.values[j] >= p)
p = eigenValues.values[k = j];
if (k != i) {
eigenValues.values[k] = eigenValues.values[i];
for (j = 0; j < rows; j++) {
p = values[j][i];
values[j][i] = values[j][k];
values[j][k] = p;
}
}
}
}
public RowArrayMatrix eigenVectors(ArrayVector eigenValues) {
RowArrayMatrix result = new RowArrayMatrix(this);
eigenValues.setSpace(getRowSpace());
ArrayVector offDiagonal = new ArrayVector(getRowSpace());
result.tridiagonalSymmetric(eigenValues, offDiagonal);
result.eigenvectorsQLdiagonal(eigenValues, offDiagonal);
result.eigenSort(eigenValues);
return result;
}
public void setAboveDiagonal(double value) {
int rows = getRowSpace().getDimensions();
int columns = getColumnSpace().getDimensions();
for (int i = 0; i < rows - 1; i++)
for (int j = i + 1; j < columns; j++)
values[i][j] = value;
}
public void setBelowDiagonal(double value) {
int rows = getRowSpace().getDimensions();
for (int i = 1; i < rows; i++)
for (int j = 0; j < i; j++)
values[i][j] = value;
}
public void swapRows(int row1, int row2) {
double swap;
int columns = getColumnSpace().getDimensions();
for (int i = 0; i < columns; i++) {
swap = values[row1][i];
values[row1][i] = values[row2][i];
values[row2][i] = swap;
}
}
}