| /* |
| * 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 java.io.DataInput; |
| import java.io.IOException; |
| import java.util.Arrays; |
| |
| import org.apache.sysds.runtime.util.SortUtils; |
| import org.apache.sysds.runtime.util.UtilFunctions; |
| |
| /** |
| * SparseBlock implementation that realizes a traditional 'compressed sparse row' |
| * representation, where the entire sparse block is stored as three arrays: ptr |
| * of length rlen+1 to store offsets per row, and indexes/values of length nnz |
| * to store column 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. Finally, the total |
| * nnz is limited to INTEGER_MAX, whereas for SparseBlockMCSR only the nnz per |
| * row are limited to INTEGER_MAX. |
| * |
| * TODO: extensions for faster incremental construction (e.g., max row) |
| * TODO more efficient fused setIndexRange impl to avoid repeated copies and updates |
| * |
| */ |
| public class SparseBlockCSR extends SparseBlock |
| { |
| private static final long serialVersionUID = 1922673868466164244L; |
| |
| private int[] _ptr = null; //row pointer array (size: rlen+1) |
| private int[] _indexes = null; //column index array (size: >=nnz) |
| private double[] _values = null; //value array (size: >=nnz) |
| private int _size = 0; //actual number of nnz |
| |
| public SparseBlockCSR(int rlen) { |
| this(rlen, INIT_CAPACITY); |
| } |
| |
| public SparseBlockCSR(int rlen, int capacity) { |
| _ptr = new int[rlen+1]; //ix0=0 |
| _indexes = new int[capacity]; |
| _values = new double[capacity]; |
| _size = 0; |
| } |
| |
| public SparseBlockCSR(int[] rowPtr, int[] colInd, double[] values, int nnz){ |
| _ptr = rowPtr; |
| _indexes = colInd; |
| _values = values; |
| _size = nnz; |
| } |
| |
| /** |
| * Copy constructor sparse block abstraction. |
| * |
| * @param sblock sparse block to copy |
| */ |
| public SparseBlockCSR(SparseBlock sblock) |
| { |
| long size = sblock.size(); |
| if( size > Integer.MAX_VALUE ) |
| throw new RuntimeException("SparseBlockCSR supports nnz<=Integer.MAX_VALUE but got "+size); |
| |
| //special case SparseBlockCSR |
| if( sblock instanceof SparseBlockCSR ) { |
| SparseBlockCSR ocsr = (SparseBlockCSR)sblock; |
| _ptr = Arrays.copyOf(ocsr._ptr, ocsr.numRows()+1); |
| _indexes = Arrays.copyOf(ocsr._indexes, ocsr._size); |
| _values = Arrays.copyOf(ocsr._values, ocsr._size); |
| _size = ocsr._size; |
| } |
| //general case SparseBlock |
| else { |
| int rlen = sblock.numRows(); |
| |
| _ptr = new int[rlen+1]; |
| _indexes = new int[(int)size]; |
| _values = new double[(int)size]; |
| _size = (int)size; |
| |
| for( int i=0, pos=0; i<rlen; i++ ) { |
| if( !sblock.isEmpty(i) ) { |
| int apos = sblock.pos(i); |
| int alen = sblock.size(i); |
| int[] aix = sblock.indexes(i); |
| double[] avals = sblock.values(i); |
| System.arraycopy(aix, apos, _indexes, pos, alen); |
| System.arraycopy(avals, apos, _values, pos, alen); |
| pos += alen; |
| } |
| _ptr[i+1]=pos; |
| } |
| } |
| } |
| |
| /** |
| * Copy constructor old sparse row representation. |
| * @param rows array of sparse rows |
| * @param nnz number of non-zeroes |
| */ |
| public SparseBlockCSR(SparseRow[] rows, int nnz) |
| { |
| int rlen = rows.length; |
| |
| _ptr = new int[rlen+1]; //ix0=0 |
| _indexes = new int[nnz]; |
| _values = new double[nnz]; |
| _size = nnz; |
| |
| for( int i=0, pos=0; i<rlen; i++ ) { |
| if( rows[i]!=null && !rows[i].isEmpty() ) { |
| int alen = rows[i].size(); |
| int[] aix = rows[i].indexes(); |
| double[] avals = rows[i].values(); |
| System.arraycopy(aix, 0, _indexes, pos, alen); |
| System.arraycopy(avals, 0, _values, pos, alen); |
| pos += alen; |
| } |
| _ptr[i+1]=pos; |
| } |
| } |
| |
| /** |
| * Copy constructor for COO representation |
| * |
| * @param rows number of rows |
| * @param rowInd row indices |
| * @param colInd column indices |
| * @param values non zero values |
| */ |
| public SparseBlockCSR(int rows, int[] rowInd, int[] colInd, double[] values) { |
| int nnz = values.length; |
| _ptr = new int[rows+1]; |
| _indexes = Arrays.copyOf(colInd, colInd.length); |
| _values = Arrays.copyOf(values, values.length); |
| _size = nnz; |
| |
| //single-pass construction of row pointers |
| int rlast = 0; |
| for(int i=0; i<nnz; i++) { |
| int r = rowInd[i]; |
| if( rlast < r ) |
| Arrays.fill(_ptr, rlast+1, r+1, i); |
| rlast = r; |
| } |
| Arrays.fill(_ptr, rlast+1, numRows()+1, nnz); |
| } |
| |
| /** |
| * Copy constructor for given array of column indexes, which |
| * identifies rows by position and implies values of 1. |
| * |
| * @param rows number of rows |
| * @param nnz number of non-zeros |
| * @param colInd column indexes |
| */ |
| public SparseBlockCSR(int rows, int nnz, int[] colInd) { |
| _ptr = new int[rows+1]; |
| _indexes = (rows==nnz) ? colInd : new int[nnz]; |
| _values = new double[nnz]; |
| Arrays.fill(_values, 1); |
| _size = nnz; |
| |
| //single-pass construction of row pointers |
| //and copy of column indexes if necessary |
| for(int i=0, pos=0; i<rows; i++) { |
| if( colInd[i] >= 0 ) { |
| if( rows > nnz ) |
| _indexes[pos] = colInd[i]; |
| pos++; |
| } |
| _ptr[i+1] = pos; |
| } |
| } |
| |
| /** |
| * Initializes the CSR 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 ij |
| * @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 rlast = 0; |
| for(int i=0; i<nnz; i++) { |
| int r = in.readInt(); |
| if( rlast < r ) |
| Arrays.fill(_ptr, rlast+1, r+1, i); |
| rlast = r; |
| _indexes[i] = in.readInt(); |
| _values[i] = in.readDouble(); |
| } |
| Arrays.fill(_ptr, rlast+1, numRows()+1, nnz); |
| |
| //update meta data |
| _size = nnz; |
| } |
| |
| /** |
| * Initializes the CSR sparse block from an ordered input |
| * stream of sparse rows (rownnz, jv-pairs*). |
| * |
| * @param rlen number of rows |
| * @param nnz number of non-zeros to read |
| * @param in data input stream of sparse rows, ordered by i |
| * @throws IOException if deserialization error occurs |
| */ |
| public void initSparse(int rlen, int nnz, DataInput in) |
| throws IOException |
| { |
| //allocate space if necessary |
| if( _values.length < nnz ) |
| resize(newCapacity(nnz)); |
| |
| //read sparse rows, append and update pointers |
| _ptr[0] = 0; |
| for( int r=0, pos=0; r<rlen; r++ ) { |
| int lnnz = in.readInt(); |
| for( int j=0; j<lnnz; j++, pos++ ) { |
| _indexes[pos] = in.readInt(); |
| _values[pos] = in.readDouble(); |
| } |
| _ptr[r+1] = pos; |
| } |
| |
| //update meta data |
| _size = nnz; |
| } |
| |
| /** |
| * 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 estimateMemory(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; //object + int field |
| size += 24 + (nrows+1) * 4d; //ptr array (row pointers) |
| size += 24 + lnnz * 4d; //indexes array (column indexes) |
| size += 24 + lnnz * 8d; //values array (non-zero values) |
| |
| //robustness for long overflows |
| return (long) Math.min(size, Long.MAX_VALUE); |
| } |
| |
| |
| /** |
| * Get raw access to underlying array of row pointers |
| * For use in GPU code |
| * @return array of row pointers |
| */ |
| public int[] rowPointers() { |
| return _ptr; |
| } |
| |
| /** |
| * Get raw access to underlying array of column indices |
| * For use in GPU code |
| * @return array of column 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() { |
| 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) { |
| int pos = pos(r); |
| int len = size(r); |
| |
| 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(r+1, len); |
| } |
| } |
| |
| @Override |
| public long size() { |
| return _size; |
| } |
| |
| @Override |
| public int size(int r) { |
| return _ptr[r+1] - _ptr[r]; |
| } |
| |
| @Override |
| public long size(int rl, int ru) { |
| return _ptr[ru] - _ptr[rl]; |
| } |
| |
| @Override |
| public long size(int rl, int ru, int cl, int cu) { |
| long nnz = 0; |
| for(int i=rl; i<ru; i++) |
| if( !isEmpty(i) ) { |
| int start = internPosFIndexGTE(i, cl); |
| int end = internPosFIndexGTE(i, cu); |
| nnz += (start!=-1) ? (end-start) : 0; |
| } |
| return nnz; |
| } |
| |
| @Override |
| public boolean isEmpty(int r) { |
| return (_ptr[r+1] - _ptr[r] == 0); |
| } |
| |
| @Override |
| public int[] indexes(int r) { |
| return _indexes; |
| } |
| |
| @Override |
| public double[] values(int r) { |
| return _values; |
| } |
| |
| @Override |
| public int pos(int r) { |
| return _ptr[r]; |
| } |
| |
| @Override |
| public boolean set(int r, int c, double v) { |
| int pos = pos(r); |
| int len = size(r); |
| |
| //search for existing col index |
| int index = Arrays.binarySearch(_indexes, pos, pos+len, c); |
| 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(r+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, c, v); |
| else |
| shiftRightAndInsert(index, c, v); |
| incrPtr(r+1); |
| return true; // nnz++ |
| } |
| |
| @Override |
| public boolean add(int r, int c, double v) { |
| //early abort on zero |
| if( v==0 ) return false; |
| |
| int pos = pos(r); |
| int len = size(r); |
| |
| //search for existing col index |
| int index = Arrays.binarySearch(_indexes, pos, pos+len, c); |
| 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, c, v); |
| else |
| shiftRightAndInsert(index, c, v); |
| incrPtr(r+1); |
| return true; // nnz++ |
| } |
| |
| @Override |
| public void set(int r, SparseRow row, boolean deep) { |
| int pos = pos(r); |
| int len = size(r); |
| int alen = row.size(); |
| int[] aix = row.indexes(); |
| double[] avals = row.values(); |
| |
| //delete existing values if necessary |
| if( len > 0 ) //incl size update |
| deleteIndexRange(r, 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(r+1, alen); |
| |
| //copy input row into internal representation |
| System.arraycopy(aix, 0, _indexes, pos, alen); |
| System.arraycopy(avals, 0, _values, pos, alen); |
| } |
| |
| @Override |
| public void append(int r, int c, double v) { |
| //early abort on zero |
| if( v==0 ) return; |
| |
| int pos = pos(r); |
| int len = size(r); |
| if( pos+len == _size ) { |
| //resize and append |
| if( _size==_values.length ) |
| resize(); |
| insert(_size, c, v); |
| } |
| else { |
| //resize, shift and insert |
| if( _size==_values.length ) |
| resizeAndInsert(pos+len, c, v); |
| else |
| shiftRightAndInsert(pos+len, c, v); |
| } |
| incrPtr(r+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); |
| int index = internPosFIndexGT(r, cl); |
| int index2 = (index>0)?index:pos(r+1); |
| shiftRightByN(index2, lnnz); |
| |
| //insert values |
| for( int i=vix; i<vix+vlen; i++ ) |
| if( v[i] != 0 ) { |
| _indexes[ index2 ] = cl+i-vix; |
| _values[ index2 ] = v[i]; |
| index2++; |
| } |
| incrPtr(r+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 index = internPosFIndexGT(r, cl); |
| int index2 = (index>0)?index:pos(r+1); |
| shiftRightByN(index2, vlen); |
| |
| //insert values |
| for( int i=vpos; i<vpos+vlen; i++ ) { |
| _indexes[ index2 ] = cl+vix[i]; |
| _values[ index2 ] = v[i]; |
| index2++; |
| } |
| incrPtr(r+1, vlen); |
| } |
| |
| /** |
| * Inserts a sorted row-major array of non-zero values into the row and column |
| * range [rl,ru) and [cl,cu). Note: that this is a CSR-specific method to address |
| * performance issues due to repeated re-shifting on update-in-place. |
| * |
| * @param rl lower row index, starting at 0, inclusive |
| * @param ru upper row index, starting at 0, exclusive |
| * @param cl lower column index, starting at 0, inclusive |
| * @param cu upper column index, starting at 0, exclusive |
| * @param v right-hand-side dense block |
| * @param vix right-hand-side dense block index |
| * @param vlen right-hand-side dense block value length |
| */ |
| public void setIndexRange(int rl, int ru, int cl, int cu, double[] v, int vix, int vlen) { |
| //step 1: determine output nnz |
| int nnz = _size - (int)size(rl, ru, cl, cu); |
| if( v != null ) |
| nnz += UtilFunctions.computeNnz(v, vix, vlen); |
| |
| //step 2: reallocate if necessary |
| if( _values.length < nnz ) |
| resize(nnz); |
| |
| //step 3: insert and overwrite index range |
| //total shift can be negative or positive and w/ internal skew |
| |
| //step 3a: forward pass: compact (delete index range) |
| int pos = pos(rl); |
| for( int r=rl; r<ru; r++ ) { |
| int rpos = pos(r); |
| int rlen = size(r); |
| _ptr[r] = pos; |
| for( int k=rpos; k<rpos+rlen; k++ ) |
| if( _indexes[k]<cl || cu<=_indexes[k] ) { |
| _indexes[pos] = _indexes[k]; |
| _values[pos++] = _values[k]; |
| } |
| } |
| shiftLeftByN(pos(ru), pos(ru)-pos); |
| decrPtr(ru, pos(ru)-pos); |
| |
| //step 3b: backward pass: merge (insert index range) |
| int tshift1 = nnz - _size; //always non-negative |
| if( v == null || tshift1==0 ) //early abort |
| return; |
| shiftRightByN(pos(ru), tshift1); |
| incrPtr(ru, tshift1); |
| pos = pos(ru)-1; |
| int clen2 = cu-cl; |
| for( int r=ru-1; r>=rl; r-- ) { |
| int rpos = pos(r); |
| int rlen = size(r) - tshift1; |
| //copy lhs right |
| int k = -1; |
| for( k=rpos+rlen-1; k>=rpos && _indexes[k]>=cu; k-- ) { |
| _indexes[pos] = _indexes[k]; |
| _values[pos--] = _values[k]; |
| } |
| //copy rhs |
| int voff = vix + (r-rl) * clen2; |
| for( int k2=clen2-1; k2>=0 & vlen>voff; k2-- ) |
| if( v[voff+k2] != 0 ) { |
| _indexes[pos] = cl + k2; |
| _values[pos--] = v[voff+k2]; |
| tshift1--; |
| } |
| //copy lhs left |
| for( ; k>=rpos; k-- ) { |
| _indexes[pos] = _indexes[k]; |
| _values[pos--] = _values[k]; |
| } |
| _ptr[r] = pos+1; |
| } |
| } |
| |
| /** |
| * Inserts a sparse block into the row and column range [rl,ru) and [cl,cu). |
| * Note: that this is a CSR-specific method to address performance issues |
| * due to repeated re-shifting on update-in-place. |
| * |
| * @param rl lower row index, starting at 0, inclusive |
| * @param ru upper row index, starting at 0, exclusive |
| * @param cl lower column index, starting at 0, inclusive |
| * @param cu upper column index, starting at 0, exclusive |
| * @param sb right-hand-side sparse block |
| */ |
| public void setIndexRange(int rl, int ru, int cl, int cu, SparseBlock sb) { |
| //step 1: determine output nnz |
| int nnz = (int) (_size - size(rl, ru, cl, cu) |
| + ((sb!=null) ? sb.size() : 0)); |
| |
| //step 2: reallocate if necessary |
| if( _values.length < nnz ) |
| resize(nnz); |
| |
| //step 3: insert and overwrite index range (backwards) |
| //total shift can be negative or positive and w/ internal skew |
| |
| //step 3a: forward pass: compact (delete index range) |
| int pos = pos(rl); |
| for( int r=rl; r<ru; r++ ) { |
| int rpos = pos(r); |
| int rlen = size(r); |
| _ptr[r] = pos; |
| for( int k=rpos; k<rpos+rlen; k++ ) |
| if( _indexes[k]<cl || cu<=_indexes[k] ) { |
| _indexes[pos] = _indexes[k]; |
| _values[pos++] = _values[k]; |
| } |
| } |
| shiftLeftByN(pos(ru), pos(ru)-pos); |
| decrPtr(ru, pos(ru)-pos); |
| |
| //step 3b: backward pass: merge (insert index range) |
| int tshift1 = nnz - _size; //always non-negative |
| if( sb == null || tshift1==0 ) //early abort |
| return; |
| shiftRightByN(pos(ru), tshift1); |
| incrPtr(ru, tshift1); |
| pos = pos(ru)-1; |
| for( int r=ru-1; r>=rl; r-- ) { |
| int rpos = pos(r); |
| int rlen = size(r) - tshift1; |
| //copy lhs right |
| int k = -1; |
| for( k=rpos+rlen-1; k>=rpos && _indexes[k]>=cu; k-- ) { |
| _indexes[pos] = _indexes[k]; |
| _values[pos--] = _values[k]; |
| } |
| //copy rhs |
| int r2 = r-rl; |
| int r2pos = sb.pos(r2); |
| for( int k2=r2pos+sb.size(r2)-1; k2>=r2pos; k2-- ) { |
| _indexes[pos] = cl + sb.indexes(r2)[k2]; |
| _values[pos--] = sb.values(r2)[k2]; |
| tshift1--; |
| } |
| //copy lhs left |
| for( ; k>=rpos; k-- ) { |
| _indexes[pos] = _indexes[k]; |
| _values[pos--] = _values[k]; |
| } |
| _ptr[r] = pos+1; |
| } |
| } |
| |
| @Override |
| public void deleteIndexRange(int r, int cl, int cu) { |
| int start = internPosFIndexGTE(r,cl); |
| if( start < 0 ) //nothing to delete |
| return; |
| |
| int len = size(r); |
| int end = internPosFIndexGTE(r, cu); |
| 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(r+1, end-start); |
| } |
| |
| @Override |
| public void sort() { |
| int rlen = numRows(); |
| for( int i=0; i<rlen && pos(i)<_size; i++ ) |
| sort(i); |
| } |
| |
| @Override |
| public void sort(int r) { |
| int pos = pos(r); |
| int len = size(r); |
| |
| 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( isEmpty(r) ) |
| return 0; |
| int pos = pos(r); |
| int len = size(r); |
| |
| //search for existing col index in [pos,pos+len) |
| int index = Arrays.binarySearch(_indexes, 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(_indexes, pos, row.indexes(), 0, len); |
| System.arraycopy(_values, pos, row.values(), 0, len); |
| row.setSize(len); |
| return row; |
| } |
| |
| @Override |
| public int posFIndexLTE(int r, int c) { |
| int index = internPosFIndexLTE(r, c); |
| return (index>=0) ? index-pos(r) : index; |
| } |
| |
| 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, 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; |
| } |
| |
| @Override |
| public int posFIndexGTE(int r, int c) { |
| int index = internPosFIndexGTE(r, c); |
| return (index>=0) ? index-pos(r) : index; |
| } |
| |
| 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, 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; |
| } |
| |
| @Override |
| public int posFIndexGT(int r, int c) { |
| int index = internPosFIndexGT(r, c); |
| return (index>=0) ? index-pos(r) : index; |
| } |
| |
| 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, 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; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append("SparseBlockCSR: rlen="); |
| sb.append(numRows()); |
| sb.append(", nnz="); |
| sb.append(size()); |
| sb.append("\n"); |
| for( int i=0; i<numRows(); i++ ) { |
| sb.append("row +"); |
| sb.append(i); |
| sb.append(": "); |
| //append row |
| int pos = pos(i); |
| int len = size(i); |
| for(int j=pos; j<pos+len; j++) { |
| sb.append(_indexes[j]); |
| sb.append(": "); |
| sb.append(_values[j]); |
| sb.append("\t"); |
| } |
| 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 && _ptr.length < rlen+1 && _values.length < nnz && _indexes.length < nnz ) { |
| throw new RuntimeException("Incorrect array lengths."); |
| } |
| |
| //3. non-decreasing row pointers |
| for( int i=1; i<rlen; i++ ) { |
| if(_ptr[i-1] > _ptr[i] && strict) |
| throw new RuntimeException("Row pointers are decreasing at row: "+i |
| + ", with pointers "+_ptr[i-1]+" > "+_ptr[i]); |
| } |
| |
| //4. sorted column indexes per row |
| for( int i=0; i<rlen; i++ ) { |
| int apos = pos(i); |
| int alen = size(i); |
| for( int k=apos+1; k<apos+alen; k++) |
| if( _indexes[k-1] >= _indexes[k] ) |
| throw new RuntimeException("Wrong sparse row ordering: " |
| + k + " "+_indexes[k-1]+" "+_indexes[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 " + _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; |
| } |
| |
| /////////////////////////// |
| // 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 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 c, 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, c, v); |
| } |
| |
| private void shiftRightAndInsert(int ix, int c, 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, c, 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; |
| } |
| |
| 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 c, double v) { |
| _indexes[ix] = c; |
| _values[ix] = v; |
| _size++; |
| } |
| |
| private void incrPtr(int rl) { |
| incrPtr(rl, 1); |
| } |
| |
| private void incrPtr(int rl, int cnt) { |
| int rlen = numRows(); |
| for( int i=rl; i<rlen+1; i++ ) |
| _ptr[i]+=cnt; |
| } |
| |
| private void decrPtr(int rl) { |
| decrPtr(rl, 1); |
| } |
| |
| private void decrPtr(int rl, int cnt) { |
| int rlen = numRows(); |
| for( int i=rl; i<rlen+1; i++ ) |
| _ptr[i]-=cnt; |
| } |
| } |