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

mboehm7 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 81efec85db [SYSTEMDS-3172] Additional CSC sparse block implementation
81efec85db is described below

commit 81efec85dbd654dac1fcfa2b8009b4239f59d0c3
Author: ReneEnjilian <[email protected]>
AuthorDate: Fri Oct 25 11:53:27 2024 +0200

    [SYSTEMDS-3172] Additional CSC sparse block implementation
    
    Closes #2132.
---
 .../org/apache/sysds/runtime/data/SparseBlock.java |    1 +
 .../apache/sysds/runtime/data/SparseBlockCSC.java  | 1455 ++++++++++++++++++++
 .../sysds/runtime/data/SparseBlockFactory.java     |    2 +
 .../component/sparse/SparseBlockAlignment.java     |   30 +
 .../component/sparse/SparseBlockAppendSort.java    |   36 +-
 .../test/component/sparse/SparseBlockDelete.java   |   15 +
 .../component/sparse/SparseBlockGetFirstIndex.java |   53 +-
 .../test/component/sparse/SparseBlockGetSet.java   |   87 +-
 .../component/sparse/SparseBlockIndexRange.java    |   31 +
 .../test/component/sparse/SparseBlockIterator.java |   32 +
 .../component/sparse/SparseBlockMemEstimate.java   |    3 +
 .../test/component/sparse/SparseBlockMerge.java    |  278 +++-
 .../test/component/sparse/SparseBlockScan.java     |   16 +
 .../test/component/sparse/SparseBlockSize.java     |   34 +
 14 files changed, 2023 insertions(+), 50 deletions(-)

diff --git a/src/main/java/org/apache/sysds/runtime/data/SparseBlock.java 
b/src/main/java/org/apache/sysds/runtime/data/SparseBlock.java
index 648f42b690..91bba40d0c 100644
--- a/src/main/java/org/apache/sysds/runtime/data/SparseBlock.java
+++ b/src/main/java/org/apache/sysds/runtime/data/SparseBlock.java
@@ -50,6 +50,7 @@ public abstract class SparseBlock implements Serializable, 
Block
        public enum Type {
                COO,  // coordinate
                CSR,  // compressed sparse rows
+               CSC,  // compressed sparse column
                DCSR, // double compressed sparse rows
                MCSR, // modified compressed sparse rows (update-friendly)
                MCSC, // modified compressed sparse column
diff --git a/src/main/java/org/apache/sysds/runtime/data/SparseBlockCSC.java 
b/src/main/java/org/apache/sysds/runtime/data/SparseBlockCSC.java
new file mode 100644
index 0000000000..1532c02b7c
--- /dev/null
+++ b/src/main/java/org/apache/sysds/runtime/data/SparseBlockCSC.java
@@ -0,0 +1,1455 @@
+/*
+ * 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.data;
+
+import org.apache.sysds.runtime.util.SortUtils;
+import org.apache.sysds.runtime.util.UtilFunctions;
+import org.apache.sysds.utils.MemoryEstimates;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * SparseBlock implementation that realizes a traditional 'compressed sparse 
column' representation, where the entire
+ * sparse block is stored as three arrays: ptr of length clen+1 to store 
offsets per column, and indexes/values of
+ * length nnz to store row indexes and values of non-zero entries. This format 
is very memory efficient for sparse (but
+ * not ultra-sparse) matrices and provides very good performance for common 
operations, partially due to lower memory
+ * bandwidth requirements. However, this format is slow on incremental 
construction (because it does not allow
+ * append/sort per row) without reshifting. For these types of operations, the 
'modified compressed sparse row/column'
+ * representations are better suited since they are update friendlier. 
Further, this representation implements both the
+ * row oriented and the column oriented one. By implementing the row-oriented 
API, we ensure interoperability with
+ * existing operators. Finally, the total nnz is limited to INTEGER_MAX, 
whereas for SparseBlockMCSR only the nnz per
+ * row are limited to INTEGER_MAX.
+ * 
+ * TODO performance - currently this sparse block is very slow and this slows 
down even the component tests
+ */
+
+public class SparseBlockCSC extends SparseBlock {
+
+       private static final long serialVersionUID = -8020198259526080455L;
+       private int[] _ptr = null;       //column pointer array (size: clen+1)
+       private int[] _indexes = null;   //row index array (size: >=nnz)
+       private double[] _values = null; //value array (size: >=nnz)
+       private int _size = 0;           //actual number of nnz
+       private int _rlen = -1;             // number of rows
+       private int _clenInferred = -1;
+
+       public SparseBlockCSC(int rlen, int clen) {
+               _rlen = rlen;
+               _ptr = new int[clen + 1];
+               _indexes = new int[INIT_CAPACITY];
+               _values = new double[INIT_CAPACITY];
+               _size = 0;
+       }
+
+       public SparseBlockCSC(int clen, int capacity, int size) {
+               _ptr = new int[clen + 1]; //ix0=0
+               _indexes = new int[capacity];
+               _values = new double[capacity];
+               _size = size;
+       }
+
+       public SparseBlockCSC(int[] rowPtr, int[] rowInd, double[] values, int 
nnz) {
+               _ptr = rowPtr;
+               _indexes = rowInd;
+               _values = values;
+               _size = nnz;
+       }
+
+       public SparseBlockCSC(SparseBlock sblock, int clen) {
+               _clenInferred = clen;
+               _rlen = sblock.numRows();
+               _size = (int) sblock.size();
+               initialize(sblock);
+       }
+
+       public SparseBlockCSC(SparseBlock sblock) {
+               inferNumCol(sblock);
+               _rlen = sblock.numRows();
+               _size = (int) sblock.size();
+               initialize(sblock);
+       }
+
+       private void initialize(SparseBlock sblock) {
+
+               if(_size > Integer.MAX_VALUE)
+                       throw new RuntimeException("SparseBlockCSC supports 
nnz<=Integer.MAX_VALUE but got " + _size);
+
+               //special case SparseBlockCSC
+               if(sblock instanceof SparseBlockCSC) {
+                       SparseBlockCSC originalCSC = (SparseBlockCSC) sblock;
+                       _ptr = Arrays.copyOf(originalCSC._ptr, 
originalCSC.numCols() + 1);
+                       _indexes = Arrays.copyOf(originalCSC._indexes, 
originalCSC._size);
+                       _values = Arrays.copyOf(originalCSC._values, 
originalCSC._size);
+               }
+
+               //special case SparseBlockMCSC
+               else if(sblock instanceof SparseBlockMCSC) {
+                       SparseBlockMCSC originalMCSC = (SparseBlockMCSC) sblock;
+                       _ptr = new int[originalMCSC.numCols() + 1];
+                       _ptr[0] = 0;
+                       _values = new double[(int) originalMCSC.size()];
+                       _indexes = new int[(int) originalMCSC.size()];
+                       int ptrPos = 1;
+                       int valPos = 0;
+                       SparseRow columns[] = originalMCSC.getCols();
+                       for(SparseRow column : columns) {
+                               int rowIdx[] = column.indexes();
+                               double vals[] = column.values();
+                               for(int i = 0; i < column.size(); i++) {
+                                       _indexes[valPos + i] = rowIdx[i];
+                                       _values[valPos + i] = vals[i];
+                               }
+                               _ptr[ptrPos] = _ptr[ptrPos - 1] + column.size();
+                               ptrPos++;
+                               valPos += column.size();
+                       }
+               }
+
+               // general case sparse block (CSR, MCSR, DCSR)
+               else {
+                       int rlen = _rlen;
+                       int clen = _clenInferred;
+                       int nnz = _size; // total number of non-zero elements
+
+                       // Step 1: Count non-zero elements per column
+                       int[] colCounts = new int[clen];
+                       for(int i = 0; i < rlen; i++) {
+                               if(!sblock.isEmpty(i)) {
+                                       int alen = sblock.size(i);
+                                       int apos = sblock.pos(i);
+                                       int[] aix = sblock.indexes(i);
+                                       for(int k = apos; k < apos + alen; k++) 
{
+                                               int col = aix[k];
+                                               colCounts[col]++;
+                                       }
+                               }
+                       }
+
+                       // Step 2: Compute CSC pointer array (_ptr)
+                       _ptr = new int[clen + 1];
+                       _ptr[0] = 0;
+                       for(int j = 0; j < clen; j++) {
+                               _ptr[j + 1] = _ptr[j] + colCounts[j];
+                       }
+
+                       // Step 3: Initialize arrays for indexes and values
+                       _size = nnz;
+                       _indexes = new int[nnz];
+                       _values = new double[nnz];
+
+                       // Step 4: Initialize position trackers for each column
+                       int[] colPositions = new int[clen];
+                       System.arraycopy(_ptr, 0, colPositions, 0, clen);
+
+                       // Step 5: Fill CSC indexes (_indexes) and values 
(_values) arrays
+                       for(int i = 0; i < rlen; i++) {
+                               if(!sblock.isEmpty(i)) {
+                                       int alen = sblock.size(i);
+                                       int apos = sblock.pos(i);
+                                       int[] aix = sblock.indexes(i);
+                                       double[] avals = sblock.values(i);
+                                       for(int k = apos; k < apos + alen; k++) 
{
+                                               int col = aix[k];
+                                               int pos = colPositions[col];
+                                               _indexes[pos] = i;        // 
row index
+                                               _values[pos] = avals[k];  // 
value
+                                               colPositions[col]++;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       public SparseBlockCSC(SparseRow[] cols, int nnz) {
+
+               int clen = cols.length;
+               _clenInferred = clen;
+               _ptr = new int[clen + 1]; //ix0=0
+               _indexes = new int[nnz];
+               _values = new double[nnz];
+               _size = nnz;
+
+               for(int i = 0, pos = 0; i < clen; i++) {
+                       if(cols[i] != null && !cols[i].isEmpty()) {
+                               int alen = cols[i].size();
+                               int[] aix = cols[i].indexes();
+                               double[] avals = cols[i].values();
+                               System.arraycopy(aix, 0, _indexes, pos, alen);
+                               System.arraycopy(avals, 0, _values, pos, alen);
+                               pos += alen;
+                       }
+                       _ptr[i + 1] = pos;
+               }
+       }
+
+       public SparseBlockCSC(int cols, int[] rowInd, int[] colInd, double[] 
values) {
+               int nnz = values.length;
+               _ptr = new int[cols + 1];
+               _indexes = Arrays.copyOf(rowInd, rowInd.length);
+               _values = Arrays.copyOf(values, nnz);
+               _size = nnz;
+               _clenInferred = cols;
+
+               //single-pass construction of pointers
+               int clast = 0;
+               for(int i = 0; i < nnz; i++) {
+                       int c = colInd[i];
+                       if(clast < c)
+                               Arrays.fill(_ptr, clast + 1, c + 1, i);
+                       clast = c;
+               }
+               Arrays.fill(_ptr, clast + 1, numCols() + 1, nnz);
+
+       }
+
+       public SparseBlockCSC(int cols, int nnz, int[] rowInd) {
+
+               _clenInferred = cols;
+               _ptr = new int[cols + 1];
+               _indexes = Arrays.copyOf(rowInd, nnz);
+               _values = new double[nnz];
+               Arrays.fill(_values, 1);
+               _size = nnz;
+
+               //single-pass construction of col pointers
+               //and copy of row indexes if necessary
+               for(int i = 0, pos = 0; i < cols; i++) {
+                       if(rowInd[i] >= 0) {
+                               if(cols > nnz)
+                                       _indexes[pos] = rowInd[i];
+                               pos++;
+                       }
+                       _ptr[i + 1] = pos;
+               }
+       }
+
+       /**
+        * Initializes the CSC sparse block from an ordered input stream of 
ultra-sparse ijv triples.
+        *
+        * @param nnz number of non-zeros to read
+        * @param in  data input stream of ijv triples, ordered by column and 
row indices
+        * @throws IOException if deserialization error occurs
+        */
+       public void initUltraSparse(int nnz, DataInput in) throws IOException {
+               // Allocate space if necessary
+               if(_values.length < nnz)
+                       resize(newCapacity(nnz));
+
+               // Read ijv triples, append and update pointers
+               int clast = 0;
+               for(int i = 0; i < nnz; i++) {
+                       int r = in.readInt();
+                       int c = in.readInt();
+                       double v = in.readDouble();
+
+                       if(clast < c)
+                               Arrays.fill(_ptr, clast + 1, c + 1, i);
+                       clast = c;
+
+                       _indexes[i] = r;   // Row indices in CSC
+                       _values[i] = v;
+               }
+               Arrays.fill(_ptr, clast + 1, numCols() + 1, nnz);
+
+               // Update meta data
+               _size = nnz;
+       }
+
+       /**
+        * Initializes the CSC sparse block from an ordered input stream of 
sparse columns (colnnz, iv-pairs*).
+        *
+        * @param clen number of columns
+        * @param nnz  number of non-zeros to read
+        * @param in   data input stream of sparse columns, ordered by column 
index
+        * @throws IOException if deserialization error occurs
+        */
+       public void initSparse(int clen, int nnz, DataInput in) throws 
IOException {
+               // Allocate space if necessary
+               if(_values.length < nnz) {
+                       resize(newCapacity(nnz));
+                       System.out.println("hallo");
+               }
+               // Read sparse columns, append and update pointers
+               _ptr[0] = 0;
+               for(int c = 0, pos = 0; c < clen; c++) {
+                       int lnnz = in.readInt();  // Number of non-zeros in 
column c
+                       for(int j = 0; j < lnnz; j++, pos++) {
+                               _indexes[pos] = in.readInt();   // Row index
+                               _values[pos] = in.readDouble(); // Value
+                       }
+                       _ptr[c + 1] = pos;
+               }
+
+               // Update meta data
+               _size = nnz;
+       }
+
+       /**
+        * Get the estimated in-memory size of the sparse block in CSC with the 
given dimensions w/o accounting for
+        * overallocation.
+        *
+        * @param nrows    number of rows
+        * @param ncols    number of columns
+        * @param sparsity sparsity ratio
+        * @return memory estimate
+        */
+       public static long estimateSizeInMemory(long nrows, long ncols, double 
sparsity) {
+               double lnnz = Math.max(INIT_CAPACITY, Math.ceil(sparsity * 
nrows * ncols));
+
+               //32B overhead per array, int arr in nrows, int/double arr in 
nnz
+               double size = 16 + 4 + 4;                            //object + 
int field + padding
+               size += MemoryEstimates.intArrayCost(nrows + 1);       //ptr 
array (row pointers)
+               size += MemoryEstimates.intArrayCost((long) lnnz);   //indexes 
array (column indexes)
+               size += MemoryEstimates.doubleArrayCost((long) lnnz);//values 
array (non-zero values)
+
+               //robustness for long overflows
+               return (long) Math.min(size, Long.MAX_VALUE);
+       }
+
+       /**
+        * Get raw access to underlying array of column pointers For use in GPU 
code
+        *
+        * @return array of column pointers
+        */
+       public int[] colPointers() {
+               return _ptr;
+       }
+
+       /**
+        * Get raw access to underlying array of row indices For use in GPU code
+        *
+        * @return array of row indexes
+        */
+       public int[] indexes() {
+               return indexes(0);
+       }
+
+       /**
+        * Get raw access to underlying array of values For use in GPU code
+        *
+        * @return array of values
+        */
+       public double[] values() {
+               return values(0);
+       }
+
+       ///////////////////
+       //SparseBlock implementation
+
+       @Override
+       public void allocate(int r) {
+               //do nothing everything preallocated
+       }
+
+       @Override
+       public void allocate(int r, int nnz) {
+               //do nothing everything preallocated
+       }
+
+       @Override
+       public void allocate(int r, int ennz, int maxnnz) {
+               //do nothing everything preallocated
+       }
+
+       @Override
+       public void compact(int r) {
+               //do nothing everything preallocated
+       }
+
+       @Override
+       public int numRows() {
+               if(_rlen > -1)
+                       return _rlen;
+               else {
+                       int rlen = Arrays.stream(_indexes).max().getAsInt();
+                       _rlen = rlen;
+                       return rlen;
+               }
+       }
+
+       /**
+        * Get the number of columns in the CSC block
+        *
+        * @return number of columns
+        */
+       public int numCols() {
+               return _ptr.length - 1;
+       }
+
+       @Override
+       public boolean isThreadSafe() {
+               return false;
+       }
+
+       @Override
+       public boolean isContiguous() {
+               return true;
+       }
+
+       @Override
+       public boolean isAllocated(int r) {
+               return true;
+       }
+
+       @Override
+       public void reset() {
+               if(_size > 0) {
+                       Arrays.fill(_ptr, 0);
+                       _size = 0;
+               }
+       }
+
+       @Override
+       public void reset(int ennz, int maxnnz) {
+               if(_size > 0) {
+                       Arrays.fill(_ptr, 0);
+                       _size = 0;
+               }
+       }
+
+       @Override
+       public void reset(int r, int ennz, int maxnnz) {
+               List<Integer> cols = new ArrayList<>();
+               List<Integer> posIdx = new ArrayList<>();
+               // find columns containing marked row index and
+               // position in indexes
+               for(int i = 0; i < _ptr.length - 1; i++) {
+                       for(int j = _ptr[i]; j < _ptr[i + 1]; j++) {
+                               if(_indexes[j] == r) {
+                                       cols.add(i);
+                                       posIdx.add(j);
+                               }
+                       }
+               }
+               // reduce pointer array.
+               for(int c : cols) {
+                       decrPtr(c + 1);
+               }
+               // adapt indexes and values
+               for(int i = posIdx.size() - 1; i >= 0; i--) {
+                       if(posIdx.get(i) < _size - 1) {
+                               System.arraycopy(_indexes, posIdx.get(i) + 1, 
_indexes, posIdx.get(i), _size - (posIdx.get(i) + 1));
+                               System.arraycopy(_values, posIdx.get(i) + 1, 
_values, posIdx.get(i), _size - (posIdx.get(i) + 1));
+                       }
+               }
+               _size -= posIdx.size();
+       }
+
+       public void resetCol(int c) {
+               int pos = posCol(c);
+               int len = sizeCol(c);
+
+               if(len > 0) {
+                       //overlapping array copy (shift rhs values left)
+                       System.arraycopy(_indexes, pos + len, _indexes, pos, 
_size - (pos + len));
+                       System.arraycopy(_values, pos + len, _values, pos, 
_size - (pos + len));
+                       _size -= len;
+                       decrPtr(c + 1, len);
+               }
+       }
+
+       @Override
+       public long size() {
+               return _ptr[_ptr.length - 1];
+       }
+
+       @Override
+       public int size(int r) {
+               if(r < 0)
+                       throw new RuntimeException("Row index has to be zero or 
larger.");
+
+               int nnz = 0;
+               for(int i = 0; i < _size; i++) {
+                       if(_indexes[i] == r)
+                               nnz++;
+               }
+               return nnz;
+       }
+
+       public int sizeCol(int c) {
+               return _ptr[c + 1] - _ptr[c];
+       }
+
+       @Override
+       public long size(int rl, int ru) {
+               if(rl < 0 || ru > _rlen)
+                       throw new RuntimeException("Incorrect row boundaries.");
+
+               int nnz = 0;
+               int row = -1;
+               for(int i = 0; i < _size; i++) {
+                       row = _indexes[i];
+                       for(int j = rl; j < ru; j++) {
+                               if(row == j) {
+                                       nnz++;
+                                       break;
+                               }
+                       }
+               }
+               return nnz;
+       }
+
+       public long sizeCol(int cl, int cu) {
+               return _ptr[cu] - _ptr[cl];
+       }
+
+       @Override
+       public long size(int rl, int ru, int cl, int cu) {
+               long nnz = 0;
+               for(int i = cl; i < cu; i++)
+                       if(!isEmptyCol(i)) {
+                               int start = internPosFIndexGTECol(rl, i);
+                               int end = internPosFIndexLTECol(ru - 1, i);
+                               nnz += (start != -1 && end != -1) ? (end - 
start + 1) : 0;
+                       }
+               return nnz;
+       }
+
+       @Override
+       public boolean isEmpty(int r) {
+               boolean empty = true;
+               for(int i = 0; i < _size; i++) {
+                       if(_indexes[i] == r)
+                               return false;
+               }
+               return empty;
+       }
+
+       public boolean isEmptyCol(int c) {
+               return (_ptr[c + 1] - _ptr[c] == 0);
+       }
+
+       @Override
+       public boolean checkValidity(int rlen, int clen, long nnz, boolean 
strict) {
+               //1. correct meta data
+               if(rlen < 0 || clen < 0) {
+                       throw new RuntimeException("Invalid block dimensions: " 
+ rlen + " " + clen);
+               }
+
+               //2. correct array lengths
+               if(_size != nnz && _ptr.length < clen + 1 && _values.length < 
nnz && _indexes.length < nnz) {
+                       throw new RuntimeException("Incorrect array lengths.");
+               }
+
+               //3. non-decreasing row pointers
+               for(int i = 1; i < clen; i++) {
+                       if(_ptr[i - 1] > _ptr[i] && strict)
+                               throw new RuntimeException(
+                                       "Column pointers are decreasing at 
column: " + i + ", with pointers " + _ptr[i - 1] + " > " +
+                                               _ptr[i]);
+               }
+
+               //4. sorted row indexes per column
+               for(int i = 0; i < clen; i++) {
+                       int apos = posCol(i);
+                       int alen = sizeCol(i);
+                       for(int k = apos + 1; k < apos + alen; k++)
+                               if(_indexes[k - 1] >= _indexes[k])
+                                       throw new RuntimeException(
+                                               "Wrong sparse column ordering: 
" + k + " " + _indexes[k - 1] + " " + _indexes[k]);
+                       for(int k = apos; k < apos + alen; k++)
+                               if(_values[k] == 0)
+                                       throw new RuntimeException("Wrong 
sparse column: zero at " + k + " at row index " + _indexes[k]);
+               }
+
+               //5. non-existing zero values
+               for(int i = 0; i < _size; i++) {
+                       if(_values[i] == 0) {
+                               throw new RuntimeException(
+                                       "The values array should not contain 
zeros." + " The " + i + "th value is " + _values[i]);
+                       }
+               }
+
+               //6. a capacity that is no larger than nnz times resize factor.
+               int capacity = _values.length;
+               if(capacity > nnz * RESIZE_FACTOR1) {
+                       throw new RuntimeException(
+                               "Capacity is larger than the nnz times a resize 
factor." + " Current size: " + capacity +
+                                       ", while Expected size:" + nnz * 
RESIZE_FACTOR1);
+               }
+
+               return true;
+       }
+
+       @Override
+       public long getExactSizeInMemory() {
+               //32B overhead per array, int arr in nrows, int/double arr in 
nnz
+               double size = 16 + 4 + 4;                                
//object + int field + padding
+               size += MemoryEstimates.intArrayCost(_ptr.length);       //ptr 
array (row pointers)
+               size += MemoryEstimates.intArrayCost(_indexes.length);   
//indexes array (column indexes)
+               size += MemoryEstimates.doubleArrayCost(_values.length); 
//values array (non-zero values)
+
+               //robustness for long overflows
+               return (long) Math.min(size, Long.MAX_VALUE);
+       }
+
+       @Override
+       public int[] indexes(int r) {
+               //Count elements per row
+               //int[] rowCounts = numElemPerRow();
+
+               // Compute csr pointers
+               int[] csrPtr = rowPointerAll();
+
+               // Populate CSR indices array
+               int[] csrIndices = new int[_size];
+               // Temporary array to keep track of the current position in 
each row
+               int[] currentPos = Arrays.copyOf(csrPtr, _rlen);
+
+               for(int col = 0; col < numCols(); col++) {
+                       for(int i = _ptr[col]; i < _ptr[col + 1]; i++) {
+                               int row = _indexes[i];
+                               int pos = currentPos[row]++;
+                               csrIndices[pos] = col;
+                       }
+               }
+
+               return csrIndices;
+       }
+
+       public int[] indexesCol(int c) {
+               return _indexes;
+       }
+
+       @Override
+       public double[] values(int r) {
+               // Only use first _size elements for sorting
+               Integer[] idx = new Integer[_size];
+               for(int i = 0; i < _size; i++)
+                       idx[i] = i;
+
+               // Sort indices based on corresponding index values
+               Arrays.sort(idx, Comparator.comparingInt(i -> _indexes[i]));
+
+               // Create values array sorted in row order
+               double[] csrValues = new double[_size];
+               for(int i = 0; i < _size; i++) {
+                       csrValues[i] = _values[idx[i]];
+               }
+               return csrValues;
+       }
+
+       public double[] valuesCol(int c) {
+               return _values;
+       }
+
+       @Override
+       public int pos(int r) {
+               int nnz = 0;
+               for(int i = 0; i < _size; i++) {
+                       if(_indexes[i] < r)
+                               nnz++;
+               }
+               return nnz;
+       }
+
+       public int posCol(int c) {
+               return _ptr[c];
+       }
+
+       @Override
+       public boolean set(int r, int c, double v) {
+               int pos = posCol(c);
+               int len = sizeCol(c);
+
+               //search for existing col index
+               int index = Arrays.binarySearch(_indexes, pos, pos + len, r);
+               if(index >= 0) {
+                       //delete/overwrite existing value (on value delete, we 
shift
+                       //left for (1) correct nnz maintenance, and (2) smaller 
size)
+                       if(v == 0) {
+                               shiftLeftAndDelete(index);
+                               decrPtr(c + 1);
+                               return true; // nnz--
+                       }
+                       else {
+                               _values[index] = v;
+                               return false;
+                       }
+               }
+
+               //early abort on zero (if no overwrite)
+               if(v == 0)
+                       return false;
+
+               //insert new index-value pair
+               index = Math.abs(index + 1);
+               if(_size == _values.length)
+                       resizeAndInsert(index, r, v);
+               else
+                       shiftRightAndInsert(index, r, v);
+               incrPtr(c + 1);
+               return true; // nnz++
+       }
+
+       @Override
+       public void set(int r, SparseRow row, boolean deep) {
+               double[] values = row.values();
+               int[] colIndexes = row.indexes();
+               for(int i = 0; i < row.size(); i++) {
+                       set(r, colIndexes[i], values[i]);
+               }
+       }
+
+       public void setCol(int c, SparseRow col, boolean deep) {
+               int pos = posCol(c);
+               int len = sizeCol(c);
+               int alen = col.size();
+               int[] aix = col.indexes();
+               double[] avals = col.values();
+
+               //delete existing values if necessary
+               if(len > 0) //incl size update
+                       deleteIndexRange(c, aix[pos], aix[pos + len - 1] + 1);
+
+               //prepare free space (allocate and shift)
+               int lsize = _size + alen;
+               if(_values.length < lsize)
+                       resize(lsize);
+               shiftRightByN(pos, alen); //incl size update
+               incrPtr(c + 1, alen);
+
+               //copy input row into internal representation
+               System.arraycopy(aix, 0, _indexes, pos, alen);
+               System.arraycopy(avals, 0, _values, pos, alen);
+       }
+
+       @Override
+       public boolean add(int r, int c, double v) {
+               //early abort on zero
+               if(v == 0)
+                       return false;
+
+               int pos = posCol(c);
+               int len = sizeCol(c);
+
+               //search for existing col index
+               int index = Arrays.binarySearch(_indexes, pos, pos + len, r);
+               if(index >= 0) {
+                       //add to existing value
+                       _values[index] += v;
+                       return false;
+               }
+
+               //insert new index-value pair
+               index = Math.abs(index + 1);
+               if(_size == _values.length)
+                       resizeAndInsert(index, r, v);
+               else
+                       shiftRightAndInsert(index, r, v);
+               incrPtr(c + 1);
+               return true; // nnz++
+       }
+
+       @Override
+       public void append(int r, int c, double v) {
+               //early abort on zero
+               if(v == 0)
+                       return;
+
+               int pos = posCol(c);
+               int len = sizeCol(c);
+               if(pos + len == _size) {
+                       //resize and append
+                       if(_size == _values.length)
+                               resize();
+                       insert(_size, r, v);
+               }
+               else {
+                       //resize, shift and insert
+                       if(_size == _values.length)
+                               resizeAndInsert(pos + len, r, v);
+                       else
+                               shiftRightAndInsert(pos + len, r, v);
+               }
+               incrPtr(c + 1);
+
+       }
+
+       @Override
+       public void setIndexRange(int r, int cl, int cu, double[] v, int vix, 
int vlen) {
+               //delete existing values in range if necessary
+               if(!isEmpty(r))
+                       deleteIndexRange(r, cl, cu);
+
+               //determine input nnz
+               int lnnz = UtilFunctions.computeNnz(v, vix, vlen);
+
+               //prepare free space (allocate and shift)
+               int lsize = _size + lnnz;
+               if(_values.length < lsize)
+                       resize(lsize);
+
+               // calculate insertion points / positions to shift
+               int[] posIdx = new int[vlen];
+               int insertionPnt = 0;
+               for(int i = cl; i < cu; i++) {
+                       for(int j = _ptr[i]; j < _ptr[i + 1]; j++) {
+                               if(_indexes[j] > r) {
+                                       posIdx[insertionPnt] = j;
+                                       break;
+                               }
+                               if(j == _ptr[i + 1] - 1)
+                                       posIdx[insertionPnt] = j + 1;
+                       }
+                       insertionPnt++;
+               }
+
+               // shift to the right by 1 given insertion points stored in 
posIdx
+               for(int i = vix + vlen - 1; i >= vix; i--) {
+                       System.arraycopy(_indexes, posIdx[i - vix], _indexes, 
posIdx[i - vix] + 1, _size - posIdx[i - vix]);
+                       System.arraycopy(_values, posIdx[i - vix], _values, 
posIdx[i - vix] + 1, _size - posIdx[i - vix]);
+                       _size++;
+                       _values[posIdx[i - vix]] = v[i];
+                       _indexes[posIdx[i - vix]] = r;
+               }
+
+               // Increment pointer array
+               for(int c = cl; c < cu; c++)
+                       incrPtr(c + 1);
+
+       }
+
+       public void setIndexRangeCol(int c, int rl, int ru, double[] v, int 
vix, int vlen) {
+               //delete existing values in range if necessary
+               if(!isEmptyCol(c))
+                       deleteIndexRangeCol(c, rl, ru);
+
+               //determine input nnz
+               int lnnz = UtilFunctions.computeNnz(v, vix, vlen);
+
+               //prepare free space (allocate and shift)
+               int lsize = _size + lnnz;
+               if(_values.length < lsize)
+                       resize(lsize);
+               int index = internPosFIndexGTCol(rl, c);
+               int index2 = (index > 0) ? index : posCol(c + 1);
+               shiftRightByN(index2, lnnz);
+
+               //insert values
+               for(int i = vix; i < vix + vlen; i++)
+                       if(v[i] != 0) {
+                               _indexes[index2] = rl + i - vix;
+                               _values[index2] = v[i];
+                               index2++;
+                       }
+               incrPtr(c + 1, lnnz);
+       }
+
+       @Override
+       public void setIndexRange(int r, int cl, int cu, double[] v, int[] vix, 
int vpos, int vlen) {
+               //delete existing values in range if necessary
+               if(!isEmpty(r))
+                       deleteIndexRange(r, cl, cu);
+
+               //prepare free space (allocate and shift)
+               int lsize = _size + vlen;
+               if(_values.length < lsize)
+                       resize(lsize);
+
+               int[] posIdx = new int[vlen];
+               int insertionPnt = 0;
+               int currentCol = 0;
+               for(int i = vpos; i < vpos + vlen; i++) {
+                       currentCol = vix[i];
+                       for(int j = _ptr[currentCol]; j < _ptr[currentCol + 1]; 
j++) {
+                               if(_indexes[j] > r) {
+                                       posIdx[insertionPnt] = j;
+                                       break;
+                               }
+                               if(j == _ptr[currentCol + 1] - 1)
+                                       posIdx[insertionPnt] = j + 1;
+                       }
+                       insertionPnt++;
+               }
+
+               // shift to the right by 1 given insertion points stored in 
posIdx
+               for(int i = vpos + vlen - 1; i >= vpos; i--) {
+                       System.arraycopy(_indexes, posIdx[i - vpos], _indexes, 
posIdx[i - vpos] + 1, _size - posIdx[i - vpos]);
+                       System.arraycopy(_values, posIdx[i - vpos], _values, 
posIdx[i - vpos] + 1, _size - posIdx[i - vpos]);
+                       _size++;
+                       _values[posIdx[i - vpos]] = v[i];
+                       _indexes[posIdx[i - vpos]] = r;
+               }
+
+               // Increment pointer array
+               for(int i = vpos; i < vpos + vlen; i++) {
+                       currentCol = vix[i];
+                       incrPtr(currentCol + 1);
+               }
+       }
+
+       public void setIndexRangeCol(int c, int rl, int ru, double[] v, int[] 
vix, int vpos, int vlen) {
+               //delete existing values in range if necessary
+               if(!isEmptyCol(c))
+                       deleteIndexRangeCol(c, rl, ru);
+
+               //prepare free space (allocate and shift)
+               int lsize = _size + vlen;
+               if(_values.length < lsize)
+                       resize(lsize);
+               int index = internPosFIndexGTCol(rl, c);
+               int index2 = (index > 0) ? index : posCol(c + 1);
+               shiftRightByN(index2, vlen);
+
+               //insert values
+               for(int i = vpos; i < vpos + vlen; i++) {
+                       _indexes[index2] = vix[i];
+                       _values[index2] = v[i];
+                       index2++;
+               }
+               incrPtr(c + 1, vlen);
+       }
+
+       @Override
+       public void deleteIndexRange(int r, int cl, int cu) {
+               List<Integer> cols = new ArrayList<>();
+               List<Integer> posIdx = new ArrayList<>();
+               // find columns containing marked row index and
+               // position in indexes
+               for(int i = cl; i < cu; i++) {
+                       for(int j = _ptr[i]; j < _ptr[i + 1]; j++) {
+                               if(_indexes[j] == r) {
+                                       cols.add(i);
+                                       posIdx.add(j);
+                               }
+                       }
+               }
+               // reduce pointer array.
+               for(int c : cols) {
+                       decrPtr(c + 1);
+               }
+               // adapt indexes and values
+               for(int i = posIdx.size() - 1; i >= 0; i--) {
+                       if(posIdx.get(i) < _size - 1) {
+                               System.arraycopy(_indexes, posIdx.get(i) + 1, 
_indexes, posIdx.get(i), _size - (posIdx.get(i) + 1));
+                               System.arraycopy(_values, posIdx.get(i) + 1, 
_values, posIdx.get(i), _size - (posIdx.get(i) + 1));
+                       }
+               }
+               _size -= posIdx.size();
+       }
+
+       public void deleteIndexRangeCol(int c, int rl, int ru) {
+               int start = internPosFIndexGTECol(rl, c);
+               if(start < 0) //nothing to delete
+                       return;
+
+               int len = sizeCol(c);
+               int end = internPosFIndexGTECol(ru, c);
+               if(end < 0) //delete all remaining
+                       end = start + len;
+
+               //overlapping array copy (shift rhs values left)
+               System.arraycopy(_indexes, end, _indexes, start, _size - end);
+               System.arraycopy(_values, end, _values, start, _size - end);
+               _size -= (end - start);
+
+               decrPtr(c + 1, end - start);
+       }
+
+       @Override
+       public void sort() {
+               for(int i = 0; i < numCols() && posCol(i) < _size; i++) {
+                       sortCol(i);
+               }
+       }
+
+       @Override
+       public void sort(int r) {
+               sort();
+       }
+
+       public void sortCol(int c) {
+               int pos = posCol(c);
+               int len = sizeCol(c);
+
+               if(len <= 100 || !SortUtils.isSorted(pos, pos + len, _indexes))
+                       SortUtils.sortByIndex(pos, pos + len, _indexes, 
_values);
+       }
+
+       @Override
+       public double get(int r, int c) {
+               if(isEmptyCol(c))
+                       return 0;
+               int pos = posCol(c);
+               int len = sizeCol(c);
+
+               //search for existing col index in [pos,pos+len)
+               int index = Arrays.binarySearch(_indexes, pos, pos + len, r);
+               return (index >= 0) ? _values[index] : 0;
+       }
+
+       @Override
+       public SparseRow get(int r) {
+               int rowSize = size(r);
+               if(rowSize == 0)
+                       return new SparseRowScalar();
+
+               //Create sparse row
+               SparseRowVector row = new SparseRowVector(rowSize);
+
+               for(int i = 0; i < _size; i++) {
+                       if(_indexes[i] == r) {
+                               //Search for index i in pointer array
+                               for(int j = 0; j < _ptr.length; j++) {
+                                       // two possible cases
+                                       if(_ptr[j] < i && _ptr[j + 1] > i) {
+                                               row.set(j, _values[i]);
+                                       }
+                                       else if(_ptr[j] == i && _ptr[j + 1] > 
i) {
+                                               row.set(j, _values[i]);
+                                               break;
+                                       }
+                               }
+                       }
+               }
+               return row;
+       }
+
+       public SparseRow getCol(int c) {
+               if(isEmptyCol(c))
+                       return new SparseRowScalar();
+               int pos = posCol(c);
+               int len = sizeCol(c);
+
+               SparseRowVector col = new SparseRowVector(len);
+               System.arraycopy(_indexes, pos, col.indexes(), 0, len);
+               System.arraycopy(_values, pos, col.values(), 0, len);
+               col.setSize(len);
+               return col;
+       }
+
+       @Override
+       public int posFIndexLTE(int r, int c) {
+               int index = internPosFIndexLTE(r, c);
+               return (index >= 0) ? index - pos(r) : index;
+       }
+
+       public int posFIndexLTECol(int r, int c) {
+               int index = internPosFIndexLTECol(r, c);
+               return (index >= 0) ? index - posCol(c) : index;
+       }
+
+       @Override
+       public int posFIndexGTE(int r, int c) {
+               final int pos = pos(r);
+               final int len = size(r);
+               final int end = pos + len;
+
+               // search for existing col index
+               int index = Arrays.binarySearch(indexes(r), pos, end, c);
+               if(index < 0)
+                       // search gt col index (see binary search)
+                       index = Math.abs(index + 1);
+
+               return (index < end) ? index - pos : -1;
+       }
+
+       public int posFIndexGTECol(int r, int c) {
+               final int pos = posCol(c);
+               final int len = sizeCol(c);
+               final int end = pos + len;
+
+               // search for existing row index
+               int index = Arrays.binarySearch(_indexes, pos, end, r);
+               if(index < 0)
+                       // search gt row index (see binary search)
+                       index = Math.abs(index + 1);
+
+               return (index < end) ? index - pos : -1;
+       }
+
+       @Override
+       public int posFIndexGT(int r, int c) {
+               int index = internPosFIndexGT(r, c);
+               return (index >= 0) ? index - pos(r) : index;
+       }
+
+       public int posFIndexGTCol(int r, int c) {
+               int index = internPosFIndexGTCol(r, c);
+               return (index >= 0) ? index - posCol(c) : index;
+       }
+
+       @Override
+       public String toString() {
+               StringBuilder sb = new StringBuilder();
+               sb.append("SparseBlockCSR: clen=");
+               sb.append(numCols());
+               sb.append(", nnz=");
+               sb.append(size());
+               sb.append("\n");
+               final int colDigits = (int) 
Math.max(Math.ceil(Math.log10(numCols())), 1);
+               for(int i = 0; i < numCols(); i++) {
+                       // append column
+                       final int pos = posCol(i);
+                       final int len = sizeCol(i);
+                       if(pos < pos + len) {
+                               sb.append(String.format("%0" + colDigits + "d 
", i));
+                               for(int j = pos; j < pos + len; j++) {
+                                       if(_values[j] == (long) _values[j])
+                                               sb.append(String.format("%" + 
colDigits + "d:%d", _indexes[j], (long) _values[j]));
+                                       else
+                                               sb.append(String.format("%" + 
colDigits + "d:%s", _indexes[j], Double.toString(_values[j])));
+                                       if(j + 1 < pos + len)
+                                               sb.append(" ");
+                               }
+                               sb.append("\n");
+                       }
+               }
+
+               return sb.toString();
+       }
+
+       @Override
+       public Iterator<Integer> getNonEmptyRowsIterator(int rl, int ru) {
+               return new NonEmptyRowsIteratorCSC(rl, ru);
+       }
+
+       public class NonEmptyRowsIteratorCSC implements Iterator<Integer> {
+               private int _rpos;
+               private final int _ru;
+               private BitSet _rows = null;
+
+               public NonEmptyRowsIteratorCSC(int rl, int ru) {
+                       _rpos = rl;
+                       _ru = ru;
+                       checkNonEmptyRows();
+               }
+
+               @Override
+               public boolean hasNext() {
+                       while(_rpos < _ru && !_rows.get(_rpos))
+                               _rpos++;
+                       return _rpos < _ru;
+               }
+
+               @Override
+               public Integer next() {
+                       return _rpos++;
+               }
+
+               private void checkNonEmptyRows() {
+                       int rlen = numRows();
+                       _rows = new BitSet(rlen);
+                       for(int i = 0; i < _size; i++)
+                               _rows.set(_indexes[i]);
+               }
+       }
+
+       public Iterator<Integer> getNonEmptyColumnsIterator(int cl, int cu) {
+               return new SparseBlockCSC.NonEmptyColumnsIteratorCSC(cl, cu);
+       }
+
+       public class NonEmptyColumnsIteratorCSC implements Iterator<Integer> {
+               private int _cpos;
+               private final int _cu;
+
+               public NonEmptyColumnsIteratorCSC(int cl, int cu) {
+                       _cpos = cl;
+                       _cu = cu;
+               }
+
+               @Override
+               public boolean hasNext() {
+                       while(_cpos < _cu && isEmptyCol(_cpos)) {
+                               _cpos++;
+                       }
+                       return _cpos < _cu;
+               }
+
+               @Override
+               public Integer next() {
+                       return _cpos++;
+               }
+
+       }
+
+       @SuppressWarnings("unused")
+       private class SparseNonEmptyColumnIterable implements Iterable<Integer> 
{
+               private final int _cl; //column lower
+               private final int _cu; //column upper
+
+               protected SparseNonEmptyColumnIterable(int cl, int cu) {
+                       _cl = cl;
+                       _cu = cu;
+               }
+
+               @Override
+               public Iterator<Integer> iterator() {
+                       //use specialized non-empty row iterators of sparse 
blocks
+                       return getNonEmptyColumnsIterator(_cl, _cu);
+               }
+       }
+
+       ///////////////////////////
+       // private helper methods
+
+       private void inferNumCol(SparseBlock sblock) {
+               int[] indexes = null;
+               if(sblock instanceof SparseBlockMCSR) {
+                       SparseRow[] origRows = ((SparseBlockMCSR) 
sblock).getRows();
+                       for(SparseRow row : origRows) {
+                               if(row != null) {
+                                       indexes = row.indexes();
+                                       int max = 
Arrays.stream(indexes).max().getAsInt();
+                                       if(max > _clenInferred)
+                                               _clenInferred = max;
+                               }
+                       }
+               }
+               else if(sblock instanceof SparseBlockMCSC) {
+                       _clenInferred = ((SparseBlockMCSC) 
sblock).getCols().length;
+               }
+               else if(sblock instanceof SparseBlockCSC) {
+                       _clenInferred = ((SparseBlockCSC) sblock).numCols();
+               }
+               // SparseBlockCSR and SparseBlockDCSR
+               else {
+                       indexes = sblock.indexes(0);
+                       _clenInferred = Arrays.stream(indexes).max().getAsInt();
+               }
+               _clenInferred += 1;
+       }
+
+       private int newCapacity(int minsize) {
+               //compute new size until minsize reached
+               double tmpCap = Math.max(_values.length, 1);
+               while(tmpCap < minsize) {
+                       tmpCap *= (tmpCap <= 1024) ? RESIZE_FACTOR1 : 
RESIZE_FACTOR2;
+               }
+               return (int) Math.min(tmpCap, Integer.MAX_VALUE);
+       }
+
+       private void resize() {
+               //resize by at least by 1
+               int newCap = newCapacity(_values.length + 1);
+               resizeCopy(newCap);
+       }
+
+       private void resize(int minsize) {
+               int newCap = newCapacity(minsize);
+               resizeCopy(newCap);
+       }
+
+       private void resizeCopy(int capacity) {
+               //reallocate arrays and copy old values
+               _indexes = Arrays.copyOf(_indexes, capacity);
+               _values = Arrays.copyOf(_values, capacity);
+       }
+
+       private void resizeAndInsert(int ix, int r, double v) {
+               //compute new size
+               int newCap = newCapacity(_values.length + 1);
+
+               int[] oldindexes = _indexes;
+               double[] oldvalues = _values;
+               _indexes = new int[newCap];
+               _values = new double[newCap];
+
+               //copy lhs values to new array
+               System.arraycopy(oldindexes, 0, _indexes, 0, ix);
+               System.arraycopy(oldvalues, 0, _values, 0, ix);
+
+               //copy rhs values to new array
+               System.arraycopy(oldindexes, ix, _indexes, ix + 1, _size - ix);
+               System.arraycopy(oldvalues, ix, _values, ix + 1, _size - ix);
+
+               //insert new value
+               insert(ix, r, v);
+       }
+
+       private void shiftRightAndInsert(int ix, int r, double v) {
+               //overlapping array copy (shift rhs values right by 1)
+               System.arraycopy(_indexes, ix, _indexes, ix + 1, _size - ix);
+               System.arraycopy(_values, ix, _values, ix + 1, _size - ix);
+
+               //insert new value
+               insert(ix, r, v);
+       }
+
+       private void shiftLeftAndDelete(int ix) {
+               //overlapping array copy (shift rhs values left by 1)
+               System.arraycopy(_indexes, ix + 1, _indexes, ix, _size - ix - 
1);
+               System.arraycopy(_values, ix + 1, _values, ix, _size - ix - 1);
+               _size--;
+       }
+
+       private void shiftRightByN(int ix, int n) {
+               //overlapping array copy (shift rhs values right by 1)
+               System.arraycopy(_indexes, ix, _indexes, ix + n, _size - ix);
+               System.arraycopy(_values, ix, _values, ix + n, _size - ix);
+               _size += n;
+       }
+
+       @SuppressWarnings("unused")
+       private void shiftLeftByN(int ix, int n) {
+               //overlapping array copy (shift rhs values left by n)
+               System.arraycopy(_indexes, ix, _indexes, ix - n, _size - ix);
+               System.arraycopy(_values, ix, _values, ix - n, _size - ix);
+               _size -= n;
+       }
+
+       private void insert(int ix, int r, double v) {
+               _indexes[ix] = r;
+               _values[ix] = v;
+               _size++;
+       }
+
+       private void incrPtr(int cl) {
+               incrPtr(cl, 1);
+       }
+
+       private void incrPtr(int cl, int cnt) {
+               int clen = numCols();
+               for(int i = cl; i < clen + 1; i++)
+                       _ptr[i] += cnt;
+       }
+
+       private void incrRowPtr(int rl, int[] csrPtr) {
+               incrRowPtr(rl, csrPtr, 1);
+       }
+
+       private void incrRowPtr(int rl, int[] csrPtr, int cnt) {
+               for(int i = rl; i < csrPtr.length; i++) {
+                       csrPtr[i] += cnt;
+               }
+       }
+
+       private void decrPtr(int cl) {
+               decrPtr(cl, 1);
+       }
+
+       private void decrPtr(int cl, int cnt) {
+               for(int i = cl; i < _ptr.length; i++)
+                       _ptr[i] -= cnt;
+       }
+
+       @SuppressWarnings("unused")
+       private int[] numElemPerRow() {
+               int rlen = numRows();
+               int[] rowCount = new int[rlen];
+               for(int i = 0; i < _size; i++) {
+                       rowCount[_indexes[i]] += 1;
+               }
+               return rowCount;
+       }
+
+       private int[] rowPointerAll() {
+               int rlen = numRows();
+               int[] csrPtr = new int[rlen + 1];
+               csrPtr[0] = 0;
+               for(int i = 0; i < _size; i++)
+                       incrRowPtr(_indexes[i] + 1, csrPtr);
+
+               return csrPtr;
+       }
+
+       private int internPosFIndexLTECol(int r, int c) {
+               int pos = posCol(c);
+               int len = sizeCol(c);
+
+               //search for existing row index in [pos,pos+len)
+               int index = Arrays.binarySearch(_indexes, pos, pos + len, r);
+               if(index >= 0)
+                       return (index < pos + len) ? index : -1;
+
+               //search lt row index (see binary search)
+               index = Math.abs(index + 1);
+               return (index - 1 >= pos) ? index - 1 : -1;
+       }
+
+       private int internPosFIndexGTECol(int r, int c) {
+               int pos = posCol(c);
+               int len = sizeCol(c);
+
+               //search for existing row index
+               int index = Arrays.binarySearch(_indexes, pos, pos + len, r);
+               if(index >= 0)
+                       return (index < pos + len) ? index : -1;
+
+               //search gt row index (see binary search)
+               index = Math.abs(index + 1);
+               return (index < pos + len) ? index : -1;
+       }
+
+       private int internPosFIndexGTCol(int r, int c) {
+               int pos = posCol(c);
+               int len = sizeCol(c);
+
+               //search for existing row index
+               int index = Arrays.binarySearch(_indexes, pos, pos + len, r);
+               if(index >= 0)
+                       return (index + 1 < pos + len) ? index + 1 : -1;
+
+               //search gt row index (see binary search)
+               index = Math.abs(index + 1);
+               return (index < pos + len) ? index : -1;
+       }
+
+       private int internPosFIndexLTE(int r, int c) {
+               int pos = pos(r);
+               int len = size(r);
+
+               //search for existing col index in [pos,pos+len)
+               int index = Arrays.binarySearch(indexes(r), pos, pos + len, c);
+               if(index >= 0)
+                       return (index < pos + len) ? index : -1;
+
+               //search lt col index (see binary search)
+               index = Math.abs(index + 1);
+               return (index - 1 >= pos) ? index - 1 : -1;
+       }
+
+       @SuppressWarnings("unused")
+       private int internPosFIndexGTE(int r, int c) {
+               int pos = pos(r);
+               int len = size(r);
+
+               //search for existing col index
+               int index = Arrays.binarySearch(indexes(r), pos, pos + len, c);
+               if(index >= 0)
+                       return (index < pos + len) ? index : -1;
+
+               //search gt col index (see binary search)
+               index = Math.abs(index + 1);
+               return (index < pos + len) ? index : -1;
+       }
+
+       private int internPosFIndexGT(int r, int c) {
+               int pos = pos(r);
+               int len = size(r);
+
+               //search for existing col index
+               int index = Arrays.binarySearch(indexes(r), pos, pos + len, c);
+               if(index >= 0)
+                       return (index + 1 < pos + len) ? index + 1 : -1;
+
+               //search gt col index (see binary search)
+               index = Math.abs(index + 1);
+               return (index < pos + len) ? index : -1;
+       }
+}
diff --git 
a/src/main/java/org/apache/sysds/runtime/data/SparseBlockFactory.java 
b/src/main/java/org/apache/sysds/runtime/data/SparseBlockFactory.java
index 106dd36ba9..22dfe5417e 100644
--- a/src/main/java/org/apache/sysds/runtime/data/SparseBlockFactory.java
+++ b/src/main/java/org/apache/sysds/runtime/data/SparseBlockFactory.java
@@ -71,6 +71,7 @@ public abstract class SparseBlockFactory{
                        case COO: return new SparseBlockCOO(sblock);
                        case DCSR: return new SparseBlockDCSR(sblock);
                        case MCSC: return new SparseBlockMCSC(sblock, clen);
+                       case CSC: return new SparseBlockCSC(sblock, clen);
                        default:
                                throw new RuntimeException("Unexpected sparse 
block type: "+type.toString());
                }
@@ -90,6 +91,7 @@ public abstract class SparseBlockFactory{
                switch( type ) {
                        case MCSR: return 
SparseBlockMCSR.estimateSizeInMemory(nrows, ncols, sparsity);
                        case CSR: return 
SparseBlockCSR.estimateSizeInMemory(nrows, ncols, sparsity);
+                       case CSC: return 
SparseBlockCSC.estimateSizeInMemory(nrows, ncols, sparsity);
                        case COO: return 
SparseBlockCOO.estimateSizeInMemory(nrows, ncols, sparsity);
                        case DCSR: return 
SparseBlockDCSR.estimateSizeInMemory(nrows, ncols, sparsity);
                        case MCSC: return 
SparseBlockMCSC.estimateSizeInMemory(nrows, ncols, sparsity);
diff --git 
a/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockAlignment.java
 
b/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockAlignment.java
index 41caea2e37..3c2ed30adc 100644
--- 
a/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockAlignment.java
+++ 
b/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockAlignment.java
@@ -122,6 +122,21 @@ public class SparseBlockAlignment extends AutomatedTestBase
                runSparseBlockScanTest(SparseBlock.Type.MCSC, sparsity3, true);
        }
 
+       @Test
+       public void testSparseBlockCSC1Pos()  {
+               runSparseBlockScanTest(SparseBlock.Type.CSC, sparsity1, true);
+       }
+
+       @Test
+       public void testSparseBlockCSC2Pos()  {
+               runSparseBlockScanTest(SparseBlock.Type.CSC, sparsity2, true);
+       }
+
+       @Test
+       public void testSparseBlockCSC3Pos()  {
+               runSparseBlockScanTest(SparseBlock.Type.CSC, sparsity3, true);
+       }
+
        @Test
        public void testSparseBlockMCSR1Neg()  {
                runSparseBlockScanTest(SparseBlock.Type.MCSR, sparsity1, false);
@@ -197,6 +212,21 @@ public class SparseBlockAlignment extends AutomatedTestBase
                runSparseBlockScanTest(SparseBlock.Type.MCSC, sparsity3, false);
        }
 
+       @Test
+       public void testSparseBlockCSC1Neg()  {
+               runSparseBlockScanTest(SparseBlock.Type.CSC, sparsity1, false);
+       }
+
+       @Test
+       public void testSparseBlockCSC2Neg()  {
+               runSparseBlockScanTest(SparseBlock.Type.CSC, sparsity2, false);
+       }
+
+       @Test
+       public void testSparseBlockCSC3Neg()  {
+               runSparseBlockScanTest(SparseBlock.Type.CSC, sparsity3, false);
+       }
+
        private void runSparseBlockScanTest( SparseBlock.Type btype, double 
sparsity, boolean positive)
        {
                try
diff --git 
a/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockAppendSort.java
 
b/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockAppendSort.java
index 37bdd4fb87..401a19e8bb 100644
--- 
a/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockAppendSort.java
+++ 
b/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockAppendSort.java
@@ -19,14 +19,15 @@
 
 package org.apache.sysds.test.component.sparse;
 
-import org.apache.sysds.runtime.data.SparseBlockDCSR;
 import org.junit.Assert;
 import org.junit.Test;
 import org.apache.sysds.runtime.data.SparseBlock;
 import org.apache.sysds.runtime.data.SparseBlockCOO;
+import org.apache.sysds.runtime.data.SparseBlockCSC;
 import org.apache.sysds.runtime.data.SparseBlockCSR;
-import org.apache.sysds.runtime.data.SparseBlockMCSR;
+import org.apache.sysds.runtime.data.SparseBlockDCSR;
 import org.apache.sysds.runtime.data.SparseBlockMCSC;
+import org.apache.sysds.runtime.data.SparseBlockMCSR;
 import org.apache.sysds.runtime.util.LongLongDoubleHashMap;
 import org.apache.sysds.runtime.util.LongLongDoubleHashMap.ADoubleEntry;
 import org.apache.sysds.test.AutomatedTestBase;
@@ -207,6 +208,36 @@ public class SparseBlockAppendSort extends 
AutomatedTestBase
        public void testSparseBlockMCSC3Rand()  {
                runSparseBlockAppendSortTest(SparseBlock.Type.MCSC, sparsity3, 
InitType.RAND_SET);
        }
+
+       @Test
+       public void testSparseBlockCSC1Seq()  {
+               runSparseBlockAppendSortTest(SparseBlock.Type.CSC, sparsity1, 
InitType.SEQ_SET);
+       }
+
+       @Test
+       public void testSparseBlockCSC2Seq()  {
+               runSparseBlockAppendSortTest(SparseBlock.Type.CSC, sparsity2, 
InitType.SEQ_SET);
+       }
+
+       @Test
+       public void testSparseBlockCSC3Seq()  {
+               runSparseBlockAppendSortTest(SparseBlock.Type.CSC, sparsity3, 
InitType.SEQ_SET);
+       }
+
+       @Test
+       public void testSparseBlockCSC1Rand()  {
+               runSparseBlockAppendSortTest(SparseBlock.Type.CSC, sparsity1, 
InitType.RAND_SET);
+       }
+
+       @Test
+       public void testSparseBlockCSC2Rand()  {
+               runSparseBlockAppendSortTest(SparseBlock.Type.CSC, sparsity2, 
InitType.RAND_SET);
+       }
+
+       @Test
+       public void testSparseBlockCSC3Rand()  {
+               runSparseBlockAppendSortTest(SparseBlock.Type.CSC, sparsity3, 
InitType.RAND_SET);
+       }
        
        private void runSparseBlockAppendSortTest( SparseBlock.Type btype, 
double sparsity, InitType itype)
        {
@@ -223,6 +254,7 @@ public class SparseBlockAppendSort extends AutomatedTestBase
                                case COO: sblock = new SparseBlockCOO(rows, 
cols); break;
                                case DCSR: sblock = new SparseBlockDCSR(rows, 
cols); break;
                                case MCSC: sblock = new SparseBlockMCSC(rows, 
cols); break;
+                               case CSC: sblock = new SparseBlockCSC(rows, 
cols); break;
                        }
                        
                        if(itype == InitType.SEQ_SET) {
diff --git 
a/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockDelete.java 
b/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockDelete.java
index 7ac63c0cee..009eb14162 100644
--- 
a/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockDelete.java
+++ 
b/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockDelete.java
@@ -126,6 +126,21 @@ public class SparseBlockDelete extends AutomatedTestBase
        public void testSparseBlockMCSC3()  {
                runSparseBlockDeleteTest(SparseBlock.Type.MCSC, sparsity3);
        }
+
+       @Test
+       public void testSparseBlockCSC1()  {
+               runSparseBlockDeleteTest(SparseBlock.Type.CSC, sparsity1);
+       }
+
+       @Test
+       public void testSparseBlockCSC2()  {
+               runSparseBlockDeleteTest(SparseBlock.Type.CSC, sparsity2);
+       }
+
+       @Test
+       public void testSparseBlockCSC3()  {
+               runSparseBlockDeleteTest(SparseBlock.Type.CSC, sparsity3);
+       }
        
        private void runSparseBlockDeleteTest( SparseBlock.Type btype, double 
sparsity)
        {
diff --git 
a/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockGetFirstIndex.java
 
b/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockGetFirstIndex.java
index 5816ba6b26..aee6ffa7c4 100644
--- 
a/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockGetFirstIndex.java
+++ 
b/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockGetFirstIndex.java
@@ -19,14 +19,15 @@
 
 package org.apache.sysds.test.component.sparse;
 
-import org.apache.sysds.runtime.data.SparseBlockDCSR;
 import org.junit.Assert;
 import org.junit.Test;
 import org.apache.sysds.runtime.data.SparseBlock;
 import org.apache.sysds.runtime.data.SparseBlockCOO;
+import org.apache.sysds.runtime.data.SparseBlockCSC;
 import org.apache.sysds.runtime.data.SparseBlockCSR;
-import org.apache.sysds.runtime.data.SparseBlockMCSR;
+import org.apache.sysds.runtime.data.SparseBlockDCSR;
 import org.apache.sysds.runtime.data.SparseBlockMCSC;
+import org.apache.sysds.runtime.data.SparseBlockMCSR;
 import org.apache.sysds.runtime.matrix.data.MatrixBlock;
 import org.apache.sysds.runtime.util.DataConverter;
 import org.apache.sysds.test.AutomatedTestBase;
@@ -281,6 +282,51 @@ public class SparseBlockGetFirstIndex extends 
AutomatedTestBase
        public void testSparseBlockMCSC3LTE()  {
                runSparseBlockGetFirstIndexTest(SparseBlock.Type.MCSC, 
sparsity3, IndexType.LTE);
        }
+
+       @Test
+       public void testSparseBlockCSC1GT()  {
+               runSparseBlockGetFirstIndexTest(SparseBlock.Type.CSC, 
sparsity1, IndexType.GT);
+       }
+
+       @Test
+       public void testSparseBlockCSC2GT()  {
+               runSparseBlockGetFirstIndexTest(SparseBlock.Type.CSC, 
sparsity2, IndexType.GT);
+       }
+
+       @Test
+       public void testSparseBlockCSC3GT()  {
+               runSparseBlockGetFirstIndexTest(SparseBlock.Type.CSC, 
sparsity3, IndexType.GT);
+       }
+
+       @Test
+       public void testSparseBlockCSC1GTE()  {
+               runSparseBlockGetFirstIndexTest(SparseBlock.Type.CSC, 
sparsity1, IndexType.GTE);
+       }
+
+       @Test
+       public void testSparseBlockCSC2GTE()  {
+               runSparseBlockGetFirstIndexTest(SparseBlock.Type.CSC, 
sparsity2, IndexType.GTE);
+       }
+
+       @Test
+       public void testSparseBlockCSC3GTE()  {
+               runSparseBlockGetFirstIndexTest(SparseBlock.Type.CSC, 
sparsity3, IndexType.GTE);
+       }
+
+       @Test
+       public void testSparseBlockCSC1LTE()  {
+               runSparseBlockGetFirstIndexTest(SparseBlock.Type.CSC, 
sparsity1, IndexType.LTE);
+       }
+
+       @Test
+       public void testSparseBlockCSC2LTE()  {
+               runSparseBlockGetFirstIndexTest(SparseBlock.Type.CSC, 
sparsity2, IndexType.LTE);
+       }
+
+       @Test
+       public void testSparseBlockCSC3LTE()  {
+               runSparseBlockGetFirstIndexTest(SparseBlock.Type.CSC, 
sparsity3, IndexType.LTE);
+       }
        
        private void runSparseBlockGetFirstIndexTest( SparseBlock.Type btype, 
double sparsity, IndexType itype)
        {
@@ -309,6 +355,9 @@ public class SparseBlockGetFirstIndex extends 
AutomatedTestBase
                                case MCSC:
                                        sblock = new SparseBlockMCSC(srtmp, 
cols);
                                        break;
+                               case CSC:
+                                       sblock = new SparseBlockCSC(srtmp, 
cols);
+                                       break;
                        }
 
                        //check for correct number of non-zeros
diff --git 
a/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockGetSet.java 
b/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockGetSet.java
index 2f357d3c8a..a73256d6c4 100644
--- 
a/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockGetSet.java
+++ 
b/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockGetSet.java
@@ -19,15 +19,16 @@
 
 package org.apache.sysds.test.component.sparse;
 
-import org.apache.sysds.runtime.data.SparseBlockDCSR;
-import org.apache.sysds.runtime.data.SparseBlockFactory;
 import org.junit.Assert;
 import org.junit.Test;
 import org.apache.sysds.runtime.data.SparseBlock;
 import org.apache.sysds.runtime.data.SparseBlockCOO;
+import org.apache.sysds.runtime.data.SparseBlockCSC;
 import org.apache.sysds.runtime.data.SparseBlockCSR;
-import org.apache.sysds.runtime.data.SparseBlockMCSR;
+import org.apache.sysds.runtime.data.SparseBlockDCSR;
+import org.apache.sysds.runtime.data.SparseBlockFactory;
 import org.apache.sysds.runtime.data.SparseBlockMCSC;
+import org.apache.sysds.runtime.data.SparseBlockMCSR;
 import org.apache.sysds.runtime.matrix.data.MatrixBlock;
 import org.apache.sysds.runtime.util.DataConverter;
 import org.apache.sysds.runtime.util.LongLongDoubleHashMap;
@@ -278,19 +279,62 @@ public class SparseBlockGetSet extends AutomatedTestBase
        }
 
        @Test
-       public void testSparseBlockMCSC2Rand()  {
+       public void testSparseBlockMCSC2Rand() {
                runSparseBlockGetSetTest(SparseBlock.Type.MCSC, sparsity2, 
InitType.RAND_SET);
        }
 
        @Test
-       public void testSparseBlockMCSC3Rand()  {
+       public void testSparseBlockMCSC3Rand() {
                runSparseBlockGetSetTest(SparseBlock.Type.MCSC, sparsity3, 
InitType.RAND_SET);
        }
-       
-       private void runSparseBlockGetSetTest( SparseBlock.Type btype, double 
sparsity, InitType itype)
-       {
-               try
-               {
+
+       @Test
+       public void testSparseBlockCSC1Bulk() {
+               runSparseBlockGetSetTest(SparseBlock.Type.CSC, sparsity1, 
InitType.BULK);
+       }
+
+       @Test
+       public void testSparseBlockCSC2Bulk() {
+               runSparseBlockGetSetTest(SparseBlock.Type.CSC, sparsity2, 
InitType.BULK);
+       }
+
+       @Test
+       public void testSparseBlockCSC3Bulk() {
+               runSparseBlockGetSetTest(SparseBlock.Type.CSC, sparsity3, 
InitType.BULK);
+       }
+
+       @Test
+       public void testSparseBlockCSC1Seq() {
+               runSparseBlockGetSetTest(SparseBlock.Type.CSC, sparsity1, 
InitType.SEQ_SET);
+       }
+
+       @Test
+       public void testSparseBlockCSC2Seq() {
+               runSparseBlockGetSetTest(SparseBlock.Type.CSC, sparsity2, 
InitType.SEQ_SET);
+       }
+
+       @Test
+       public void testSparseBlockCSC3Seq() {
+               runSparseBlockGetSetTest(SparseBlock.Type.CSC, sparsity3, 
InitType.SEQ_SET);
+       }
+
+       @Test
+       public void testSparseBlockCSC1Rand() {
+               runSparseBlockGetSetTest(SparseBlock.Type.CSC, sparsity1, 
InitType.RAND_SET);
+       }
+
+       @Test
+       public void testSparseBlockCSC2Rand() {
+               runSparseBlockGetSetTest(SparseBlock.Type.CSC, sparsity2, 
InitType.RAND_SET);
+       }
+
+       @Test
+       public void testSparseBlockCSC3Rand() {
+               runSparseBlockGetSetTest(SparseBlock.Type.CSC, sparsity3, 
InitType.RAND_SET);
+       }
+
+       private void runSparseBlockGetSetTest(SparseBlock.Type btype, double 
sparsity, InitType itype) {
+               try {
                        //data generation
                        double[][] A = getRandomMatrix(rows, cols, -10, 10, 
sparsity, 7654321);
 
@@ -303,11 +347,24 @@ public class SparseBlockGetSet extends AutomatedTestBase
                        }
                        else if( itype == InitType.SEQ_SET || itype == 
InitType.RAND_SET ) {
                                switch( btype ) {
-                                       case MCSR: sblock = new 
SparseBlockMCSR(rows, cols); break;
-                                       case CSR: sblock = new 
SparseBlockCSR(rows, cols); break;
-                                       case COO: sblock = new 
SparseBlockCOO(rows, cols); break;
-                                       case DCSR: sblock = new 
SparseBlockDCSR(rows, cols); break;
-                                       case MCSC: sblock = new 
SparseBlockMCSC(rows, cols); break;
+                                       case MCSR:
+                                               sblock = new 
SparseBlockMCSR(rows, cols);
+                                               break;
+                                       case CSR:
+                                               sblock = new 
SparseBlockCSR(rows, cols);
+                                               break;
+                                       case COO:
+                                               sblock = new 
SparseBlockCOO(rows, cols);
+                                               break;
+                                       case DCSR:
+                                               sblock = new 
SparseBlockDCSR(rows, cols);
+                                               break;
+                                       case MCSC:
+                                               sblock = new 
SparseBlockMCSC(rows, cols);
+                                               break;
+                                       case CSC:
+                                               sblock = new 
SparseBlockCSC(rows, cols);
+                                               break;
                                }
 
                                if(itype == InitType.SEQ_SET) {
diff --git 
a/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockIndexRange.java
 
b/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockIndexRange.java
index d51f9d1f6d..2125be294f 100644
--- 
a/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockIndexRange.java
+++ 
b/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockIndexRange.java
@@ -206,6 +206,36 @@ public class SparseBlockIndexRange extends 
AutomatedTestBase
        public void testSparseBlockMCSC3Insert()  {
                runSparseBlockIndexRangeTest(SparseBlock.Type.MCSC, sparsity3, 
UpdateType.INSERT);
        }
+
+       @Test
+       public void testSparseBlockCSC1Delete()  {
+               runSparseBlockIndexRangeTest(SparseBlock.Type.CSC, sparsity1, 
UpdateType.DELETE);
+       }
+
+       @Test
+       public void testSparseBlockCSC2Delete()  {
+               runSparseBlockIndexRangeTest(SparseBlock.Type.CSC, sparsity2, 
UpdateType.DELETE);
+       }
+
+       @Test
+       public void testSparseBlockCSC3Delete()  {
+               runSparseBlockIndexRangeTest(SparseBlock.Type.CSC, sparsity3, 
UpdateType.DELETE);
+       }
+
+       @Test
+       public void testSparseBlockCSC1Insert()  {
+               runSparseBlockIndexRangeTest(SparseBlock.Type.CSC, sparsity1, 
UpdateType.INSERT);
+       }
+
+       @Test
+       public void testSparseBlockCSC2Insert()  {
+               runSparseBlockIndexRangeTest(SparseBlock.Type.CSC, sparsity2, 
UpdateType.INSERT);
+       }
+
+       @Test
+       public void testSparseBlockCSC3Insert()  {
+               runSparseBlockIndexRangeTest(SparseBlock.Type.CSC, sparsity3, 
UpdateType.INSERT);
+       }
        
        private void runSparseBlockIndexRangeTest( SparseBlock.Type btype, 
double sparsity, UpdateType utype)
        {
@@ -224,6 +254,7 @@ public class SparseBlockIndexRange extends AutomatedTestBase
                                case COO: sblock = new SparseBlockCOO(srtmp); 
break;
                                case DCSR: sblock = new SparseBlockDCSR(srtmp); 
break;
                                case MCSC: sblock = new SparseBlockMCSC(srtmp, 
cols); break;
+                               case CSC: sblock = new SparseBlockCSC(srtmp, 
cols); break;
                        }
                        
                        //delete range per row via set
diff --git 
a/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockIterator.java 
b/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockIterator.java
index 736f981025..57cee617fb 100644
--- 
a/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockIterator.java
+++ 
b/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockIterator.java
@@ -202,6 +202,38 @@ public class SparseBlockIterator extends AutomatedTestBase 
{
                runSparseBlockIteratorTest(SparseBlock.Type.MCSC, sparsity3, 
true);
        }
 
+       @Test
+       public void testSparseBlockCSC1Full() {
+               runSparseBlockIteratorTest(SparseBlock.Type.CSC, sparsity1, 
false);
+       }
+
+       @Test
+       public void testSparseBlockCSC2Full() {
+               runSparseBlockIteratorTest(SparseBlock.Type.CSC, sparsity2, 
false);
+       }
+
+       @Test
+       public void testSparseBlockCSC3Full() {
+               runSparseBlockIteratorTest(SparseBlock.Type.CSC, sparsity3, 
false);
+       }
+
+       @Test
+       public void testSparseBlockCSC1Partial() {
+               runSparseBlockIteratorTest(SparseBlock.Type.CSC, sparsity1, 
true);
+       }
+
+       @Test
+       public void testSparseBlockCSC2Partial() {
+               runSparseBlockIteratorTest(SparseBlock.Type.CSC, sparsity2, 
true);
+       }
+
+       @Test
+       public void testSparseBlockCSC3Partial() {
+               runSparseBlockIteratorTest(SparseBlock.Type.CSC, sparsity3, 
true);
+       }
+
+
+
        private void runSparseBlockIteratorTest(SparseBlock.Type btype, double 
sparsity, boolean partial) {
                try {
                        //data generation
diff --git 
a/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockMemEstimate.java
 
b/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockMemEstimate.java
index 1c0c2aa7c5..8269e1b0e0 100644
--- 
a/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockMemEstimate.java
+++ 
b/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockMemEstimate.java
@@ -59,6 +59,7 @@ public class SparseBlockMemEstimate extends AutomatedTestBase
                double memMCSC = 
SparseBlockFactory.estimateSizeSparseInMemory(SparseBlock.Type.MCSC, rows, 
cols, sparsity);
                double memMCSR = 
SparseBlockFactory.estimateSizeSparseInMemory(SparseBlock.Type.MCSR, rows, 
cols, sparsity);
                double memCSR = 
SparseBlockFactory.estimateSizeSparseInMemory(SparseBlock.Type.CSR, rows, cols, 
sparsity);
+               double memCSC = 
SparseBlockFactory.estimateSizeSparseInMemory(SparseBlock.Type.CSC, rows, cols, 
sparsity);
                double memCOO = 
SparseBlockFactory.estimateSizeSparseInMemory(SparseBlock.Type.COO, rows, cols, 
sparsity);
                double memDCSR = 
SparseBlockFactory.estimateSizeSparseInMemory(SparseBlock.Type.DCSR, rows, 
cols, sparsity);
                double memDense = MatrixBlock.estimateSizeDenseInMemory(rows, 
cols);
@@ -70,6 +71,8 @@ public class SparseBlockMemEstimate extends AutomatedTestBase
                        Assert.fail("SparseBlockMCSR memory estimate <= 0.");
                if( memCSR  <= 0 )
                        Assert.fail("SparseBlockCSR memory estimate <= 0.");
+               if( memCSC  <= 0 )
+                       Assert.fail("SparseBlockCSC memory estimate <= 0.");
                if( memCOO  <= 0 )
                        Assert.fail("SparseBlockCOO memory estimate <= 0.");
                if( memDCSR  <= 0 )
diff --git 
a/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockMerge.java 
b/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockMerge.java
index 7c061eaf27..acba9ac938 100644
--- a/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockMerge.java
+++ b/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockMerge.java
@@ -134,22 +134,42 @@ public class SparseBlockMerge extends AutomatedTestBase
        }
 
        @Test
-       public void testMergeMCSR_MCSC_2()  {
+       public void testMergeMCSR_MCSC_2() {
                runSparseBlockMergeTest(SparseBlock.Type.MCSR, 
SparseBlock.Type.MCSC, sparsity2);
        }
 
        @Test
-       public void testMergeMCSR_MCSC_3()  {
+       public void testMergeMCSR_MCSC_3() {
                runSparseBlockMergeTest(SparseBlock.Type.MCSR, 
SparseBlock.Type.MCSC, sparsity3);
        }
-       
+
+       @Test
+       public void testMergeMCSR_CSC_0() {
+               runSparseBlockMergeTest(SparseBlock.Type.MCSR, 
SparseBlock.Type.CSC, sparsity0);
+       }
+
+       @Test
+       public void testMergeMCSR_CSC_1() {
+               runSparseBlockMergeTest(SparseBlock.Type.MCSR, 
SparseBlock.Type.CSC, sparsity1);
+       }
+
+       @Test
+       public void testMergeMCSR_CSC_2() {
+               runSparseBlockMergeTest(SparseBlock.Type.MCSR, 
SparseBlock.Type.CSC, sparsity2);
+       }
+
+       @Test
+       public void testMergeMCSR_CSC_3() {
+               runSparseBlockMergeTest(SparseBlock.Type.MCSR, 
SparseBlock.Type.CSC, sparsity3);
+       }
+
        @Test
-       public void testMergeCSR_CSR_0()  {
+       public void testMergeCSR_CSR_0() {
                runSparseBlockMergeTest(SparseBlock.Type.CSR, 
SparseBlock.Type.CSR, sparsity0);
        }
-       
+
        @Test
-       public void testMergeCSR_CSR_1()  {
+       public void testMergeCSR_CSR_1() {
                runSparseBlockMergeTest(SparseBlock.Type.CSR, 
SparseBlock.Type.CSR, sparsity1);
        }
        
@@ -234,22 +254,42 @@ public class SparseBlockMerge extends AutomatedTestBase
        }
 
        @Test
-       public void testMergeCSR_MCSC_2()  {
+       public void testMergeCSR_MCSC_2() {
                runSparseBlockMergeTest(SparseBlock.Type.CSR, 
SparseBlock.Type.MCSC, sparsity2);
        }
 
        @Test
-       public void testMergeCSR_MCSC_3()  {
+       public void testMergeCSR_MCSC_3() {
                runSparseBlockMergeTest(SparseBlock.Type.CSR, 
SparseBlock.Type.MCSC, sparsity3);
        }
-       
+
        @Test
-       public void testMergeCOO_COO_0()  {
+       public void testMergeCSR_CSC_0() {
+               runSparseBlockMergeTest(SparseBlock.Type.CSR, 
SparseBlock.Type.CSC, sparsity0);
+       }
+
+       @Test
+       public void testMergeCSR_CSC_1() {
+               runSparseBlockMergeTest(SparseBlock.Type.CSR, 
SparseBlock.Type.CSC, sparsity1);
+       }
+
+       @Test
+       public void testMergeCSR_CSC_2() {
+               runSparseBlockMergeTest(SparseBlock.Type.CSR, 
SparseBlock.Type.CSC, sparsity2);
+       }
+
+       @Test
+       public void testMergeCSR_CSC_3() {
+               runSparseBlockMergeTest(SparseBlock.Type.CSR, 
SparseBlock.Type.CSC, sparsity3);
+       }
+
+       @Test
+       public void testMergeCOO_COO_0() {
                runSparseBlockMergeTest(SparseBlock.Type.COO, 
SparseBlock.Type.COO, sparsity0);
        }
-       
+
        @Test
-       public void testMergeCOO_COO_1()  {
+       public void testMergeCOO_COO_1() {
                runSparseBlockMergeTest(SparseBlock.Type.COO, 
SparseBlock.Type.COO, sparsity1);
        }
        
@@ -334,22 +374,42 @@ public class SparseBlockMerge extends AutomatedTestBase
        }
 
        @Test
-       public void testMergeCOO_MCSC_2()  {
+       public void testMergeCOO_MCSC_2() {
                runSparseBlockMergeTest(SparseBlock.Type.COO, 
SparseBlock.Type.MCSC, sparsity2);
        }
 
        @Test
-       public void testMergeCOO_MCSC_3()  {
+       public void testMergeCOO_MCSC_3() {
                runSparseBlockMergeTest(SparseBlock.Type.COO, 
SparseBlock.Type.MCSC, sparsity3);
        }
 
        @Test
-       public void testMergeDCSR_DCSR_0()  {
+       public void testMergeCOO_CSC_0() {
+               runSparseBlockMergeTest(SparseBlock.Type.COO, 
SparseBlock.Type.CSC, sparsity0);
+       }
+
+       @Test
+       public void testMergeCOO_CSC_1() {
+               runSparseBlockMergeTest(SparseBlock.Type.COO, 
SparseBlock.Type.CSC, sparsity1);
+       }
+
+       @Test
+       public void testMergeCOO_CSC_2() {
+               runSparseBlockMergeTest(SparseBlock.Type.COO, 
SparseBlock.Type.CSC, sparsity2);
+       }
+
+       @Test
+       public void testMergeCOO_CSC_3() {
+               runSparseBlockMergeTest(SparseBlock.Type.COO, 
SparseBlock.Type.CSC, sparsity3);
+       }
+
+       @Test
+       public void testMergeDCSR_DCSR_0() {
                runSparseBlockMergeTest(SparseBlock.Type.DCSR, 
SparseBlock.Type.DCSR, sparsity0);
        }
 
        @Test
-       public void testMergeDCSR_DCSR_1()  {
+       public void testMergeDCSR_DCSR_1() {
                runSparseBlockMergeTest(SparseBlock.Type.DCSR, 
SparseBlock.Type.DCSR, sparsity1);
        }
 
@@ -434,22 +494,42 @@ public class SparseBlockMerge extends AutomatedTestBase
        }
 
        @Test
-       public void testMergeDCSR_MCSC_2()  {
+       public void testMergeDCSR_MCSC_2() {
                runSparseBlockMergeTest(SparseBlock.Type.DCSR, 
SparseBlock.Type.MCSC, sparsity2);
        }
 
        @Test
-       public void testMergeDCSR_MCSC_3()  {
+       public void testMergeDCSR_MCSC_3() {
                runSparseBlockMergeTest(SparseBlock.Type.DCSR, 
SparseBlock.Type.MCSC, sparsity3);
        }
 
        @Test
-       public void testMergeMCSC_MCSC_0()  {
+       public void testMergeDCSR_CSC_0() {
+               runSparseBlockMergeTest(SparseBlock.Type.DCSR, 
SparseBlock.Type.CSC, sparsity0);
+       }
+
+       @Test
+       public void testMergeDCSR_CSC_1() {
+               runSparseBlockMergeTest(SparseBlock.Type.DCSR, 
SparseBlock.Type.CSC, sparsity1);
+       }
+
+       @Test
+       public void testMergeDCSR_CSC_2() {
+               runSparseBlockMergeTest(SparseBlock.Type.DCSR, 
SparseBlock.Type.CSC, sparsity2);
+       }
+
+       @Test
+       public void testMergeDCSR_CSC_3() {
+               runSparseBlockMergeTest(SparseBlock.Type.DCSR, 
SparseBlock.Type.CSC, sparsity3);
+       }
+
+       @Test
+       public void testMergeMCSC_MCSC_0() {
                runSparseBlockMergeTest(SparseBlock.Type.MCSC, 
SparseBlock.Type.MCSC, sparsity0);
        }
 
        @Test
-       public void testMergeMCSC_MCSC_1()  {
+       public void testMergeMCSC_MCSC_1() {
                runSparseBlockMergeTest(SparseBlock.Type.MCSC, 
SparseBlock.Type.MCSC, sparsity1);
        }
 
@@ -494,43 +574,179 @@ public class SparseBlockMerge extends AutomatedTestBase
        }
 
        @Test
-       public void testMergeMCSC_MCSR_2()  {
+       public void testMergeMCSC_MCSR_2() {
                runSparseBlockMergeTest(SparseBlock.Type.MCSC, 
SparseBlock.Type.MCSR, sparsity2);
        }
 
        @Test
-       public void testMergeMCSC_MCSR_3()  {
+       public void testMergeMCSC_MCSR_3() {
                runSparseBlockMergeTest(SparseBlock.Type.MCSC, 
SparseBlock.Type.MCSR, sparsity3);
        }
 
        @Test
-       public void testMergeMCSC_COO_0()  {
+       public void testMergeMCSC_DCSR_0() {
+               runSparseBlockMergeTest(SparseBlock.Type.MCSC, 
SparseBlock.Type.DCSR, sparsity0);
+       }
+
+       @Test
+       public void testMergeMCSC_DCSR_1() {
+               runSparseBlockMergeTest(SparseBlock.Type.MCSC, 
SparseBlock.Type.DCSR, sparsity1);
+       }
+
+       @Test
+       public void testMergeMCSC_DCSR_2() {
+               runSparseBlockMergeTest(SparseBlock.Type.MCSC, 
SparseBlock.Type.DCSR, sparsity2);
+       }
+
+       @Test
+       public void testMergeMCSC_DCSR_3() {
+               runSparseBlockMergeTest(SparseBlock.Type.MCSC, 
SparseBlock.Type.DCSR, sparsity3);
+       }
+
+       @Test
+       public void testMergeMCSC_COO_0() {
                runSparseBlockMergeTest(SparseBlock.Type.MCSC, 
SparseBlock.Type.COO, sparsity0);
        }
 
        @Test
-       public void testMergeMCSC_COO_1()  {
+       public void testMergeMCSC_COO_1() {
                runSparseBlockMergeTest(SparseBlock.Type.MCSC, 
SparseBlock.Type.COO, sparsity1);
        }
 
        @Test
-       public void testMergeMCSC_COO_2()  {
+       public void testMergeMCSC_COO_2() {
                runSparseBlockMergeTest(SparseBlock.Type.MCSC, 
SparseBlock.Type.COO, sparsity2);
        }
 
        @Test
-       public void testMergeMCSC_COO_3()  {
+       public void testMergeMCSC_COO_3() {
                runSparseBlockMergeTest(SparseBlock.Type.MCSC, 
SparseBlock.Type.COO, sparsity3);
        }
 
+       @Test
+       public void testMergeMCSC_CSC_0() {
+               runSparseBlockMergeTest(SparseBlock.Type.MCSC, 
SparseBlock.Type.CSC, sparsity0);
+       }
+
+       @Test
+       public void testMergeMCSC_CSC_1() {
+               runSparseBlockMergeTest(SparseBlock.Type.MCSC, 
SparseBlock.Type.CSC, sparsity1);
+       }
+
+       @Test
+       public void testMergeMCSC_CSC_2() {
+               runSparseBlockMergeTest(SparseBlock.Type.MCSC, 
SparseBlock.Type.CSC, sparsity2);
+       }
+
+       @Test
+       public void testMergeMCSC_CSC_3() {
+               runSparseBlockMergeTest(SparseBlock.Type.MCSC, 
SparseBlock.Type.CSC, sparsity3);
+       }
+
+       @Test
+       public void testMergeCSC_CSC_0() {
+               runSparseBlockMergeTest(SparseBlock.Type.CSC, 
SparseBlock.Type.CSC, sparsity0);
+       }
 
+       @Test
+       public void testMergeCSC_CSC_1() {
+               runSparseBlockMergeTest(SparseBlock.Type.CSC, 
SparseBlock.Type.CSC, sparsity1);
+       }
+
+       @Test
+       public void testMergeCSC_CSC_2() {
+               runSparseBlockMergeTest(SparseBlock.Type.CSC, 
SparseBlock.Type.CSC, sparsity2);
+       }
+
+       @Test
+       public void testMergeCSC_CSC_3() {
+               runSparseBlockMergeTest(SparseBlock.Type.CSC, 
SparseBlock.Type.CSC, sparsity3);
+       }
+
+       @Test
+       public void testMergeCSC_MCSC_0() {
+               runSparseBlockMergeTest(SparseBlock.Type.CSC, 
SparseBlock.Type.MCSC, sparsity0);
+       }
+
+       @Test
+       public void testMergeCSC_MCSC_1() {
+               runSparseBlockMergeTest(SparseBlock.Type.CSC, 
SparseBlock.Type.MCSC, sparsity1);
+       }
+
+       @Test
+       public void testMergeCSC_MCSC_2() {
+               runSparseBlockMergeTest(SparseBlock.Type.CSC, 
SparseBlock.Type.MCSC, sparsity2);
+       }
+
+       @Test
+       public void testMergeCSC_MCSC_3() {
+               runSparseBlockMergeTest(SparseBlock.Type.CSC, 
SparseBlock.Type.MCSC, sparsity3);
+       }
+
+       @Test
+       public void testMergeCSC_CSR_0() {
+               runSparseBlockMergeTest(SparseBlock.Type.CSC, 
SparseBlock.Type.CSR, sparsity0);
+       }
+
+       @Test
+       public void testMergeCSC_CSR_1() {
+               runSparseBlockMergeTest(SparseBlock.Type.CSC, 
SparseBlock.Type.CSR, sparsity1);
+       }
+
+       @Test
+       public void testMergeCSC_CSR_2() {
+               runSparseBlockMergeTest(SparseBlock.Type.CSC, 
SparseBlock.Type.CSR, sparsity2);
+       }
+
+       @Test
+       public void testMergeCSC_CSR_3() {
+               runSparseBlockMergeTest(SparseBlock.Type.CSC, 
SparseBlock.Type.CSR, sparsity3);
+       }
+
+       @Test
+       public void testMergeCSC_MCSR_0() {
+               runSparseBlockMergeTest(SparseBlock.Type.CSC, 
SparseBlock.Type.MCSR, sparsity0);
+       }
+
+       @Test
+       public void testMergeCSC_MCSR_1() {
+               runSparseBlockMergeTest(SparseBlock.Type.CSC, 
SparseBlock.Type.MCSR, sparsity1);
+       }
+
+       @Test
+       public void testMergeCSC_MCSR_2() {
+               runSparseBlockMergeTest(SparseBlock.Type.CSC, 
SparseBlock.Type.MCSR, sparsity2);
+       }
+
+       @Test
+       public void testMergeCSC_MCSR_3() {
+               runSparseBlockMergeTest(SparseBlock.Type.CSC, 
SparseBlock.Type.MCSR, sparsity3);
+       }
+
+       @Test
+       public void testMergeCSC_COO_0() {
+               runSparseBlockMergeTest(SparseBlock.Type.CSC, 
SparseBlock.Type.COO, sparsity0);
+       }
+
+       @Test
+       public void testMergeCSC_COO_1() {
+               runSparseBlockMergeTest(SparseBlock.Type.CSC, 
SparseBlock.Type.COO, sparsity1);
+       }
+
+       @Test
+       public void testMergeCSC_COO_2() {
+               runSparseBlockMergeTest(SparseBlock.Type.CSC, 
SparseBlock.Type.COO, sparsity2);
+       }
+
+       @Test
+       public void testMergeCSC_COO_3() {
+               runSparseBlockMergeTest(SparseBlock.Type.CSC, 
SparseBlock.Type.COO, sparsity3);
+       }
 
-       private void runSparseBlockMergeTest( SparseBlock.Type btype1, 
SparseBlock.Type btype2, double sparsity)
-       {
-               try
-               {
+       private void runSparseBlockMergeTest(SparseBlock.Type btype1, 
SparseBlock.Type btype2, double sparsity) {
+               try {
                        //data generation
-                       double[][] A = getRandomMatrix(rows, cols, -10, 10, 
sparsity, 1234); 
+                       double[][] A = getRandomMatrix(rows, cols, -10, 10, 
sparsity, 1234);
                        double[][] B1 = new double[A.length][];
                        double[][] B2 = new double[A.length][];
                        for(int i=0; i<A.length; i++) {
diff --git 
a/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockScan.java 
b/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockScan.java
index 8163b49a6e..6729580209 100644
--- a/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockScan.java
+++ b/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockScan.java
@@ -120,6 +120,21 @@ public class SparseBlockScan extends AutomatedTestBase
        public void testSparseBlockMCSC3Full()  {
                runSparseBlockScanTest(SparseBlock.Type.MCSC, sparsity3);
        }
+
+       @Test
+       public void testSparseBlockCSC1Full()  {
+               runSparseBlockScanTest(SparseBlock.Type.CSC, sparsity1);
+       }
+
+       @Test
+       public void testSparseBlockCSC2Full()  {
+               runSparseBlockScanTest(SparseBlock.Type.CSC, sparsity2);
+       }
+
+       @Test
+       public void testSparseBlockCSC3Full()  {
+               runSparseBlockScanTest(SparseBlock.Type.CSC, sparsity3);
+       }
        
        private void runSparseBlockScanTest( SparseBlock.Type btype, double 
sparsity)
        {
@@ -138,6 +153,7 @@ public class SparseBlockScan extends AutomatedTestBase
                                case COO: sblock = new SparseBlockCOO(srtmp); 
break;
                                case DCSR: sblock = new SparseBlockDCSR(srtmp); 
break;
                                case MCSC: sblock = new SparseBlockMCSC(srtmp); 
break;
+                               case CSC: sblock = new SparseBlockCSC(srtmp); 
break;
                        }
                        
                        //check for correct number of non-zeros
diff --git 
a/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockSize.java 
b/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockSize.java
index edc84098ca..7059dfe333 100644
--- a/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockSize.java
+++ b/src/test/java/org/apache/sysds/test/component/sparse/SparseBlockSize.java
@@ -125,6 +125,21 @@ public class SparseBlockSize extends AutomatedTestBase
        public void testSparseBlockMCSC3(){
                runSparseBlockSizeTest(SparseBlock.Type.MCSC, _sparsity3);
        }
+
+       @Test
+       public void testSparseBlockCSC1(){
+               runSparseBlockSizeTest(SparseBlock.Type.CSC, _sparsity1);
+       }
+
+       @Test
+       public void testSparseBlockCSC2(){
+               runSparseBlockSizeTest(SparseBlock.Type.CSC, _sparsity2);
+       }
+
+       @Test
+       public void testSparseBlockCSC3(){
+               runSparseBlockSizeTest(SparseBlock.Type.CSC, _sparsity3);
+       }
        
        @Test
        public void testSparseBlockMCSRFixed1(){
@@ -143,6 +158,12 @@ public class SparseBlockSize extends AutomatedTestBase
                double[][] A = getFixedData1();
                runSparseBlockSizeTest(A, 0, 4, 0, 6, SparseBlock.Type.COO);
        }
+
+       @Test
+       public void testSparseBlockCSCFixed1(){
+               double[][] A = getFixedData1();
+               runSparseBlockSizeTest(A, 0, 4, 0, 6, SparseBlock.Type.CSC);
+       }
        
        @Test
        public void testSparseBlockMCSRFixed2(){
@@ -155,6 +176,12 @@ public class SparseBlockSize extends AutomatedTestBase
                double[][] A = getFixedData2();
                runSparseBlockSizeTest(A, 0, 4, 2, 4, SparseBlock.Type.CSR);
        }
+
+       @Test
+       public void testSparseBlockCSCFixed2(){
+               double[][] A = getFixedData2();
+               runSparseBlockSizeTest(A, 0, 4, 2, 4, SparseBlock.Type.CSC);
+       }
        
        @Test
        public void testSparseBlockCOOFixed2(){
@@ -173,6 +200,12 @@ public class SparseBlockSize extends AutomatedTestBase
                double[][] A = getFixedData3();
                runSparseBlockSizeTest(A, 0, 4, 3, 3, SparseBlock.Type.CSR);
        }
+
+       @Test
+       public void testSparseBlockCSCFixed3(){
+               double[][] A = getFixedData3();
+               runSparseBlockSizeTest(A, 0, 4, 3, 3, SparseBlock.Type.CSC);
+       }
        
        @Test
        public void testSparseBlockCOOFixed3(){
@@ -204,6 +237,7 @@ public class SparseBlockSize extends AutomatedTestBase
                                case COO: sblock = new SparseBlockCOO(srtmp); 
break;
                                case DCSR: sblock = new SparseBlockDCSR(srtmp); 
break;
                                case MCSC: sblock = new SparseBlockMCSC(srtmp, 
cols); break;
+                               case CSC: sblock = new SparseBlockCSC(srtmp, 
cols); break;
                        }
                        
                        //prepare summary statistics nnz

Reply via email to