3 import jalview.ext.android.SparseDoubleArray;
6 * A variant of Matrix intended for use for sparse (mostly zero) matrices. This
7 * class uses a SparseDoubleArray to hold each row of the matrix. The sparse
8 * array only stores non-zero values. This gives a smaller memory footprint, and
9 * fewer matrix calculation operations, for mostly zero matrices.
13 public class SparseMatrix extends Matrix
16 * we choose columns for the sparse arrays as this allows
17 * optimisation of the preMultiply() method used in PCA.run()
19 SparseDoubleArray[] sparseColumns;
22 * Constructor given data in [row][column] order
26 public SparseMatrix(double[][] v)
32 sparseColumns = new SparseDoubleArray[cols];
35 * transpose v[row][col] into [col][row] order
37 for (int col = 0; col < cols; col++)
39 SparseDoubleArray sparseColumn = new SparseDoubleArray();
40 sparseColumns[col] = sparseColumn;
41 for (int row = 0; row < rows; row++)
43 double value = v[row][col];
46 sparseColumn.put(row, value);
53 * Answers the value at row i, column j
56 public double getValue(int i, int j)
58 return sparseColumns[j].get(i);
62 * Sets the value at row i, column j to val
65 public void setValue(int i, int j, double val)
69 sparseColumns[j].delete(i);
73 sparseColumns[j].put(i, val);
78 public double[] getColumn(int i)
80 double[] col = new double[height()];
82 SparseDoubleArray vals = sparseColumns[i];
83 for (int nonZero = 0; nonZero < vals.size(); nonZero++)
85 col[vals.keyAt(nonZero)] = vals.valueAt(nonZero);
93 double[][] vals = new double[height()][width()];
94 for (int i = 0; i < height(); i++)
98 return new SparseMatrix(vals);
102 public MatrixI transpose()
104 double[][] out = new double[cols][rows];
109 for (int i = 0; i < cols; i++)
112 * put non-zero values into the corresponding row
113 * of the transposed matrix
115 SparseDoubleArray vals = sparseColumns[i];
116 for (int nonZero = 0; nonZero < vals.size(); nonZero++)
118 out[i][vals.keyAt(nonZero)] = vals.valueAt(nonZero);
122 return new SparseMatrix(out);
126 * Answers a new matrix which is the product in.this. If the product contains
127 * less than 20% non-zero values, it is returned as a SparseMatrix, else as a
130 * This method is optimised for the sparse arrays which store column values
131 * for a SparseMatrix. Note that postMultiply is not so optimised. That would
132 * require redundantly also storing sparse arrays for the rows, which has not
133 * been done. Currently only preMultiply is used in Jalview.
136 public MatrixI preMultiply(MatrixI in)
138 if (in.width() != rows)
140 throw new IllegalArgumentException("Can't pre-multiply " + this.rows
141 + " rows by " + in.width() + " columns");
143 double[][] tmp = new double[in.height()][this.cols];
146 for (int i = 0; i < in.height(); i++)
148 for (int j = 0; j < this.cols; j++)
151 * result[i][j] is the vector product of
152 * in.row[i] and this.column[j]
153 * we only need to use non-zero values from the column
155 SparseDoubleArray vals = sparseColumns[j];
156 boolean added = false;
157 for (int nonZero = 0; nonZero < vals.size(); nonZero++)
159 int myRow = vals.keyAt(nonZero);
160 double myValue = vals.valueAt(nonZero);
161 tmp[i][j] += (in.getValue(i, myRow) * myValue);
164 if (added && tmp[i][j] != 0d)
166 count++; // non-zero entry in product
172 * heuristic rule - if product is more than 80% zero
173 * then construct a SparseMatrix, else a Matrix
175 if (count * 5 < in.height() * cols)
177 return new SparseMatrix(tmp);
181 return new Matrix(tmp);
186 protected double divideValue(int i, int j, double divisor)
190 return getValue(i, j);
192 double v = sparseColumns[j].divide(i, divisor);
197 protected double addValue(int i, int j, double addend)
199 double v = sparseColumns[j].add(i, addend);
204 * Returns the fraction of the whole matrix size that is actually modelled in
205 * sparse arrays (normally, the non-zero values)
209 public float getFillRatio()
212 for (SparseDoubleArray col : sparseColumns)
216 return count / (float) (height() * width());