Added some iterating methods to Matrix (issue #57) #79

Merged
merged 18 commits into from Aug 10, 2013

Projects

None yet

2 participants

@SamoylovMD
Contributor

Added: each, eachInRow, eachInColumn methods;
Renamed each method to eachNonZero.

@vkostyukov
Owner

Test are failed, since there is a usage if each in MatrixMarketStream. So, we need to change it to new eachNonZero call.

@vkostyukov vkostyukov commented on an outdated diff Aug 8, 2013
src/main/java/org/la4j/matrix/AbstractMatrix.java
@@ -772,6 +803,33 @@ public void each(MatrixProcedure procedure) {
}
@Override
+ public void eachInRow(MatrixProcedure procedure, int i) {
+ Vector vector = this.getRow(i);
@vkostyukov
vkostyukov Aug 8, 2013 Owner

This is not a good idea. The getRow is extreamally slow method. We need to iterate in straightforward way. Like:

for (int j = 0; j < columns; j++) {
  procedure.apply(i, j, get(i, j));
}
@vkostyukov vkostyukov commented on an outdated diff Aug 8, 2013
src/main/java/org/la4j/matrix/AbstractMatrix.java
@@ -772,6 +803,33 @@ public void each(MatrixProcedure procedure) {
}
@Override
+ public void eachInRow(MatrixProcedure procedure, int i) {
@vkostyukov
vkostyukov Aug 8, 2013 Owner

I would prefer to have signature like:

void eachInRow(int i, MatrixProcedure procedure);
@vkostyukov vkostyukov commented on an outdated diff Aug 8, 2013
src/main/java/org/la4j/matrix/AbstractMatrix.java
@@ -772,6 +803,33 @@ public void each(MatrixProcedure procedure) {
}
@Override
+ public void eachInRow(MatrixProcedure procedure, int i) {
+ Vector vector = this.getRow(i);
+ for (int j = 0; j < columns; j++) {
+ procedure.apply(i, j, vector.get(j));
+ }
+ }
+
+ @Override
+ public void eachInColumn(MatrixProcedure procedure, int j) {
@vkostyukov
vkostyukov Aug 8, 2013 Owner

I would prefer to have signature like:

void eachInColumn(int j, MatrixProcedure procedure);
@vkostyukov vkostyukov commented on an outdated diff Aug 8, 2013
src/main/java/org/la4j/matrix/AbstractMatrix.java
@@ -772,6 +803,33 @@ public void each(MatrixProcedure procedure) {
}
@Override
+ public void eachInRow(MatrixProcedure procedure, int i) {
+ Vector vector = this.getRow(i);
+ for (int j = 0; j < columns; j++) {
+ procedure.apply(i, j, vector.get(j));
+ }
+ }
+
+ @Override
+ public void eachInColumn(MatrixProcedure procedure, int j) {
+ Vector vector = this.getColumn(j);
@vkostyukov
vkostyukov Aug 8, 2013 Owner

And the same issue with getColumn(). Use straightforward iteration.

@vkostyukov vkostyukov commented on an outdated diff Aug 8, 2013
src/main/java/org/la4j/matrix/AbstractMatrix.java
+ }
+ }
+
+ @Override
+ public void eachInColumn(MatrixProcedure procedure, int j) {
+ Vector vector = this.getColumn(j);
+ for (int i = 0; i < rows; i++) {
+ procedure.apply(i, j, vector.get(j));
+ }
+ }
+
+ @Override
+ public void eachNonZero(MatrixProcedure procedure) {
+ for (int i = 0; i < rows; i++) {
+ for (int j = 0; j < columns; j++) {
+ if (Math.abs(get(i,j))>Matrices.EPS) {
@vkostyukov
vkostyukov Aug 8, 2013 Owner

That's fine. Just add a bit spaces. Like:

(Math.abs(get(i, j)) > Matrices.EPS)
@vkostyukov
vkostyukov Aug 9, 2013 Owner

I still have a concern about spaces.

@vkostyukov vkostyukov commented on an outdated diff Aug 8, 2013
src/main/java/org/la4j/matrix/AbstractSafeMatrix.java
@@ -408,6 +418,21 @@ public void each(MatrixProcedure procedure) {
}
@Override
+ public void eachInRow(MatrixProcedure procedure, int i) {
@vkostyukov
vkostyukov Aug 8, 2013 Owner

Swap arguments:

eachInRow(int i, MatrixProcedure procedure);
@vkostyukov vkostyukov commented on an outdated diff Aug 8, 2013
src/main/java/org/la4j/matrix/AbstractSafeMatrix.java
@@ -408,6 +418,21 @@ public void each(MatrixProcedure procedure) {
}
@Override
+ public void eachInRow(MatrixProcedure procedure, int i) {
+ self.eachInRow(procedure, i);
+ }
+
+ @Override
+ public void eachInColumn(MatrixProcedure procedure, int j) {
@vkostyukov
vkostyukov Aug 8, 2013 Owner

Swap arguments:

eachInColumn(int j, MatrixProcedure procedure);
@vkostyukov vkostyukov commented on an outdated diff Aug 8, 2013
src/main/java/org/la4j/matrix/Matrix.java
@@ -600,6 +617,29 @@ Matrix slice(int fromRow, int fromColumn, int untilRow, int untilColumn,
void each(MatrixProcedure procedure);
/**
+ * Applies the <code>procedure</code> to every element in the
+ * <code>i</code> row of this matrix.
+ * @param procedure
+ * @param i
+ */
+ void eachInRow(MatrixProcedure procedure, int i);
@vkostyukov
vkostyukov Aug 8, 2013 Owner

Issue with arguments.

@vkostyukov vkostyukov commented on an outdated diff Aug 8, 2013
src/main/java/org/la4j/matrix/Matrix.java
@@ -600,6 +617,29 @@ Matrix slice(int fromRow, int fromColumn, int untilRow, int untilColumn,
void each(MatrixProcedure procedure);
/**
+ * Applies the <code>procedure</code> to every element in the
+ * <code>i</code> row of this matrix.
+ * @param procedure
+ * @param i
+ */
+ void eachInRow(MatrixProcedure procedure, int i);
+
+ /**
+ * Applies the <code>procedure</code> to every element in the
+ * <code>i</code> column of this matrix.
+ * @param procedure
+ * @param j
+ */
+ void eachInColumn(MatrixProcedure procedure, int j);
@vkostyukov
vkostyukov Aug 8, 2013 Owner

Issue with arguments order.

@vkostyukov vkostyukov commented on the diff Aug 8, 2013
src/main/java/org/la4j/matrix/sparse/CCSMatrix.java
@@ -286,6 +286,52 @@ public void each(MatrixProcedure procedure) {
}
@Override
+ public void each(MatrixProcedure procedure) {
@vkostyukov
vkostyukov Aug 8, 2013 Owner

We don't need this. There is each method in AbstractMatrix class that has the same logic. And we can't do it better than there.

@vkostyukov vkostyukov commented on an outdated diff Aug 8, 2013
src/main/java/org/la4j/matrix/sparse/CCSMatrix.java
+ int l = 0;
+ for (int i = 0; i < rows; i++) {
+ for (int j = 0; j < columns; j++) {
+ if (j == rowIndices[l]) {
+ procedure.apply(i,j,values[l++]);
+ }
+ else {
+ procedure.apply(i,j,0);
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public void eachInColumn(MatrixProcedure procedure, int j) {
@vkostyukov
vkostyukov Aug 8, 2013 Owner

For now, it would be enough to have eachInRow() in general implementation for sparse matrices. I mean, AbstractMatrix.eachInRow() should be enough. So, we don't need this concreet method for CCS.

@vkostyukov vkostyukov commented on an outdated diff Aug 8, 2013
src/main/java/org/la4j/matrix/sparse/CCSMatrix.java
+
+ @Override
+ public void eachInColumn(MatrixProcedure procedure, int j) {
+ int k = columnPointers[j];
+ for (int i = 0; i < columns; i++) {
+ if (i == rowIndices[k]) {
+ procedure.apply(i,j,values[k++]);
+ }
+ else {
+ procedure.apply(i,j,0);
+ }
+ }
+ }
+
+ @Override
+ public void eachInRow(MatrixProcedure procedure, int i) {
@vkostyukov
vkostyukov Aug 8, 2013 Owner

The same. I don't see the reason having this method in CCS. It would be enough to have in general.

@vkostyukov vkostyukov commented on the diff Aug 8, 2013
src/main/java/org/la4j/matrix/sparse/CRSMatrix.java
@@ -360,6 +360,52 @@ public void each(MatrixProcedure procedure) {
}
@Override
+ public void each(MatrixProcedure procedure) {
@vkostyukov
vkostyukov Aug 8, 2013 Owner

We don't need a separate implementation of each method for CRS matrix. We already have it in AbstractMatrix.

@SamoylovMD
SamoylovMD Aug 9, 2013 Contributor

I thought that implementation of getting an element in CRS and CCS matrices works longer than O(1). So, my implementation works for O(rows*columns) which is faster than method inherited from AbstractMatrix.

@vkostyukov
vkostyukov Aug 9, 2013 Owner

Right. The get/set works in O(log rows) for CCS and in O(log columns) for CRS. Probably, you're right. We might need each methods for CRS/CCS.

@vkostyukov vkostyukov commented on an outdated diff Aug 8, 2013
src/main/java/org/la4j/matrix/sparse/CRSMatrix.java
+ int l = 0;
+ for (int i = 0; i < rows; i++) {
+ for (int j = 0; j < columns; j++) {
+ if (j == columnIndices[l]) {
+ procedure.apply(i,j,values[l++]);
+ }
+ else {
+ procedure.apply(i,j,0);
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public void eachInRow(MatrixProcedure procedure, int i) {
@vkostyukov
vkostyukov Aug 8, 2013 Owner

We don't need this. We do have such method in AbstractMatrix.

@vkostyukov vkostyukov commented on an outdated diff Aug 8, 2013
src/main/java/org/la4j/matrix/sparse/CRSMatrix.java
+
+ @Override
+ public void eachInRow(MatrixProcedure procedure, int i) {
+ int k = rowPointers[i];
+ for (int j = 0; j < columns; j++) {
+ if (j == columnIndices[k]) {
+ procedure.apply(i,j,values[k++]);
+ }
+ else {
+ procedure.apply(i,j,0);
+ }
+ }
+ }
+
+ @Override
+ public void eachInColumn(MatrixProcedure procedure, int j) {
@vkostyukov
vkostyukov Aug 8, 2013 Owner

For now, it would be enough to have this in AbstractMatrix.

@vkostyukov
Owner

Hi Maxim @SamoylovMD,

Please also add eachNonZeroInRow() and eachNonZeroInColumn() implementations. And we can provide more efficient implementation of eachNonZeroInRow() for CRS matrix and eachNonZeroInColumn for CCS matrix. For other cases, it would be enough to have general implenetation in AbstractMatrix class.

Please, also follow my coments to improve the code.

@vkostyukov vkostyukov commented on an outdated diff Aug 9, 2013
src/main/java/org/la4j/matrix/AbstractMatrix.java
@@ -28,8 +28,10 @@
import java.util.Random;
import org.la4j.decomposition.MatrixDecompositor;
+import org.la4j.factory.Basic2DFactory;
@vkostyukov
vkostyukov Aug 9, 2013 Owner

Why do we need this?

@vkostyukov vkostyukov commented on an outdated diff Aug 9, 2013
src/main/java/org/la4j/matrix/AbstractMatrix.java
import org.la4j.factory.Factory;
import org.la4j.inversion.MatrixInvertor;
+import org.la4j.matrix.dense.Basic2DMatrix;
@vkostyukov
vkostyukov Aug 9, 2013 Owner

And this?

@vkostyukov vkostyukov and 1 other commented on an outdated diff Aug 9, 2013
src/main/java/org/la4j/matrix/sparse/CRSMatrix.java
+ @Override
+ public void eachInRow(int i, MatrixProcedure procedure) {
+ int k = rowPointers[i];
+ for (int j = 0; j < columns; j++) {
+ if (j == columnIndices[k]) {
+ procedure.apply(i,j,values[k++]);
+ }
+ else {
+ procedure.apply(i,j,0);
+ }
+ }
+ }
+
+ @Override
+ public void eachNonZeroInRow(int i, MatrixProcedure procedure) {
+ eachInRow(i,procedure);
@vkostyukov
vkostyukov Aug 9, 2013 Owner

I don't think this is correct. We need a separate implementation for non-zero elements here.

@SamoylovMD
SamoylovMD Aug 9, 2013 Contributor

Ofc, my error. Will rewrite in few seconds.

@SamoylovMD
SamoylovMD Aug 9, 2013 Contributor

Fixed in new commit.

@vkostyukov vkostyukov commented on an outdated diff Aug 10, 2013
src/main/java/org/la4j/matrix/AbstractMatrix.java
+ }
+ }
+
+ @Override
+ public void eachInColumn(int j,MatrixProcedure procedure) {
+ for (int i = 0; i < rows; i++) {
+ procedure.apply(i, j, get(i,j));
+ }
+ }
+
+ @Override
+ public void eachNonZero(MatrixProcedure procedure) {
+ for (int i = 0; i < rows; i++) {
+ for (int j = 0; j < columns; j++) {
+ if (Math.abs(get(i,j)) > Matrices.EPS) {
+ procedure.apply(i,j,get(i,j));
@vkostyukov
vkostyukov Aug 10, 2013 Owner

I would prefere to have spaces here. Like

procedure.apply(i, j, get(i, j));
@vkostyukov vkostyukov commented on an outdated diff Aug 10, 2013
src/main/java/org/la4j/matrix/AbstractMatrix.java
+
+ @Override
+ public void eachNonZero(MatrixProcedure procedure) {
+ for (int i = 0; i < rows; i++) {
+ for (int j = 0; j < columns; j++) {
+ if (Math.abs(get(i,j)) > Matrices.EPS) {
+ procedure.apply(i,j,get(i,j));
+ }
+ }
+ }
+ }
+
+ @Override
+ public void eachNonZeroInRow(int i, MatrixProcedure procedure) {
+ for (int j = 0; j < columns; j++) {
+ if (get(i, j) > Matrices.EPS) procedure.apply(i,j,get(i,j));
@vkostyukov
vkostyukov Aug 10, 2013 Owner

It seems Math.abs() is missed. What about negative values?
And also please, add {} arround the procedure.apply(...)

@vkostyukov vkostyukov commented on an outdated diff Aug 10, 2013
src/main/java/org/la4j/matrix/AbstractMatrix.java
+ }
+ }
+ }
+ }
+
+ @Override
+ public void eachNonZeroInRow(int i, MatrixProcedure procedure) {
+ for (int j = 0; j < columns; j++) {
+ if (get(i, j) > Matrices.EPS) procedure.apply(i,j,get(i,j));
+ }
+ }
+
+ @Override
+ public void eachNonZeroInColumn(int j, MatrixProcedure procedure) {
+ for (int i = 0; i < rows; i++) {
+ if (get(i, j) > Matrices.EPS) procedure.apply(i,j,get(i,j));
@vkostyukov
vkostyukov Aug 10, 2013 Owner

Again: missed Math.abs() and {} arround procedure.appy(...).

@vkostyukov vkostyukov commented on an outdated diff Aug 10, 2013
src/main/java/org/la4j/matrix/AbstractSafeMatrix.java
@@ -408,6 +418,31 @@ public void each(MatrixProcedure procedure) {
}
@Override
+ public void eachInRow(int i,MatrixProcedure procedure) {
+ self.eachInRow(i, procedure);
+ }
+
+ @Override
+ public void eachInColumn(int j,MatrixProcedure procedure) {
+ self.eachInColumn(j,procedure);
@vkostyukov
vkostyukov Aug 10, 2013 Owner

Need space: j, procedure.

@vkostyukov vkostyukov commented on an outdated diff Aug 10, 2013
src/main/java/org/la4j/matrix/AbstractSafeMatrix.java
+ self.eachInColumn(j,procedure);
+ }
+
+ @Override
+ public void eachNonZero(MatrixProcedure procedure) {
+ self.eachNonZero(procedure);
+ }
+
+ @Override
+ public void eachNonZeroInRow(int i, MatrixProcedure procedure) {
+ self.eachNonZeroInRow(i,procedure);
+ }
+
+ @Override
+ public void eachNonZeroInColumn(int j, MatrixProcedure procedure) {
+ self.eachNonZeroInColumn(j,procedure);
@vkostyukov
vkostyukov Aug 10, 2013 Owner

Need space: j, procedure.

@vkostyukov vkostyukov commented on an outdated diff Aug 10, 2013
src/main/java/org/la4j/matrix/sparse/CCSMatrix.java
@@ -286,6 +286,44 @@ public void each(MatrixProcedure procedure) {
}
@Override
+ public void each(MatrixProcedure procedure) {
+ int l = 0;
@vkostyukov
vkostyukov Aug 10, 2013 Owner

Why didn't you call it k instead of l. I mean, in the eachInColumn it's called k.

@vkostyukov vkostyukov commented on the diff Aug 10, 2013
src/main/java/org/la4j/matrix/sparse/CCSMatrix.java
+
+ @Override
+ public void eachInColumn(int j, MatrixProcedure procedure) {
+ int k = columnPointers[j];
+ for (int i = 0; i < columns; i++) {
+ if (i == rowIndices[k]) {
+ procedure.apply(i,j,values[k++]);
+ }
+ else {
+ procedure.apply(i,j,0);
+ }
+ }
+ }
+
+ @Override
+ public void eachNonZeroInColumn(int j, MatrixProcedure procedure) {
@vkostyukov
vkostyukov Aug 10, 2013 Owner

What about having a simple for loop here. Like:

for (int i = columnPointers[k]; i < columnPointers[j + 1]; i++) {
    procedure.apply(rowIndices[i], j, values[i]);
}

There is something wrong with your code. First, i = rowIndicies[k]. And then you're getting row by rowIndicies[i]. This is strange. I guarantee it will fail tests.

@SamoylovMD
SamoylovMD Aug 10, 2013 Contributor

But this loop

for (int i = columnPointers[k]; i < columnPointers[j + 1]; i++) {
    procedure.apply(rowIndices[i], j, values[i]);
}

will apply procedure to non-zero values column only, won't it? Your implementation looks like mine in CCSMatrix.eachNonZeroInColumn

@vkostyukov vkostyukov commented on an outdated diff Aug 10, 2013
src/main/java/org/la4j/matrix/sparse/CRSMatrix.java
@@ -360,6 +360,44 @@ public void each(MatrixProcedure procedure) {
}
@Override
+ public void each(MatrixProcedure procedure) {
+ int l = 0;
@vkostyukov
vkostyukov Aug 10, 2013 Owner

Let's rename it to k.

@vkostyukov vkostyukov commented on an outdated diff Aug 10, 2013
src/main/java/org/la4j/matrix/sparse/CRSMatrix.java
+ @Override
+ public void eachInRow(int i, MatrixProcedure procedure) {
+ int k = rowPointers[i];
+ for (int j = 0; j < columns; j++) {
+ if (j == columnIndices[k]) {
+ procedure.apply(i,j,values[k++]);
+ }
+ else {
+ procedure.apply(i,j,0);
+ }
+ }
+ }
+
+ @Override
+ public void eachNonZeroInRow(int i, MatrixProcedure procedure) {
+ int k = rowPointers[i], j = columnIndices[k];
@vkostyukov
vkostyukov Aug 10, 2013 Owner

Again strange loop. We need something like this:

for (int j = rowPointers[i]; j < rowPointers[j + 1]; j++) {
    procedure.apply(i, columnIndices[j], values[j]);
}
@vkostyukov
Owner

So, I believe the best way to make sure that all implementations are correct - is to write tests. Could you please write simple tess for all the methods (esp. eachIn.. and eachNonZeroIn...) in AbstractMatrixTest class.

Here is some ideas how to test it. We can write simple matrix procedure that dumps row into double array and then check it. Like this:

public static class RowDumper implements MatrixProcedure {
  double data[];
  public Dumper(int n) {
    data = new double[n];
  }

  public apply(int i, int j, double) {
    data[j] = value;
  }
}

Matrix a = new CRSMatrix(...);
RowDumper d = new RowDumper(a.columns());
a.eachInRow(0, d);
// check whether the d.data is correct
a.eachNonZeroInRow(1, d);
// check whether the d.data is correct
@SamoylovMD
Contributor

EachInRow works correct, EachNonZeroInRow needs correction.

@vkostyukov vkostyukov commented on an outdated diff Aug 10, 2013
src/main/java/org/la4j/matrix/sparse/CRSMatrix.java
+ public void eachInRow(int i, MatrixProcedure procedure) {
+ int k = rowPointers[i];
+ for (int j = 0; j < columns; j++) {
+ if (j == columnIndices[k]) {
+ procedure.apply(i, j, values[k]);
+ k++;
+ }
+ else {
+ procedure.apply(i, j, 0);
+ }
+ }
+ }
+
+ @Override
+ public void eachNonZeroInRow(int i, MatrixProcedure procedure) {
+ int k = rowPointers[i], j = k;
@vkostyukov
vkostyukov Aug 10, 2013 Owner

So, now it looks correct, but I still don't see the reason of having this k variable. For what it is?
And I would prefer having a for loop like (equivalent to your while loop but much clear):

for (int j = rowPointers[i]; j < rowPointers[i + 1]; j++) {
  procedure.apply(i, columnIndices[j], values[j]);
}
@vkostyukov vkostyukov commented on an outdated diff Aug 10, 2013
src/main/java/org/la4j/matrix/sparse/CCSMatrix.java
+ public void eachInColumn(int j, MatrixProcedure procedure) {
+ int k = columnPointers[j];
+ for (int i = 0; i < columns; i++) {
+ if (i == rowIndices[k]) {
+ procedure.apply(i, j, values[k]);
+ k++;
+ }
+ else {
+ procedure.apply(i, j, 0);
+ }
+ }
+ }
+
+ @Override
+ public void eachNonZeroInColumn(int j, MatrixProcedure procedure) {
+ int k = columnPointers[j], i = k;
@vkostyukov
vkostyukov Aug 10, 2013 Owner

So, now it looks correct, but I still don't see the reason of having this k variable. For what it is?
And I would prefer having a for loop like (equivalent to your while loop but much clear):

for (int i = columnPointers[j]; i < columnPointers[j + 1]; i++) {
  procedure.apply(rowIndices[i], j, values[i]);
}
SamoylovMD added some commits Aug 10, 2013
@SamoylovMD SamoylovMD Update CCSMatrix.java
Refactored eachNonZeroInColumn method
4b7a0c9
@SamoylovMD SamoylovMD Update CCSMatrix.java
Fixed error
3acbe38
@SamoylovMD SamoylovMD Update CRSMatrix.java
Refactored eachNonZeroInRow method
da5f8fb
@vkostyukov
Owner

Thank you Maxim @SamoylovMD!

@vkostyukov vkostyukov merged commit c7b3843 into vkostyukov:master Aug 10, 2013

1 check passed

default The Travis CI build passed
Details
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment