package ch.akuhn.hapax.linalg;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.NoSuchElementException;
import ch.akuhn.hapax.linalg.Vector.Entry;
import ch.akuhn.util.Files;
import ch.akuhn.util.PrintOn;
import ch.akuhn.util.Throw;
public abstract class Matrix {
public double add(int row, int column, double value) {
return put(row, column, get(row, column) + value);
}
public Iterable<Vector> rows() {
return vecs(/*isRow*/ true);
}
private Iterable<Vector> vecs(final boolean isRow) {
return new Iterable<Vector>() {
@Override
public Iterator<Vector> iterator() {
return new Iterator<Vector>() {
private int count = 0;
@Override
public boolean hasNext() {
return count < (isRow ? rowCount() : columnCount());
}
@Override
public Vector next() {
if (!hasNext()) throw new NoSuchElementException();
return new Vec(count++, isRow);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
};
}
public Iterable<Vector> columns() {
return vecs(/*isRow*/ false);
}
public abstract int columnCount();
public double density() {
return (double) used() / size();
}
public int size() {
return rowCount() * columnCount();
}
public abstract double get(int row, int column);
public abstract double put(int row, int column, double value);
public abstract int rowCount();
public abstract int used();
public void storeBinaryOn(DataOutput out) throws IOException {
out.writeInt(this.rowCount());
out.writeInt(this.columnCount());
out.writeInt(this.used());
for (Vector row: rows()) {
out.writeInt(row.used());
for (Entry each: row.entries()) {
out.writeInt(each.index);
out.writeFloat((float) each.value);
}
}
Files.close(out);
}
public void storeBinaryOn(String fname) {
try {
DataOutputStream out = new DataOutputStream(new FileOutputStream(fname));
storeBinaryOn(out);
} catch (Exception ex) {
throw Throw.exception(ex);
}
}
/** @see http://tedlab.mit.edu/~dr/svdlibc/SVD_F_ST.html */
public void storeSparseOn(Appendable appendable) {
// this stores the transposed matrix, but as we will transpose it again
// when reading it, this can be done without loss of generality.
PrintOn out = new PrintOn(appendable);
out.print(this.columnCount()).space();
out.print(this.rowCount()).space();
out.print(this.used()).cr();
for (Vector row: rows()) {
out.print(row.used()).cr();
for (Entry each: row.entries()) {
out.print(each.index).space().print(each.value).space();
}
out.cr();
}
out.close();
}
public void storeSparseOn(String fname) {
storeSparseOn(Files.openWrite(fname));
}
public Vector row(int row) {
return new Vec(row, /*isRow*/ true);
}
public Vector column(int column) {
return new Vec(column, /*isRow*/ false);
}
public double[][] asArray() {
double[][] result = new double[rowCount()][columnCount()];
for (int x = 0; x < result.length; x++) {
for (int y = 0; y < result.length; y++) {
result[x][y] = get(x,y);
}
}
return result;
}
public static int indexOf(Vector vec) {
return ((Vec) vec).index0;
}
private class Vec extends Vector {
private int index0;
private boolean isRow;
private Vec(int n, boolean isRow) {
this.isRow = isRow;
this.index0 = n;
}
@Override
public int size() {
return isRow ? columnCount() : rowCount();
}
@Override
public double put(int index, double value) {
return isRow ? Matrix.this.put(this.index0, index, value)
: Matrix.this.put(index, this.index0, value);
}
@Override
public double get(int index) {
return isRow ? Matrix.this.get(this.index0, index)
: Matrix.this.get(index, this.index0);
}
}
public double minValue() {
double min = Double.MAX_VALUE;
for (Vector row: rows())
for (Entry each: row.entries())
if (each.value < min) min = each.value;
return min;
}
public void storeOn(Appendable appendable) {
PrintOn p = new PrintOn(appendable);
p.p("{\"n\":").p(rowCount()).p(",\"m\":").p(columnCount()).p(",\"data\":[");
for (Vector row: rows()) {
p.separatedBy(",").p("[");
PrintOn pp = new PrintOn(p);
for (Entry each: row.entries()) pp.separatedBy(",").p(each.value);
p.p("]");
}
p.p("]}");
};
}