This is an automated email from the ASF dual-hosted git repository.

baunsgaard pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/systemds.git


The following commit(s) were added to refs/heads/main by this push:
     new e11d20a59a [SYSTEMDS-3526] Transpose dense to sparse
e11d20a59a is described below

commit e11d20a59a0a2b8cb09717243c179803429c1768
Author: baunsgaard <[email protected]>
AuthorDate: Fri Apr 21 12:33:30 2023 +0200

    [SYSTEMDS-3526] Transpose dense to sparse
    
    Before: single thread transpose dense to sparse dominated:
    census_enc_16k-kmeans+-claWorkloadb16 -- dams-so001
    r'      15.085    112
    
    I removed an indirection of allocation via append on MCSR and
    managed the sparse vectors directly:
    
    r'       9.334    112
    
    And finally parallelized:
    r'       4.454    112
    
    Furthermore this commit extends the component transpose tests
    to verify behavior in various parameterized scenarios.
    
    Closes #1809
---
 .../colgroup/dictionary/MatrixBlockDictionary.java |   6 +-
 .../compress/readers/ReaderColumnSelection.java    |  17 +-
 .../readers/ReaderColumnSelectionEmpty.java        |  36 ++++
 .../sysds/runtime/matrix/data/LibMatrixReorg.java  | 187 ++++++++++++++-------
 .../component/compress/readers/ReadersTest.java    |   2 +-
 .../readers/ReadersTestCompareReaders.java         |  49 ++++--
 .../test/component/matrix/TransposeCSRTest.java    |   1 -
 .../test/component/matrix/TransposeTwiceTest.java  | 129 ++++++++++++++
 8 files changed, 348 insertions(+), 79 deletions(-)

diff --git 
a/src/main/java/org/apache/sysds/runtime/compress/colgroup/dictionary/MatrixBlockDictionary.java
 
