package org.openlca.eigen; import gnu.trove.impl.Constants; import gnu.trove.iterator.TIntDoubleIterator; import gnu.trove.map.hash.TIntDoubleHashMap; import gnu.trove.map.hash.TIntObjectHashMap; import org.openlca.core.math.IMatrix; /** * A sparse matrix implementation that uses primitive hash maps from the Trove * project to store the data. Filling this matrix is fast with relatively low * memory consumption. */ public class HashMatrix implements IMatrix { private final int rows; private final int cols; private final TIntObjectHashMap<TIntDoubleHashMap> data; public HashMatrix(int rows, int cols) { this.rows = rows; this.cols = cols; data = new TIntObjectHashMap<>(Constants.DEFAULT_CAPACITY, Constants.DEFAULT_LOAD_FACTOR, -1); } public HashMatrix(double[][] values) { rows = values.length; int cols = 1; for (int row = 0; row < rows; row++) cols = Math.max(cols, values[row].length); this.cols = cols; data = new TIntObjectHashMap<>(Constants.DEFAULT_CAPACITY, Constants.DEFAULT_LOAD_FACTOR, -1); for (int row = 0; row < rows; row++) { double[] rowVals = values[row]; for (int col = 0; col < rowVals.length; col++) set(row, col, rowVals[col]); } } @Override public int rows() { return rows; } @Override public int columns() { return cols; } @Override public void set(int row, int col, double val) { if (Numbers.isZero(val) && !hasEntry(row, col)) return; TIntDoubleHashMap rowMap = data.get(row); if (rowMap == null) { rowMap = new TIntDoubleHashMap(Constants.DEFAULT_CAPACITY, Constants.DEFAULT_LOAD_FACTOR, -1, 0); data.put(row, rowMap); } rowMap.put(col, val); } public boolean hasEntry(int row, int col) { TIntDoubleHashMap rowMap = data.get(row); if (rowMap == null) return false; return rowMap.get(col) != 0; } @Override public double get(int row, int col) { TIntDoubleHashMap rowMap = data.get(row); if (rowMap == null) return 0; return rowMap.get(col); } @Override public double[] getColumn(int i) { double[] column = new double[rows]; for (int row = 0; row < rows; row++) { column[row] = get(row, i); } return column; } @Override public double[] getRow(int i) { double[] row = new double[cols]; for (int col = 0; col < cols; col++) { row[col] = get(i, col); } return row; } @Override public HashMatrix copy() { final HashMatrix copy = new HashMatrix(rows, cols); iterate(new MatrixIterator() { @Override public void next(int row, int col, double val) { copy.set(row, col, val); } }); return copy; } /** * Iterates over the non-zero values in this matrix. */ public void iterate(MatrixIterator it) { for (int row : data.keys()) { TIntDoubleHashMap rowMap = data.get(row); if (rowMap == null) continue; for (int col : rowMap.keys()) { double val = rowMap.get(col); if (Numbers.isZero(val)) continue; it.next(row, col, val); } } } public interface MatrixIterator { void next(int row, int col, double val); } public CompressedRowMatrix compress() { CompressedRowMatrix c = new CompressedRowMatrix(rows, cols); int entryCount = getNumberOfEntries(); c.columnIndices = new int[entryCount]; c.values = new double[entryCount]; int idx = 0; for (int row = 0; row < rows; row++) { c.rowPointers[row] = idx; TIntDoubleHashMap rowMap = data.get(row); if (rowMap == null) continue; TIntDoubleIterator it = rowMap.iterator(); while (it.hasNext()) { it.advance(); c.columnIndices[idx] = it.key(); c.values[idx] = it.value(); idx++; } } return c; } public int getNumberOfEntries() { int entryCount = 0; for (int row = 0; row < rows; row++) { TIntDoubleHashMap rowMap = data.get(row); if (rowMap == null) continue; entryCount += rowMap.size(); } return entryCount; } @Override public String toString() { StringBuilder builder = new StringBuilder("HashMapMatrix = ["); int maxRows = rows > 15 ? 15 : rows; int maxCols = cols > 15 ? 15 : cols; for (int row = 0; row < maxRows; row++) { for (int col = 0; col < maxCols; col++) { double val = get(row, col); builder.append(val); if (col < (maxCols - 1)) builder.append(","); } if (row < (maxRows - 1)) builder.append(";"); } if (maxCols < cols) builder.append(", ..."); if (maxRows < rows) builder.append("; ..."); builder.append("]"); return builder.toString(); } }