| /* |
| * 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.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.sysds.runtime.matrix.data.IJV; |
| import org.apache.sysds.runtime.util.SortUtils; |
| import org.apache.sysds.runtime.util.UtilFunctions; |
| import org.apache.sysds.utils.MemoryEstimates; |
| |
| import java.util.Arrays; |
| import java.util.Iterator; |
| |
| import static java.util.stream.IntStream.range; |
| |
| public class SparseBlockDCSR extends SparseBlock |
| { |
| private static final long serialVersionUID = 456844244252549431L; |
| |
| private static final Log LOG = LogFactory.getLog(SparseBlockDCSR.class.getName()); |
| |
| private int[] _rowidx = null; // row index array (size: >= |
| private int[] _rowptr = null; // |
| private int[] _colidx = null; // column index array (size: >=nnz) |
| private double[] _values = null; // value array (size: >=nnz) |
| private int _size = 0; // actual nnz |
| private int _rlen = 0; // number of rows |
| private int _nnzr = 0; // number of nonzero rows |
| |
| public SparseBlockDCSR(int rlen) { |
| this(rlen, INIT_CAPACITY); |
| } |
| |
| public SparseBlockDCSR(int rlen, int capacity) { |
| //TODO: This allocates too much space (we care about number of non-empty rows) |
| LOG.warn("Allocating a DCSR-block using row-length. This will lead to significant overhead!"); |
| LOG.warn("If you want to initialize a sparse block using rlen, choose SparseBlockCSR instead!"); |
| |
| _rowidx = new int[rlen]; |
| _rowptr = new int[rlen + 1]; |
| _colidx = new int[capacity]; |
| _values = new double[capacity]; |
| _rlen = rlen; |
| _size = 0; |
| _nnzr = 0; |
| } |
| |
| public SparseBlockDCSR(int rlen, int capacity, int size, int nnzr){ |
| LOG.warn("Allocating a DCSR-block using row-length. This will lead to significant overhead!"); |
| _rowidx = new int[rlen]; |
| _rowptr = new int[rlen + 1]; |
| _colidx = new int[capacity]; |
| _values = new double[capacity]; |
| _rlen = rlen; |
| _size = size; |
| _nnzr = nnzr; |
| } |
| |
| public SparseBlockDCSR(int[] rowIdx, int[] rowPtr, int[] colIdx, double[] values, int rlen, int nnz, int nnzr){ |
| LOG.warn("Allocating a DCSR-block using row-length. This will lead to significant overhead!"); |
| _rowidx = rowIdx; |
| _rowptr = rowPtr; |
| _colidx = colIdx; |
| _values = values; |
| _rlen = rlen; |
| _size = nnz; |
| _nnzr = nnzr; |
| } |
| |
| /** |
| * Copy constructor sparse block abstraction. |
| * |
| * @param sblock sparse block to copy |
| */ |
| public SparseBlockDCSR(SparseBlock sblock) |
| { |
| long size = sblock.size(); |
| if( size > Integer.MAX_VALUE ) |
| throw new RuntimeException("SparseBlockDCSR supports nnz<=Integer.MAX_VALUE but got "+size); |
| |
| //special case SparseBlockDCSR |
| if( sblock instanceof SparseBlockDCSR ) { |
| SparseBlockDCSR ocsr = (SparseBlockDCSR)sblock; |
| _rowidx = Arrays.copyOf(ocsr._rowidx, ocsr._nnzr); |
| _rowptr = Arrays.copyOf(ocsr._rowptr, ocsr._nnzr+1); |
| _colidx = Arrays.copyOf(ocsr._colidx, ocsr._size); |
| _values = Arrays.copyOf(ocsr._values, ocsr._size); |
| _rlen = ocsr._rlen; |
| _nnzr = ocsr._nnzr; |
| _size = ocsr._size; |
| } |
| else if( sblock instanceof SparseBlockCSR ) { |
| // More efficient conversion from CSR to DCSR |
| int rlen = sblock.numRows(); |
| |
| SparseBlockCSR ocsr = (SparseBlockCSR)sblock; |
| _rowidx = range(0, rlen).filter(rowIdx -> !sblock.isEmpty(rowIdx)).toArray(); |
| _rowptr = new int[_rowidx.length + 1]; |
| _colidx = Arrays.copyOf(ocsr.indexes(), (int)ocsr.size()); |
| _values = Arrays.copyOf(ocsr.values(), (int)ocsr.size()); |
| _rlen = rlen; |
| _nnzr = _rowidx.length; |
| _size = (int)ocsr.size(); |
| |
| int vpos = 0; |
| for (int i = 0; i < _rowidx.length; i++) { |
| vpos += sblock.size(_rowidx[i]); |
| _rowptr[i+1] = vpos; |
| } |
| } |
| //general case SparseBlock |
| else { |
| int rlen = sblock.numRows(); |
| _rowidx = range(0, rlen).filter(rowIdx -> !sblock.isEmpty(rowIdx)).toArray(); |
| _rowptr = new int[_rowidx.length + 1]; |
| _colidx = new int[(int)size]; |
| _values = new double[(int)size]; |
| _rlen = rlen; |
| _nnzr = _rowidx.length; |
| _size = (int)size; |
| |
| int vpos = 0, rpos = 1; |
| for ( int rowIdx : _rowidx ) { |
| int apos = sblock.pos(rowIdx); |
| int alen = sblock.size(rowIdx); |
| int[] aix = sblock.indexes(rowIdx); |
| double[] avals = sblock.values(rowIdx); |
| System.arraycopy(aix, apos, _colidx, vpos, alen); |
| System.arraycopy(avals, apos, _values, vpos, alen); |
| vpos += alen; |
| _rowptr[rpos++] = vpos; |
| } |
| } |
| } |
| |
| /** |
| * Get the estimated in-memory size of the sparse block in CSR |
| * 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; // Memory overhead of the object |
| size += 4 + 4 + 4 + 4; // 3x int field + 0 (padding not necessary) |
| size += MemoryEstimates.intArrayCost(nrows); // rowidx array (row indices) |
| size += MemoryEstimates.intArrayCost(nrows+1); // rowptr array (row pointers) |
| size += MemoryEstimates.intArrayCost((long) lnnz); // colidx 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); |
| } |
| |
| @Override |
| public long getExactSizeInMemory() { |
| double size = 16; |
| size += 4 + 4 + 4 + 4; |
| size += MemoryEstimates.intArrayCost(_rowidx.length); |
| size += MemoryEstimates.intArrayCost(_rowptr.length); |
| size += MemoryEstimates.intArrayCost(_colidx.length); |
| size += MemoryEstimates.doubleArrayCost(_values.length); |
| |
| return (long) Math.min(size, Long.MAX_VALUE); |
| } |
| |
| /////////////////// |
| //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() { |
| return _rlen; |
| } |
| |
| @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 ) { |
| _size = 0; |
| _nnzr = 0; |
| _rlen = 0; |
| } |
| } |
| |
| @Override |
| public void reset(int ennz, int maxnnz) { |
| if( _size > 0 ) { |
| _size = 0; |
| _nnzr = 0; |
| _rlen = 0; |
| } |
| } |
| |
| @Override |
| public void reset(int r, int ennz, int maxnnz) { |
| deleteIndexRange(r, 0, Integer.MAX_VALUE); |
| } |
| |
| @Override |
| public long size() { |
| return _size; |
| } |
| |
| @Override |
| public int size(int r) { |
| int idx = Arrays.binarySearch(_rowidx, 0, _nnzr, r); |
| if (idx < 0) |
| return 0; |
| |
| return _rowptr[idx+1] - _rowptr[idx]; |
| } |
| |
| @Override |
| public long size(int rl, int ru) { |
| int lowerIdx = Arrays.binarySearch(_rowidx, 0, _nnzr, rl); |
| |
| if (lowerIdx < 0) |
| lowerIdx = -lowerIdx - 1; |
| |
| int upperIdx = Arrays.binarySearch(_rowidx, lowerIdx, _nnzr, ru); |
| |
| if (upperIdx < 0) |
| upperIdx = -upperIdx - 1; |
| |
| return _rowptr[upperIdx] - _rowptr[lowerIdx]; |
| } |
| |
| @Override |
| public long size(int rl, int ru, int cl, int cu) { |
| long nnz = 0; |
| |
| int lRowIdx = Arrays.binarySearch(_rowidx, 0, _nnzr, rl); |
| if (lRowIdx < 0) |
| lRowIdx = -lRowIdx - 1; |
| |
| int uRowIdx = Arrays.binarySearch(_rowidx, lRowIdx, _nnzr, ru); |
| if (uRowIdx < 0) |
| uRowIdx = -uRowIdx - 1; |
| |
| for (int rowIdx = lRowIdx; rowIdx < uRowIdx; rowIdx++) { |
| int clIdx = Arrays.binarySearch(_colidx, _rowptr[rowIdx], _rowptr[rowIdx+1], cl); |
| if (clIdx < 0) |
| clIdx = -clIdx - 1; |
| |
| int cuIdx = Arrays.binarySearch(_colidx, clIdx, _rowptr[rowIdx+1], cu); |
| if (cuIdx < 0) |
| cuIdx = -cuIdx - 1; |
| |
| nnz += cuIdx - clIdx; |
| } |
| return nnz; |
| } |
| |
| @Override |
| public boolean isEmpty(int r) { |
| return size(r) == 0; |
| } |
| |
| @Override |
| public int[] indexes(int r) { |
| return _colidx; |
| } |
| |
| @Override |
| public double[] values(int r) { |
| return _values; |
| } |
| |
| @Override |
| public int pos(int r) { |
| int idx = Arrays.binarySearch(_rowidx, 0, _nnzr, r); |
| |
| if (idx < 0) |
| idx = Math.max(-idx - 2, 0); |
| |
| return _rowptr[idx]; |
| } |
| |
| @Override |
| public boolean set(int r, int c, double v) { |
| int rowIndex = Arrays.binarySearch(_rowidx, 0, _nnzr, r); |
| boolean rowExists = rowIndex >= 0; |
| |
| if (!rowExists) { |
| if (v == 0) // Nothing to do |
| return false; |
| |
| int rowInsertionIndex = -rowIndex - 1; |
| int tmp = _rowptr[rowInsertionIndex]; |
| insertRow(rowInsertionIndex, r, tmp); |
| incrRowPtr(rowInsertionIndex+1); |
| insertCol(tmp, c, v); |
| return true; |
| } |
| |
| int pos = _rowptr[rowIndex]; |
| int len = _rowptr[rowIndex+1] - pos; |
| int index = Arrays.binarySearch(_colidx, pos, pos+len, c); |
| boolean colExists = index >= 0; |
| |
| if (v != 0) { |
| if (colExists) { |
| _values[index] = v; |
| return false; |
| } |
| |
| // Insert a new column into an existing row |
| insertCol(-index-1, c, v); |
| incrRowPtr(rowIndex+1); |
| |
| return true; |
| } |
| |
| if (!colExists) |
| return false; |
| |
| // If there is only one entry in the row, we have to remove the entire row |
| if (len == 1) { |
| deleteRow(rowIndex); |
| rowIndex--; |
| } |
| |
| // remove the column |
| incrRowPtr(rowIndex+1, -1); |
| deleteCol(index); |
| |
| return true; |
| } |
| |
| @Override |
| public boolean add(int r, int c, double v) { |
| // TODO: performance |
| double oldValue = get(r, c); |
| |
| if (v == 0) |
| return false; |
| |
| return set(r, c, oldValue + v); |
| } |
| |
| @Override |
| public void set(int r, SparseRow row, boolean deep) { |
| int newRowSize = row.size(); |
| |
| int rowIndex = Arrays.binarySearch(_rowidx, 0, _nnzr, r); |
| boolean rowExists = rowIndex >= 0; |
| |
| if (!rowExists) { |
| // Nothing to do |
| if (newRowSize == 0) |
| return; |
| |
| int rowInsertionIndex = -rowIndex - 1; |
| int tmp = _rowptr[rowInsertionIndex]; |
| insertRow(rowInsertionIndex, r, tmp); |
| incrRowPtr(rowInsertionIndex+1, newRowSize); |
| insertCols(tmp, row.indexes(), row.values(), 0, 0, newRowSize); |
| return; |
| } |
| |
| int pos = _rowptr[rowIndex]; |
| int oldRowSize = _rowptr[rowIndex+1] - pos; |
| |
| if (newRowSize == 0) { |
| // Delete row |
| deleteRow(rowIndex); |
| incrRowPtr(rowIndex, -oldRowSize); |
| deleteCols(pos, oldRowSize); |
| return; |
| } |
| |
| incrRowPtr(rowIndex+1, newRowSize-oldRowSize); |
| insertCols(pos, row.indexes(), row.values(), oldRowSize); |
| } |
| |
| @Override |
| public void append(int r, int c, double v) { |
| // TODO performance |
| set(r, c, v); |
| } |
| |
| @Override |
| public void setIndexRange(int r, int cl, int cu, double[] v, int vix, int vlen) { |
| int lnnz = UtilFunctions.computeNnz(v, vix, vlen); |
| |
| if (lnnz == 0) { |
| deleteIndexRange(r, cl, cu); |
| return; |
| } |
| |
| int rowIdx = Arrays.binarySearch(_rowidx, 0, _nnzr, r); |
| |
| if (rowIdx < 0) { |
| rowIdx = -rowIdx - 1; |
| insertRow(rowIdx, r, _rowptr[rowIdx]); |
| } |
| |
| int rowStart = _rowptr[rowIdx]; |
| int rowEnd = _rowptr[rowIdx+1]; |
| |
| int clIdx = Arrays.binarySearch(_colidx, rowStart, rowEnd, cl); |
| if (clIdx < 0) |
| clIdx = -clIdx - 1; |
| |
| int cuIdx = Arrays.binarySearch(_colidx, clIdx, rowEnd, cu); |
| if (cuIdx < 0) |
| cuIdx = -cuIdx - 1; |
| |
| int oldnnz = cuIdx - clIdx; |
| |
| allocateCols(clIdx, lnnz, oldnnz); |
| incrRowPtr(rowIdx+1, lnnz - oldnnz); |
| |
| int insertionIndex = clIdx; |
| |
| for (int i = vix; i < vix+vlen; i++) { |
| if (v[i] != 0) { |
| _colidx[insertionIndex] = cl + i - vix; |
| _values[insertionIndex] = v[i]; |
| insertionIndex++; |
| } |
| } |
| } |
| |
| @Override |
| public void setIndexRange(int r, int cl, int cu, double[] v, int[] vix, int vpos, int vlen) { |
| if (vlen == 0) { |
| deleteIndexRange(r, cl, cu); |
| return; |
| } |
| |
| int rowIdx = Arrays.binarySearch(_rowidx, 0, _nnzr, r); |
| |
| if (rowIdx < 0) { |
| rowIdx = -rowIdx - 1; |
| insertRow(rowIdx, r, _rowptr[rowIdx]); |
| } |
| |
| int rowStart = _rowptr[rowIdx]; |
| int rowEnd = _rowptr[rowIdx+1]; |
| |
| int clIdx = Arrays.binarySearch(_colidx, rowStart, rowEnd, cl); |
| if (clIdx < 0) |
| clIdx = -clIdx - 1; |
| |
| int cuIdx = Arrays.binarySearch(_colidx, clIdx, rowEnd, cu); |
| if (cuIdx < 0) |
| cuIdx = -cuIdx - 1; |
| |
| int oldnnz = cuIdx - clIdx; |
| |
| allocateCols(clIdx, vlen, oldnnz); |
| incrRowPtr(rowIdx+1, vlen - oldnnz); |
| |
| int insertionIndex = clIdx; |
| |
| for (int i = vpos; i < vpos+vlen; i++) { |
| if (v[i] != 0) { |
| _colidx[insertionIndex] = cl - vix[i]; |
| _values[insertionIndex] = v[i]; |
| insertionIndex++; |
| } |
| } |
| } |
| |
| @Override |
| public void deleteIndexRange(int r, int cl, int cu) { |
| int rowIdx = Arrays.binarySearch(_rowidx, 0, _nnzr, r); |
| if( rowIdx < 0 ) //nothing to delete |
| return; |
| |
| int nnz = _rowptr[rowIdx+1] - _rowptr[rowIdx]; |
| |
| int start = Arrays.binarySearch(_colidx, _rowptr[rowIdx], _rowptr[rowIdx+1], cl); |
| if (start < 0) |
| start = -start-1; |
| |
| int end = Arrays.binarySearch(_colidx, start, _rowptr[rowIdx+1], cu); |
| if( end < 0 ) //delete all remaining |
| end = -end-1; |
| |
| if (end-start <= 0) // Nothing to delete |
| return; |
| |
| if (nnz == end-start) { |
| deleteRow(rowIdx); |
| rowIdx--; |
| } |
| |
| //overlapping array copy (shift rhs values left) |
| System.arraycopy(_colidx, end, _colidx, start, _size-end); |
| System.arraycopy(_values, end, _values, start, _size-end); |
| _size -= (end-start); |
| |
| incrRowPtr(rowIdx+1, start-end); |
| } |
| |
| @Override |
| public void sort() { |
| for( int i=0; i < _rowidx.length; i++ ) |
| sortFromRowIndex(i); |
| } |
| |
| @Override |
| public void sort(int r) { |
| int rowIdx = Arrays.binarySearch(_rowidx, 0, _nnzr, r); |
| |
| if (rowIdx >= 0) |
| sortFromRowIndex(rowIdx); |
| } |
| |
| private void sortFromRowIndex(int rowIndex) { |
| int pos = _rowptr[rowIndex]; |
| int len = _rowptr[rowIndex+1] - pos; |
| if( !SortUtils.isSorted(pos, pos+len, _colidx) ) |
| SortUtils.sortByIndex(pos, pos+len, _colidx, _values); |
| } |
| |
| @Override |
| public double get(int r, int c) { |
| int rowIndex = Arrays.binarySearch(_rowidx, 0, _nnzr, r); |
| |
| if (rowIndex < 0) |
| return 0; |
| |
| int pos = _rowptr[rowIndex]; |
| int len = _rowptr[rowIndex+1] - pos; |
| |
| //search for existing col index in [pos,pos+len) |
| int index = Arrays.binarySearch(_colidx, pos, pos+len, c); |
| return (index >= 0) ? _values[index] : 0; |
| } |
| |
| @Override |
| public SparseRow get(int r) { |
| if( isEmpty(r) ) |
| return new SparseRowScalar(); |
| int pos = pos(r); |
| int len = size(r); |
| |
| SparseRowVector row = new SparseRowVector(len); |
| System.arraycopy(_colidx, pos, row.indexes(), 0, len); |
| System.arraycopy(_values, pos, row.values(), 0, len); |
| row.setSize(len); |
| return row; |
| } |
| |
| @Override |
| public Iterator<IJV> getIterator() { |
| // TODO: performance |
| return super.getIterator(); |
| } |
| |
| @Override |
| public int posFIndexLTE(int r, int c) { |
| int rowIdx = Arrays.binarySearch(_rowidx, 0, _nnzr, r); |
| |
| if (rowIdx < 0) |
| return -1; |
| |
| int colIdx = Arrays.binarySearch(_colidx, _rowptr[rowIdx], _rowptr[rowIdx+1], c); |
| |
| if (colIdx < 0) |
| colIdx = -colIdx - 2; |
| |
| // There is no element smaller or equal in this row |
| if (colIdx < _rowptr[rowIdx]) |
| return -1; |
| |
| return colIdx - _rowptr[rowIdx]; |
| } |
| |
| @Override |
| public final int posFIndexGTE(int r, int c) { |
| int rowIdx = Arrays.binarySearch(_rowidx, 0, _nnzr, r); |
| |
| if (rowIdx < 0) |
| return -1; |
| |
| int colIdx = Arrays.binarySearch(_colidx, _rowptr[rowIdx], _rowptr[rowIdx+1], c); |
| |
| if (colIdx < 0) |
| colIdx = -colIdx - 1; |
| |
| // There is no element greater or equal in this row |
| if (colIdx >= _rowptr[rowIdx+1]) |
| return -1; |
| |
| return colIdx - _rowptr[rowIdx]; |
| } |
| |
| @Override |
| public int posFIndexGT(int r, int c) { |
| int rowIdx = Arrays.binarySearch(_rowidx, 0, _nnzr, r); |
| |
| if (rowIdx < 0) |
| return -1; |
| |
| int colIdx = Arrays.binarySearch(_colidx, _rowptr[rowIdx], _rowptr[rowIdx+1], c); |
| |
| if (colIdx >= 0) |
| colIdx++; |
| else |
| colIdx = -colIdx - 1; |
| |
| // There is no element great in this row |
| if (colIdx >= _rowptr[rowIdx+1]) |
| return -1; |
| |
| return colIdx - _rowptr[rowIdx]; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append("SparseBlockCSR: rlen="); |
| sb.append(numRows()); |
| sb.append(", nnz="); |
| sb.append(size()); |
| sb.append("\n"); |
| final int rowDigits = (int)Math.max(Math.ceil(Math.log10(numRows())),1) ; |
| for(int rowIdx = 0; rowIdx < _rowidx.length; rowIdx++) { |
| // append row |
| final int row = _rowidx[rowIdx]; |
| final int pos = _rowptr[rowIdx]; |
| final int len = _rowptr[rowIdx+1] - pos; |
| |
| sb.append(String.format("%0"+rowDigits+"d ", row)); |
| for(int j = pos; j < pos + len; j++) { |
| if(_values[j] == (long) _values[j]) |
| sb.append(String.format("%"+rowDigits+"d:%d", _colidx[j], (long)_values[j])); |
| else |
| sb.append(String.format("%"+rowDigits+"d:%s", _colidx[j], Double.toString(_values[j]))); |
| if(j + 1 < pos + len) |
| sb.append(" "); |
| } |
| sb.append("\n"); |
| } |
| |
| return sb.toString(); |
| } |
| |
| @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 && _rowptr.length != _rowidx.length + 1 && _values.length < nnz && _colidx.length < nnz ) { |
| throw new RuntimeException("Incorrect array lengths."); |
| } |
| |
| //3. non-decreasing row pointers |
| for ( int i=1; i <_rowidx.length; i++ ) { |
| if (_rowidx[i-1] > _rowidx[i]) |
| throw new RuntimeException("Row indices are decreasing at row: " + i |
| + ", with indices " + _rowidx[i-1] + " > " +_rowidx[i]); |
| } |
| |
| for (int i = 1; i < _rowptr.length; i++ ) { |
| if (_rowptr[i - 1] > _rowptr[i]) { |
| throw new RuntimeException("Row pointers are decreasing at row: " + i |
| + ", with pointers " + _rowptr[i-1] + " > " +_rowptr[i]); |
| } |
| } |
| |
| //4. sorted column indexes per row |
| for ( int rowIdx = 0; rowIdx < _rowidx.length; rowIdx++ ) { |
| int apos = _rowidx[rowIdx]; |
| int alen = _rowidx[rowIdx+1] - apos; |
| |
| for( int k = apos + 1; k < apos + alen; k++) |
| if( _colidx[k-1] >= _colidx[k] ) |
| throw new RuntimeException("Wrong sparse row ordering: " |
| + k + " " + _colidx[k-1] + " " + _colidx[k]); |
| |
| for( int k=apos; k<apos+alen; k++ ) |
| if( _values[k] == 0 ) |
| throw new RuntimeException("Wrong sparse row: zero at " |
| + k + " at col index " + _colidx[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 //specialized for CSR |
| public boolean contains(double pattern, int rl, int ru) { |
| boolean NaNpattern = Double.isNaN(pattern); |
| double[] vals = _values; |
| int prl = pos(rl), pru = pos(ru); |
| for(int i=prl; i<pru; i++) |
| if(vals[i]==pattern || (NaNpattern && Double.isNaN(vals[i]))) |
| return true; |
| return false; |
| } |
| |
| @Override |
| public Iterator<Integer> getNonEmptyRowsIterator(int rl, int ru) { |
| return new NonEmptyRowsIteratorDCSR(rl, ru); |
| } |
| |
| public class NonEmptyRowsIteratorDCSR implements Iterator<Integer> { |
| private int _rpos; |
| private final int _ru; |
| |
| public NonEmptyRowsIteratorDCSR(int rl, int ru) { |
| _rpos = (rl==0) ? 0 : posRowIndex(rl); |
| _ru = ru; |
| } |
| |
| @Override |
| public boolean hasNext() { |
| return _rpos < _nnzr && _rowidx[_rpos] < _ru; |
| } |
| |
| @Override |
| public Integer next() { |
| return _rowidx[_rpos++]; |
| } |
| } |
| |
| /////////////////////////// |
| // private helper methods |
| |
| 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 deleteRow(int rowIdx) { |
| System.arraycopy(_rowidx, rowIdx + 1, _rowidx, rowIdx, _nnzr-rowIdx-1); |
| System.arraycopy(_rowptr, rowIdx + 1, _rowptr, rowIdx, _nnzr-rowIdx); |
| _nnzr--; |
| } |
| |
| private void insertRow(int ix, int row, int rowPtr) { |
| if (_nnzr >= _rowidx.length) { |
| resizeAndInsertRow(ix, row, rowPtr); |
| return; |
| } |
| |
| System.arraycopy(_rowidx, ix, _rowidx, ix+1, _nnzr-ix); |
| System.arraycopy(_rowptr, ix, _rowptr, ix+1, _nnzr-ix+1); |
| _rowidx[ix] = row; |
| _rowptr[ix] = rowPtr; |
| _nnzr++; |
| } |
| |
| private void resizeAndInsertRow(int ix, int row, int rowPtr) { |
| //compute new size |
| int newCap = newCapacity(_rowidx.length+1); |
| |
| int[] oldrowidx = _rowidx; |
| int[] oldrowptr = _rowptr; |
| _rowidx = new int[newCap]; |
| _rowptr = new int[newCap+1]; |
| |
| //copy lhs values to new array |
| System.arraycopy(oldrowidx, 0, _rowidx, 0, ix); |
| System.arraycopy(oldrowptr, 0, _rowptr, 0, ix); |
| |
| //copy rhs values to new array |
| System.arraycopy(oldrowidx, ix, _rowidx, ix+1, _nnzr-ix); |
| System.arraycopy(oldrowptr, ix, _rowptr, ix+1, _nnzr-ix+1); |
| |
| _rowidx[ix] = row; |
| _rowptr[ix] = rowPtr; |
| |
| _nnzr++; |
| } |
| |
| private void deleteCol(int ix) { |
| // Without removing row |
| //overlapping array copy (shift rhs values left by 1) |
| System.arraycopy(_colidx, ix+1, _colidx, ix, _size-ix-1); |
| System.arraycopy(_values, ix+1, _values, ix, _size-ix-1); |
| _size--; |
| } |
| |
| private void insertCol(int ix, int c, double v) { |
| // Without inserting row |
| if (_size >= _colidx.length) { |
| resizeAndInsertCol(ix, c, v); |
| return; |
| } |
| |
| System.arraycopy(_colidx, ix, _colidx, ix+1, _size-ix); |
| System.arraycopy(_values, ix, _values, ix+1, _size-ix); |
| |
| _colidx[ix] = c; |
| _values[ix] = v; |
| _size++; |
| } |
| |
| private void deleteCols(int ix, int len) { |
| insertCols(ix, new int[0], new double[0], len, 0, 0); |
| } |
| |
| private void insertCols(int ix, int[] cols, double[] vals, int overwriteNum) { |
| insertCols(ix, cols, vals, overwriteNum, 0, vals.length); |
| } |
| |
| private void insertCols(int ix, int[] cols, double[] vals, int overwriteNum, int vix, int vlen) { |
| // Without inserting row |
| if (_size + vlen - overwriteNum > _colidx.length) { |
| resizeAndInsertCols(ix, cols, vals, overwriteNum, vix, vlen); |
| return; |
| } |
| |
| allocateCols(ix, vlen, overwriteNum); |
| |
| System.arraycopy(cols, vix, _colidx, ix, vlen); |
| System.arraycopy(vals, vix, _values, ix, vlen); |
| } |
| |
| private void resizeAndInsertCols(int ix, int[] cols, double[] vals, int overwriteNum, int vix, int vlen) { |
| resizeAndAllocateCols(ix, vlen, overwriteNum); |
| |
| //copy new vals into row |
| System.arraycopy(cols, vix, _colidx, ix, vlen); |
| System.arraycopy(vals, vix, _values, ix, vlen); |
| } |
| |
| @SuppressWarnings("unused") |
| private void allocateCols(int ix, int numCols) { |
| allocateCols(ix, numCols, 0); |
| } |
| |
| private void allocateCols(int ix, int numCols, int overwriteNum) { |
| if (numCols == 0) |
| return; |
| |
| if (_size + numCols - overwriteNum > _colidx.length) { |
| resizeAndAllocateCols(ix, numCols, overwriteNum); |
| return; |
| } |
| |
| System.arraycopy(_colidx, ix+overwriteNum, _colidx, ix+numCols, _size-ix-overwriteNum); |
| System.arraycopy(_values, ix+overwriteNum, _values, ix+numCols, _size-ix-overwriteNum); |
| _size += numCols - overwriteNum; |
| } |
| |
| private void resizeAndAllocateCols(int ix, int numCols, int overwriteNum) { |
| //compute new size |
| int newCap = newCapacity(_size + numCols - overwriteNum); |
| |
| int[] oldcolidx = _colidx; |
| double[] oldvalues = _values; |
| _colidx = new int[newCap]; |
| _values = new double[newCap]; |
| |
| //copy lhs values to new array |
| System.arraycopy(oldcolidx, 0, _colidx, 0, ix); |
| System.arraycopy(oldvalues, 0, _values, 0, ix); |
| |
| //copy rhs values to new array |
| System.arraycopy(oldcolidx, ix + overwriteNum, _colidx, ix+numCols, _size-ix-overwriteNum); |
| System.arraycopy(oldvalues, ix + overwriteNum, _values, ix+numCols, _size-ix-overwriteNum); |
| |
| _size += numCols - overwriteNum; |
| } |
| |
| private void resizeAndInsertCol(int ix, int c, double v) { |
| //compute new size |
| int newCap = newCapacity(_values.length+1); |
| |
| int[] oldcolidx = _colidx; |
| double[] oldvalues = _values; |
| _colidx = new int[newCap]; |
| _values = new double[newCap]; |
| |
| //copy lhs values to new array |
| System.arraycopy(oldcolidx, 0, _colidx, 0, ix); |
| System.arraycopy(oldvalues, 0, _values, 0, ix); |
| |
| //copy rhs values to new array |
| System.arraycopy(oldcolidx, ix, _colidx, ix+1, _size-ix); |
| System.arraycopy(oldvalues, ix, _values, ix+1, _size-ix); |
| |
| //insert new value |
| _colidx[ix] = c; |
| _values[ix] = v; |
| |
| _size++; |
| } |
| |
| private void incrRowPtr(int rowIndex) { |
| incrRowPtr(rowIndex, 1); |
| } |
| |
| private void incrRowPtr(int rowIndex, int cnt) { |
| for( int i = rowIndex; i < _nnzr + 1; i++ ) |
| _rowptr[i] += cnt; |
| } |
| |
| private int posRowIndex(int r) { |
| int rowIndex = Arrays.binarySearch(_rowidx, 0, _nnzr, r); |
| if( rowIndex < 0 ) |
| rowIndex = -rowIndex - 1; |
| return rowIndex; |
| } |
| } |