b/src/main/java/org/apache/sysds/runtime/compress/colgroup/dictionary/MatrixBlockDictionary.java
index 7126f8e532..a1b11eaf29 100644
--- 
a/src/main/java/org/apache/sysds/runtime/compress/colgroup/dictionary/MatrixBlockDictionary.java
+++ 
b/src/main/java/org/apache/sysds/runtime/compress/colgroup/dictionary/MatrixBlockDictionary.java
@@ -318,8 +318,10 @@ public class MatrixBlockDictionary extends ADictionary {
        public void aggregateCols(double[] c, Builtin fn, IColIndex colIndexes) 
{
                if(_data.isInSparseFormat()) {
                        MatrixBlock t = LibMatrixReorg.transpose(_data);
-                       if(!t.isInSparseFormat()) // highly unlikely.
-                               throw new NotImplementedException("Not 
implemented aggregate Cols on dense transposed dict.");
+                       if(!t.isInSparseFormat()){
+                               LOG.warn("Transpose for aggregating of 
columns");
+                               t.denseToSparse(true);
+                       } 
 
                        SparseBlock sbt = t.getSparseBlock();
 
diff --git 
a/src/main/java/org/apache/sysds/runtime/compress/readers/ReaderColumnSelection.java
 
b/src/main/java/org/apache/sysds/runtime/compress/readers/ReaderColumnSelection.java
index 019e1e2380..8f5661ee93 100644
--- 
a/src/main/java/org/apache/sysds/runtime/compress/readers/ReaderColumnSelection.java
+++ 
b/src/main/java/org/apache/sysds/runtime/compress/readers/ReaderColumnSelection.java
@@ -36,6 +36,7 @@ public abstract class ReaderColumnSelection {
        protected final IColIndex _colIndexes;
        protected final DblArray reusableReturn;
        protected final double[] reusableArr;
+       /** The row index to stop the reading at */
        protected final int _ru;
 
        /** rl is used as a pointer to current row */
@@ -76,11 +77,14 @@ public abstract class ReaderColumnSelection {
                return createReader(rawBlock, colIndices, transposed, rl, ru);
        }
 
-
-       public static ReaderColumnSelection createReader(MatrixBlock rawBlock, 
IColIndex colIndices, boolean transposed, int rl,
-               int ru) {
+       public static ReaderColumnSelection createReader(MatrixBlock rawBlock, 
IColIndex colIndices, boolean transposed,
+               int rl, int ru) {
                checkInput(rawBlock, colIndices, rl, ru);
                rl = rl - 1;
+               if(rawBlock.isEmpty()) {
+                       LOG.warn("It is likely an error occurred when reading 
an empty block. But we do support it!");
+                       return new ReaderColumnSelectionEmpty(rawBlock, 
colIndices, rl, ru, transposed);
+               }
 
                if(transposed) {
                        if(rawBlock.isInSparseFormat())
@@ -99,15 +103,16 @@ public abstract class ReaderColumnSelection {
 
        private static void checkInput(final MatrixBlock rawBlock, final 
IColIndex colIndices, final int rl, final int ru) {
                if(colIndices.size() <= 1)
-                       throw new DMLCompressionException("Column selection 
reader should not be done on single column groups: " + colIndices);
+                       throw new DMLCompressionException(
+                               "Column selection reader should not be done on 
single column groups: " + colIndices);
                else if(rawBlock.getSparseBlock() == null && 
rawBlock.getDenseBlock() == null)
                        throw new DMLCompressionException("Input Block was 
null");
                else if(rl >= ru)
                        throw new DMLCompressionException("Invalid inverse 
range for reader " + rl + " to " + ru);
        }
 
-       protected void warnNaN(){
-               if(!nanEncountered){
+       protected void warnNaN() {
+               if(!nanEncountered) {
                        LOG.warn("NaN value encountered, replaced by 0 in 
compression, since nan is not supported");
                        nanEncountered = true;
                }
diff --git 
a/src/main/java/org/apache/sysds/runtime/compress/readers/ReaderColumnSelectionEmpty.java
 
b/src/main/java/org/apache/sysds/runtime/compress/readers/ReaderColumnSelectionEmpty.java
new file mode 100644
index 0000000000..c428df77d6
--- /dev/null
+++ 
b/src/main/java/org/apache/sysds/runtime/compress/readers/ReaderColumnSelectionEmpty.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sysds.runtime.compress.readers;
+
+import org.apache.sysds.runtime.compress.colgroup.indexes.IColIndex;
+import org.apache.sysds.runtime.compress.utils.DblArray;
+import org.apache.sysds.runtime.matrix.data.MatrixBlock;
+
+public class ReaderColumnSelectionEmpty extends ReaderColumnSelection {
+
+       protected ReaderColumnSelectionEmpty(MatrixBlock data, IColIndex 
colIndices, int rl, int ru, boolean transposed) {
+               super(colIndices, rl, Math.min(ru, 1 - (transposed ? 
data.getNumColumns() : data.getNumRows())));
+       }
+
+       protected DblArray getNextRow() {
+               _rl = _ru;
+               return null;
+       }
+}
diff --git 
a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixReorg.java 
b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixReorg.java
index 2651327957..49bdc7145f 100644
--- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixReorg.java
+++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixReorg.java
@@ -35,6 +35,8 @@ import java.util.concurrent.Future;
 import java.util.stream.Collectors;
 
 import org.apache.commons.lang.NotImplementedException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.apache.sysds.runtime.DMLRuntimeException;
 import org.apache.sysds.runtime.compress.CompressedMatrixBlock;
 import org.apache.sysds.runtime.compress.DMLCompressionException;
@@ -43,6 +45,7 @@ import org.apache.sysds.runtime.data.DenseBlock;
 import org.apache.sysds.runtime.data.DenseBlockFactory;
 import org.apache.sysds.runtime.data.SparseBlock;
 import org.apache.sysds.runtime.data.SparseBlockCSR;
+import org.apache.sysds.runtime.data.SparseBlockMCSR;
 import org.apache.sysds.runtime.data.SparseRowVector;
 import org.apache.sysds.runtime.functionobjects.DiagIndex;
 import org.apache.sysds.runtime.functionobjects.RevIndex;
@@ -71,7 +74,7 @@ import org.apache.sysds.runtime.util.UtilFunctions;
  */
 public class LibMatrixReorg {
 
-       // private static final Log LOG = 
LogFactory.getLog(LibMatrixReorg.class.getName());
+       protected static final Log LOG = 
LogFactory.getLog(LibMatrixReorg.class.getName());
 
        //minimum number of elements for multi-threaded execution
        public static long PAR_NUMCELL_THRESHOLD = 1024*1024; //1M
@@ -145,8 +148,12 @@ public class LibMatrixReorg {
                }
        }
 
-       public static MatrixBlock transpose(MatrixBlock in){
-               return transpose(in, new MatrixBlock(in.getNumColumns(), 
in.getNumRows(), in.isInSparseFormat()));
+       public static MatrixBlock transpose(MatrixBlock in) {
+               final int clen = in.getNumColumns();
+               final int rlen = in.getNumRows();
+               final long nnz = in.getNonZeros();
+               final boolean sparseOut = 
MatrixBlock.evalSparseFormatInMemory(clen, rlen, nnz, true);
+               return transpose(in, new MatrixBlock(clen, rlen, sparseOut));
        }
 
        public static MatrixBlock transpose( MatrixBlock in, MatrixBlock out ) {
@@ -195,28 +202,43 @@ public class LibMatrixReorg {
                return out;
        }
 
-       public static MatrixBlock transpose(MatrixBlock in, int k){
-               return transpose(in, new MatrixBlock(in.getNumColumns(), 
in.getNumRows(), in.isInSparseFormat()), k);
+       public static MatrixBlock transpose(MatrixBlock in, int k) {
+               final int clen = in.getNumColumns();
+               final int rlen = in.getNumRows();
+               final long nnz = in.getNonZeros();
+               final boolean sparseOut = 
MatrixBlock.evalSparseFormatInMemory(clen, rlen, nnz, true);
+               return transpose(in, new MatrixBlock(clen, rlen, sparseOut), k);
        }
-       
+
        public static MatrixBlock transpose( MatrixBlock in, MatrixBlock out, 
int k ) {
                return transpose(in, out, k, false);
        }
        
        public static MatrixBlock transpose(MatrixBlock in, MatrixBlock out, 
int k, boolean allowCSR) {
                // redirect small or special cases to sequential execution
-               if(in.isEmptyBlock(false) || ((long) in.rlen * (long) in.clen < 
PAR_NUMCELL_THRESHOLD) || k == 1
-                       || (SHALLOW_COPY_REORG && !in.sparse && !out.sparse && 
(in.rlen == 1 || in.clen == 1))
-                       || (in.sparse && !out.sparse && in.rlen == 1) || 
(!in.sparse && out.sparse && in.rlen == 1)
-                       || (in.sparse && out.sparse && in.nonZeros < 
Math.max(in.rlen, in.clen)) //ultra-sparse
-                       || (!in.sparse && out.sparse) ) {
+               if(in.isEmptyBlock(false) //
+                       || ((long) in.rlen * (long) in.clen < 
PAR_NUMCELL_THRESHOLD) //
+                       || k <= 1 //
+                       || (SHALLOW_COPY_REORG && !in.sparse && !out.sparse && 
(in.rlen == 1 || in.clen == 1)) //
+                       || (in.sparse && !out.sparse && in.rlen == 1) //
+                       || (!in.sparse && out.sparse && in.rlen == 1) //
+                       || (in.sparse && out.sparse && in.nonZeros < 
Math.max(in.rlen, in.clen)) // ultra-sparse
+               ) {
                        return transpose(in, out);
                }
                // set meta data and allocate output arrays (if required)
                out.nonZeros = in.nonZeros;
+
+               if(!in.sparse && out.sparse){
+                       // special case dense to sparse is different than 
others because appending to sparse rows.
+                       transposeDenseToSparse(in, out, k);
+                       return out;
+               }
+
+               // Timing time = new Timing(true);
+
                // CSR is only allowed in the transposed output if the number 
of non zeros is counted in the columns
                allowCSR = allowCSR && out.nonZeros < (long) Integer.MAX_VALUE 
&& in.clen <= 4096;
-               // Timing time = new Timing(true);
 
                if(out.sparse && allowCSR) {
                        int size = (int) out.nonZeros;
@@ -229,7 +251,7 @@ public class LibMatrixReorg {
 
                // core multi-threaded transpose
                try {
-                       ExecutorService pool = CommonThreadPool.get(k);
+                       final ExecutorService pool = CommonThreadPool.get(k);
                        // pre-processing (compute nnz per column once for 
sparse)
                        int[] cnt = null;
                        // filter matrices with many columns since the 
CountNnzTask would return
@@ -272,17 +294,13 @@ public class LibMatrixReorg {
                return out;
        }
 
-       public static int[] countNNZColumns(MatrixBlock in, int k, 
ExecutorService pool) {
-               try {
-                       int[] cnt = null;
-                       List<Future<int[]>> rtasks = 
countNNZColumnsFuture(in,k,pool);
-                       for(Future<int[]> rtask : rtasks)
-                               cnt = mergeNnzCounts(cnt, rtask.get());
-                       return cnt;
-               }
-               catch(InterruptedException | ExecutionException e) {
-                       throw new DMLRuntimeException(e);
-               }
+       public static int[] countNNZColumns(MatrixBlock in, int k, 
ExecutorService pool)
+               throws InterruptedException, ExecutionException {
+               int[] cnt = null;
+               List<Future<int[]>> rtasks = countNNZColumnsFuture(in, k, pool);
+               for(Future<int[]> rtask : rtasks)
+                       cnt = mergeNnzCounts(cnt, rtask.get());
+               return cnt;
        }
 
        public static List<Future<int[]>> countNNZColumnsFuture(MatrixBlock in, 
int k, ExecutorService pool) throws InterruptedException {
@@ -300,7 +318,7 @@ public class LibMatrixReorg {
                        out = new MatrixBlock(in.getNumColumns(), 
in.getNumRows(), true);
                else if(in.isInSparseFormat()) {
                        // If input is sparse use default implementation and 
allocate a new matrix.
-                       out = transpose(in, new MatrixBlock(in.getNumColumns(), 
in.getNumRows(), true), k);
+                       out = transpose(in, new MatrixBlock(in.getNumColumns(), 
in.getNumRows(), true), k, true);
                }
                else {
                        transposeInPlaceDense(in, k);
@@ -918,49 +936,104 @@ public class LibMatrixReorg {
                }
        }
 
-       private static void transposeDenseToSparse(MatrixBlock in, MatrixBlock 
out)
-       {
-               //NOTE: called only in sequential execution
-               
+       private static void transposeDenseToSparse(MatrixBlock in, MatrixBlock 
out){
+               transposeDenseToSparse(in, out, 1);
+       }
+
+       private static void transposeDenseToSparse(MatrixBlock in, MatrixBlock 
out, int k){
+               if( out.rlen == 1 ) 
+                       transposeDenseToSparseVV(in,out);
+               else
+                       transposeDenseToSparseMM(in, out, k);
+       }
+
+       private static void transposeDenseToSparseVV(MatrixBlock in, 
MatrixBlock out){
+               final int m = in.rlen;
+               final DenseBlock a = in.getDenseBlock();
+               out.allocateSparseRowsBlock(false);
+               final SparseBlock c = out.getSparseBlock();
+               c.set(0, new SparseRowVector((int)in.nonZeros, a.valuesAt(0), 
m), false);
+       }
+
+       private static void transposeDenseToSparseMM(MatrixBlock in, 
MatrixBlock out, int k){
                final int m = in.rlen;
                final int n = in.clen;
                final int m2 = out.rlen;
                final int n2 = out.clen;
                final int ennz2 = (int) (in.nonZeros/m2); 
-               
-               DenseBlock a = in.getDenseBlock();
-               SparseBlock c = out.getSparseBlock();
-               
-               if( out.rlen == 1 ) //VECTOR-VECTOR
-               {
-                       //allocate row once by nnz, copy non-zeros
-                       c.set(0, new SparseRowVector((int)in.nonZeros, 
a.valuesAt(0), m), false);
+               final DenseBlock a = in.getDenseBlock();
+
+               final SparseRowVector[] rows = new SparseRowVector[m2];
+               for(int j = 0; j < m2; j++)
+                       rows[j] = new SparseRowVector(ennz2, n2);
+
+               if(k <= 1)
+                       transposeDenseToSparseMMRange(a, rows, 0, m, 0, n);
+               else {
+                       final ExecutorService pool = CommonThreadPool.get(k);
+                       try {
+                               final ArrayList<TransposeDenseToSparseTask> 
tasks = new ArrayList<>();
+                               final int rbz = Math.max(1, m2 / k);
+                               for(int i = 0; i < m2; i += rbz)
+                                       tasks.add(new 
TransposeDenseToSparseTask(a, rows, 0, m, i, Math.min(i + rbz, n)));
+                               for(Future<Object> task : pool.invokeAll(tasks))
+                                       task.get();
+                               pool.shutdown();
+                       }
+                       catch(Exception ex) {
+                               pool.shutdown();
+                               throw new DMLRuntimeException(ex);
+                       }
                }
-               else //general case: MATRIX-MATRIX
-               {
-                       //blocking according to typical L2 cache sizes 
-                       final int blocksizeI = 128;
-                       final int blocksizeJ = 128;
-                       
-                       //blocked execution
-                       for( int bi = 0; bi<m; bi+=blocksizeI ) {
-                               int bimin = Math.min(bi+blocksizeI, m);
-                               for( int bj = 0; bj<n; bj+=blocksizeJ ) {
-                                       int bjmin = Math.min(bj+blocksizeJ, n);
-                                       //core transpose operation
-                                       for( int i=bi; i<bimin; i++ ) {
-                                               double[] avals = a.values(i);
-                                               int aix = a.pos(i);
-                                               for( int j=bj; j<bjmin; j++ ) {
-                                                       c.allocate(j, ennz2, 
n2); 
-                                                       c.append(j, i, 
avals[aix+j]);
-                                               }
-                                       }
+
+               SparseBlock c = new SparseBlockMCSR(rows, false);
+               out.setSparseBlock(c);
+       }
+
+       private static void transposeDenseToSparseMMRange(DenseBlock a, 
SparseRowVector[] rows, int rl, int ru, int cl,
+               int cu) {
+               // blocking according to typical L2 cache sizes
+               final int blocksizeI = 128;
+               final int blocksizeJ = 128;
+               for(int bi = rl; bi < ru; bi += blocksizeI) {
+                       final int bimin = Math.min(bi + blocksizeI, ru);
+                       for(int bj = cl; bj < cu; bj += blocksizeJ) {
+                               final int bjmin = Math.min(bj + blocksizeJ, cu);
+                               // core transpose operation
+                               for(int i = bi; i < bimin; i++) {
+                                       final double[] avals = a.values(i);
+                                       final int aix = a.pos(i);
+                                       for(int j = bj; j < bjmin; j++)
+                                               rows[j].append(i, avals[aix + 
j]);
                                }
                        }
                }
        }
 
+       private static class TransposeDenseToSparseTask implements 
Callable<Object> {
+               private DenseBlock a;
+               private SparseRowVector[] rows;
+               private int rl;
+               private int ru;
+               private int cl;
+               private int cu;
+
+               protected TransposeDenseToSparseTask(DenseBlock a, 
SparseRowVector[] rows, int rl, int ru, int cl, int cu) {
+                       this.a = a;
+                       this.rows = rows;
+                       this.rl = rl;
+                       this.ru = ru;
+                       this.cl = cl;
+                       this.cu = cu;
+               }
+
+               @Override
+               public Object call() {
+                       transposeDenseToSparseMMRange(a, rows, rl, ru, cl, cu);
+                       return null;
+               }
+       }
+
        private static void transposeUltraSparse(MatrixBlock in, MatrixBlock 
out) {
                //note: applied if nnz < max(rlen, clen) - so no cache blocking
                // but basic, naive transposition in a single-threaded context
diff --git 
a/src/test/java/org/apache/sysds/test/component/compress/readers/ReadersTest.java
 
b/src/test/java/org/apache/sysds/test/component/compress/readers/ReadersTest.java
index b7aeb9cd09..fa1db91818 100644
--- 
a/src/test/java/org/apache/sysds/test/component/compress/readers/ReadersTest.java
+++ 
b/src/test/java/org/apache/sysds/test/component/compress/readers/ReadersTest.java
@@ -41,7 +41,7 @@ public class ReadersTest {
        @Test(expected = DMLCompressionException.class)
        public void testDenseSingleCol() {
                MatrixBlock mb = TestUtils.generateTestMatrixBlock(10, 1, 1, 1, 
0.5, 21342);
-               ReaderColumnSelection.createReader(mb,ColIndexFactory.create( 
1), false);
+               ReaderColumnSelection.createReader(mb, 
ColIndexFactory.create(1), false);
        }
 
        @Test
diff --git 
a/src/test/java/org/apache/sysds/test/component/compress/readers/ReadersTestCompareReaders.java
 
b/src/test/java/org/apache/sysds/test/component/compress/readers/ReadersTestCompareReaders.java
index 57f5d7866f..7f5d7990cf 100644
--- 
a/src/test/java/org/apache/sysds/test/component/compress/readers/ReadersTestCompareReaders.java
+++ 
b/src/test/java/org/apache/sysds/test/component/compress/readers/ReadersTestCompareReaders.java
@@ -117,7 +117,13 @@ public class ReadersTestCompareReaders {
                        sm = m2;
                }
 
+               if(!sm.isInSparseFormat())
+                       sm.denseToSparse(true);
+               if(m.isInSparseFormat())
+                       m.sparseToDense();
+
                MatrixBlock tin = LibMatrixReorg.transpose(in);
+
                MatrixBlock m2t = new MatrixBlock();
                m2t.copy(tin);
                final boolean isSparseTIn = in.isInSparseFormat();
@@ -132,11 +138,22 @@ public class ReadersTestCompareReaders {
                        tsm = m2t;
                }
 
+               if(tm.isInSparseFormat())
+                       tm.sparseToDense();
+               if(!tsm.isInSparseFormat())
+                       tsm.denseToSparse(true);
+
+               MatrixBlock tmd = new MatrixBlock();
+               tmd.copy(tm);
+               tmd.sparseToDense();
+
                mMockLarge = new MatrixBlock(m.getNumRows(), m.getNumColumns(),
                        new DenseBlockFP64Mock(new int[] {m.getNumRows(), 
m.getNumColumns()}, m.getDenseBlockValues()));
+               mMockLarge.setNonZeros(m.getNonZeros());
 
-               mMockLargeTransposed = new MatrixBlock(tm.getNumRows(), 
tm.getNumColumns(),
-                       new DenseBlockFP64Mock(new int[] {tm.getNumRows(), 
tm.getNumColumns()}, tm.getDenseBlockValues()));
+               mMockLargeTransposed = new MatrixBlock(tmd.getNumRows(), 
tmd.getNumColumns(),
+                       new DenseBlockFP64Mock(new int[] {tmd.getNumRows(), 
tmd.getNumColumns()}, tmd.getDenseBlockValues()));
+               mMockLargeTransposed.setNonZeros(tm.getNonZeros());
 
        }
 
@@ -144,7 +161,8 @@ public class ReadersTestCompareReaders {
        public void testCompareSparseDense() {
                ReaderColumnSelection a = ReaderColumnSelection.createReader(m, 
cols, false);
                ReaderColumnSelection b = 
ReaderColumnSelection.createReader(sm, cols, false);
-               if(b instanceof ReaderColumnSelectionSparse && a instanceof 
ReaderColumnSelectionDenseSingleBlock)
+               if((b instanceof ReaderColumnSelectionSparse && a instanceof 
ReaderColumnSelectionDenseSingleBlock) ||
+                       (m.isEmpty() || sm.isEmpty()))
                        compareReaders(a, b);
                else
                        fail("Incorrect type of reader");
@@ -202,7 +220,7 @@ public class ReadersTestCompareReaders {
        public void testCompareDenseTransposedDense() {
                ReaderColumnSelection a = ReaderColumnSelection.createReader(m, 
cols, false);
                ReaderColumnSelection b = 
ReaderColumnSelection.createReader(tm, cols, true);
-               if(b instanceof ReaderColumnSelectionDenseSingleBlockTransposed)
+               if((b instanceof 
ReaderColumnSelectionDenseSingleBlockTransposed) || (m.isEmpty() || 
tm.isEmpty()))
                        compareReaders(a, b);
                else
                        fail("Incorrect type of reader");
@@ -248,7 +266,7 @@ public class ReadersTestCompareReaders {
        public void testCompareDenseTransposedSparse() {
                ReaderColumnSelection a = ReaderColumnSelection.createReader(m, 
cols, false);
                ReaderColumnSelection b = 
ReaderColumnSelection.createReader(tsm, cols, true);
-               if(b instanceof ReaderColumnSelectionSparseTransposed)
+               if((b instanceof ReaderColumnSelectionSparseTransposed) || 
(m.isEmpty() || tsm.isEmpty()))
                        compareReaders(a, b);
                else
                        fail("Incorrect type of reader");
@@ -473,13 +491,20 @@ public class ReadersTestCompareReaders {
 
        @Test
        public void testCompareDenseTransposedLargeFewRowsFromEnd() {
-               final int nRow = m.getNumRows();
-               if(nRow > 30) {
-                       final int end = m.getNumRows() - 10;
-                       final int start = end - 10;
-                       ReaderColumnSelection a = 
ReaderColumnSelection.createReader(m, cols, false, start, end);
-                       ReaderColumnSelection b = 
ReaderColumnSelection.createReader(mMockLargeTransposed, cols, true, start, 
end);
-                       compareReaders(a, b, start, end);
+               try {
+
+                       final int nRow = m.getNumRows();
+                       if(nRow > 30) {
+                               final int end = m.getNumRows() - 10;
+                               final int start = end - 10;
+                               ReaderColumnSelection a = 
ReaderColumnSelection.createReader(m, cols, false, start, end);
+                               ReaderColumnSelection b = 
ReaderColumnSelection.createReader(mMockLargeTransposed, cols, true, start, 
end);
+                               compareReaders(a, b, start, end);
+                       }
+               }
+               catch(Exception e) {
+                       e.printStackTrace();
+                       fail(e.getMessage());
                }
        }
 
diff --git 
a/src/test/java/org/apache/sysds/test/component/matrix/TransposeCSRTest.java 
b/src/test/java/org/apache/sysds/test/component/matrix/TransposeCSRTest.java
index 875667b726..9f4ff33719 100644
--- a/src/test/java/org/apache/sysds/test/component/matrix/TransposeCSRTest.java
+++ b/src/test/java/org/apache/sysds/test/component/matrix/TransposeCSRTest.java
@@ -79,7 +79,6 @@ public class TransposeCSRTest {
        @Test
        public void testEstimation() {
                try {
-
                        LibMatrixReorg.PAR_NUMCELL_THRESHOLD = 100;
                        MatrixBlock ret1 = LibMatrixReorg.transpose(in,
                                new MatrixBlock(in.getNumColumns(), 
in.getNumRows(), in.isInSparseFormat()), k, false);
diff --git 
a/src/test/java/org/apache/sysds/test/component/matrix/TransposeTwiceTest.java 
b/src/test/java/org/apache/sysds/test/component/matrix/TransposeTwiceTest.java
new file mode 100644
index 0000000000..6bae3f83ff
--- /dev/null
+++ 
b/src/test/java/org/apache/sysds/test/component/matrix/TransposeTwiceTest.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sysds.test.component.matrix;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.sysds.runtime.matrix.data.LibMatrixReorg;
+import org.apache.sysds.runtime.matrix.data.MatrixBlock;
+import org.apache.sysds.test.TestUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(value = Parameterized.class)
[email protected]
+public class TransposeTwiceTest {
+       protected static final Log LOG = 
LogFactory.getLog(TransposeTwiceTest.class.getName());
+
+       long old = LibMatrixReorg.PAR_NUMCELL_THRESHOLD;
+
+       @Parameters
+       public static Collection<Object[]> data() {
+               ArrayList<Object[]> tests = new ArrayList<>();
+
+               try {
+                       double[] sparsities = new double[] {0.0, 0.001, 0.1, 
0.2, 0.35, 0.5, 1.0};
+                       int[] rowsCols = new int[] {1, 2, 4, 5, 10, 30, 50, 
100, 300};
+                       int[] parallelization = new int[] {-1, 1, 5};
+
+                       // smaller cases
+                       for(int rows : rowsCols) {
+                               for(int cols : rowsCols) {
+                                       for(double sp : sparsities) {
+                                               MatrixBlock mb = 
TestUtils.generateTestMatrixBlock(rows, cols, -10, 10, sp, 42);
+                                               mb.examSparsity(false);
+                                               for(int k1 : parallelization) {
+                                                       for(int k2 : 
parallelization) {
+                                                               tests.add(new 
Object[] {mb, k1, k2});
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+
+                       // Larger edge case
+                       MatrixBlock mb;
+                       rowsCols = new int[] {1, 2, 5, 10};
+                       for(int rows : rowsCols) {
+                               for(double sp : sparsities) {
+                                       if(sp > 0.4)
+                                               continue;
+                                       mb = 
TestUtils.generateTestMatrixBlock(rows, 5000, -10, 10, sp, 43);
+                                       tests.add(new Object[] {mb, 16, 16});
+                               }
+                       }
+               }
+               catch(Exception e) {
+                       fail(e.getMessage());
+               }
+               return tests;
+       }
+
+       @Parameterized.Parameter
+       public MatrixBlock in;
+       @Parameterized.Parameter(1)
+       public int k1;
+       @Parameterized.Parameter(2)
+       public int k2;
+
+       @Test
+       public void testTransposeBackAndForth() {
+               try {
+                       LibMatrixReorg.PAR_NUMCELL_THRESHOLD = 100;
+
+                       MatrixBlock inT = LibMatrixReorg.transpose(in, k1);
+                       MatrixBlock inTT = LibMatrixReorg.transpose(inT, k2);
+
+                       TestUtils.compareMatricesBitAvgDistance(in, inTT, 0, 0, 
"Not equal transpose result");
+                       assertEquals(in.getNonZeros(), inT.getNonZeros());
+                       assertEquals(in.getNonZeros(), inTT.getNonZeros());
+                       assertEquals(in.getNumRows(), inT.getNumColumns());
+                       assertEquals(in.getNumColumns(), inT.getNumRows());
+                       compareTransposed(in, inT);
+
+               }
+               catch(Exception e) {
+                       e.printStackTrace();
+                       fail("Failed in transposition");
+               }
+               finally {
+
+                       LibMatrixReorg.PAR_NUMCELL_THRESHOLD = old;
+               }
+       }
+
+       private void compareTransposed(MatrixBlock in, MatrixBlock inT) {
+               int nRow = in.getNumRows();
+               int nCol = in.getNumColumns();
+               for(int i = 0; i < nRow; i++) {
+                       for(int j = 0; j < nCol; j++) {
+                               assertEquals(in.quickGetValue(i, j), 
inT.quickGetValue(j, i), 0.0);
+                       }
+               }
+       }
+}

Reply via email to