| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.apache.sysds.runtime.compress.colgroup; |
| |
| import java.io.DataInput; |
| import java.io.DataOutput; |
| import java.io.IOException; |
| import java.util.Arrays; |
| import java.util.List; |
| |
| import org.apache.commons.lang.NotImplementedException; |
| import org.apache.sysds.runtime.DMLRuntimeException; |
| import org.apache.sysds.runtime.compress.CompressedMatrixBlock; |
| import org.apache.sysds.runtime.compress.CompressedMatrixBlockFactory; |
| import org.apache.sysds.runtime.compress.CompressionSettings; |
| import org.apache.sysds.runtime.compress.CompressionSettingsBuilder; |
| import org.apache.sysds.runtime.compress.DMLCompressionException; |
| import org.apache.sysds.runtime.compress.colgroup.dictionary.ADictionary; |
| import org.apache.sysds.runtime.compress.colgroup.dictionary.DictLibMatrixMult; |
| import org.apache.sysds.runtime.compress.colgroup.dictionary.MatrixBlockDictionary; |
| import org.apache.sysds.runtime.compress.colgroup.indexes.ColIndexFactory; |
| import org.apache.sysds.runtime.compress.colgroup.indexes.IColIndex; |
| import org.apache.sysds.runtime.compress.colgroup.scheme.ICLAScheme; |
| import org.apache.sysds.runtime.compress.cost.ComputationCostEstimator; |
| import org.apache.sysds.runtime.compress.estim.CompressedSizeInfoColGroup; |
| import org.apache.sysds.runtime.compress.estim.EstimationFactors; |
| import org.apache.sysds.runtime.compress.estim.encoding.EncodingFactory; |
| import org.apache.sysds.runtime.compress.estim.encoding.IEncode; |
| import org.apache.sysds.runtime.compress.utils.Util; |
| import org.apache.sysds.runtime.controlprogram.parfor.stat.InfrastructureAnalyzer; |
| import org.apache.sysds.runtime.data.DenseBlock; |
| import org.apache.sysds.runtime.data.SparseBlock; |
| import org.apache.sysds.runtime.functionobjects.Builtin; |
| import org.apache.sysds.runtime.functionobjects.Builtin.BuiltinCode; |
| import org.apache.sysds.runtime.functionobjects.CM; |
| import org.apache.sysds.runtime.functionobjects.Multiply; |
| import org.apache.sysds.runtime.functionobjects.ReduceAll; |
| import org.apache.sysds.runtime.functionobjects.ReduceRow; |
| import org.apache.sysds.runtime.functionobjects.ValueFunction; |
| import org.apache.sysds.runtime.instructions.cp.CM_COV_Object; |
| import org.apache.sysds.runtime.matrix.data.LibMatrixMult; |
| import org.apache.sysds.runtime.matrix.data.LibMatrixReorg; |
| import org.apache.sysds.runtime.matrix.data.MatrixBlock; |
| import org.apache.sysds.runtime.matrix.data.MatrixIndexes; |
| import org.apache.sysds.runtime.matrix.operators.AggregateUnaryOperator; |
| import org.apache.sysds.runtime.matrix.operators.BinaryOperator; |
| import org.apache.sysds.runtime.matrix.operators.CMOperator; |
| import org.apache.sysds.runtime.matrix.operators.ScalarOperator; |
| import org.apache.sysds.runtime.matrix.operators.UnaryOperator; |
| |
| /** |
| * Column group type for columns that are stored as dense arrays of doubles. Uses a MatrixBlock internally to store the |
| * column contents. |
| */ |
| public class ColGroupUncompressed extends AColGroup { |
| private static final long serialVersionUID = -8254271148043292199L; |
| /** |
| * We store the contents of the columns as a MatrixBlock to take advantage of high-performance routines available for |
| * this data structure. |
| */ |
| private final MatrixBlock _data; |
| |
| private ColGroupUncompressed(MatrixBlock mb, IColIndex colIndexes) { |
| super(colIndexes); |
| _data = mb; |
| } |
| |
| protected static AColGroup create(MatrixBlock mb, IColIndex colIndexes) { |
| if(mb == null || mb.isEmpty()) |
| return new ColGroupEmpty(colIndexes); |
| else |
| return new ColGroupUncompressed(mb, colIndexes); |
| } |
| |
| /** |
| * Main constructor for Uncompressed ColGroup. |
| * |
| * @param colIndexes Indices (relative to the current block) of the columns that this column group represents. |
| * @param rawBlock The uncompressed block; uncompressed data must be present at the time that the constructor is |
| * called |
| * @param transposed Says if the input matrix raw block have been transposed. |
| * @return AColGroup. |
| */ |
| public static AColGroup create(IColIndex colIndexes, MatrixBlock rawBlock, boolean transposed) { |
| |
| // special cases |
| if(rawBlock.isEmptyBlock(false)) // empty input |
| return new ColGroupEmpty(colIndexes); |
| else if(!transposed && colIndexes.size() == rawBlock.getNumColumns()) |
| // full input to uncompressedColumnGroup |
| return new ColGroupUncompressed(rawBlock, colIndexes); |
| |
| MatrixBlock mb; |
| final int _numRows = transposed ? rawBlock.getNumColumns() : rawBlock.getNumRows(); |
| |
| if(colIndexes.size() == 1) { |
| final int col = colIndexes.get(0); |
| if(transposed) { |
| mb = rawBlock.slice(col, col, 0, rawBlock.getNumColumns() - 1); |
| mb = LibMatrixReorg.transposeInPlace(mb, InfrastructureAnalyzer.getLocalParallelism()); |
| } |
| else |
| mb = rawBlock.slice(0, rawBlock.getNumRows() - 1, col, col); |
| |
| return create(mb, colIndexes); |
| } |
| |
| // Create a matrix with just the requested rows of the original block |
| mb = new MatrixBlock(_numRows, colIndexes.size(), rawBlock.isInSparseFormat()); |
| |
| final int m = _numRows; |
| final int n = colIndexes.size(); |
| |
| if(transposed) |
| for(int i = 0; i < m; i++) |
| for(int j = 0; j < n; j++) |
| mb.appendValue(i, j, rawBlock.quickGetValue(colIndexes.get(j), i)); |
| else |
| for(int i = 0; i < m; i++) |
| for(int j = 0; j < n; j++) |
| mb.appendValue(i, j, rawBlock.quickGetValue(i, colIndexes.get(j))); |
| |
| mb.recomputeNonZeros(); |
| mb.examSparsity(); |
| |
| return create(mb, colIndexes); |
| |
| } |
| |
| public static AColGroup create(MatrixBlock data) { |
| return create(ColIndexFactory.create(data.getNumColumns()), data, false); |
| } |
| |
| @Override |
| public CompressionType getCompType() { |
| return CompressionType.UNCOMPRESSED; |
| } |
| |
| @Override |
| public ColGroupType getColGroupType() { |
| return ColGroupType.UNCOMPRESSED; |
| } |
| |
| /** |
| * Access for superclass |
| * |
| * @return direct pointer to the internal representation of the columns |
| */ |
| public MatrixBlock getData() { |
| return _data; |
| } |
| |
| @Override |
| public long estimateInMemorySize() { |
| return ColGroupSizes.estimateInMemorySizeUncompressed(_data.getNumRows(), _colIndexes.isContiguous(), |
| getNumCols(), _data.getSparsity()); |
| } |
| |
| @Override |
| public void decompressToDenseBlock(DenseBlock db, int rl, int ru, int offR, int offC) { |
| // _data is never empty |
| if(_data.isInSparseFormat()) |
| decompressToDenseBlockSparseData(db, rl, ru, offR, offC); |
| else if(_colIndexes.size() == db.getDim(1)) |
| decompressToDenseBlockDenseDataAllColumns(db, rl, ru, offR); |
| else |
| decompressToDenseBlockDenseData(db, rl, ru, offR, offC); |
| } |
| |
| private void decompressToDenseBlockDenseData(DenseBlock db, int rl, int ru, int offR, int offC) { |
| int offT = rl + offR; |
| final int nCol = _colIndexes.size(); |
| final double[] values = _data.getDenseBlockValues(); |
| int offS = rl * nCol; |
| for(int row = rl; row < ru; row++, offT++, offS += nCol) { |
| final double[] c = db.values(offT); |
| final int off = db.pos(offT) + offC; |
| for(int j = 0; j < nCol; j++) |
| c[off + _colIndexes.get(j)] += values[offS + j]; |
| } |
| } |
| |
| private void decompressToDenseBlockDenseDataAllColumns(DenseBlock db, int rl, int ru, int offR) { |
| int offT = rl + offR; |
| final int nCol = _colIndexes.size(); |
| final double[] values = _data.getDenseBlockValues(); |
| int offS = rl * nCol; |
| for(int row = rl; row < ru; row++, offT++, offS += nCol) { |
| final double[] c = db.values(offT); |
| final int off = db.pos(offT); |
| for(int j = 0; j < nCol; j++) |
| c[off + j] += values[offS + j]; |
| } |
| } |
| |
| private void decompressToDenseBlockSparseData(DenseBlock db, int rl, int ru, int offR, int offC) { |
| |
| final SparseBlock sb = _data.getSparseBlock(); |
| for(int row = rl, offT = rl + offR; row < ru; row++, offT++) { |
| if(sb.isEmpty(row)) |
| continue; |
| final double[] c = db.values(offT); |
| final int off = db.pos(offT) + offC; |
| final int apos = sb.pos(row); |
| final int alen = sb.size(row) + apos; |
| final int[] aix = sb.indexes(row); |
| final double[] avals = sb.values(row); |
| for(int col = apos; col < alen; col++) |
| c[_colIndexes.get(aix[col]) + off] += avals[col]; |
| } |
| } |
| |
| @Override |
| public void decompressToSparseBlock(SparseBlock ret, int rl, int ru, int offR, int offC) { |
| // data is never empty |
| if(_data.isInSparseFormat()) |
| decompressToSparseBlockSparseData(ret, rl, ru, offR, offC); |
| else |
| decompressToSparseBlockDenseData(ret, rl, ru, offR, offC); |
| } |
| |
| private void decompressToSparseBlockDenseData(SparseBlock ret, int rl, int ru, int offR, int offC) { |
| final int nCol = _colIndexes.size(); |
| final double[] values = _data.getDenseBlockValues(); |
| int offS = rl * nCol; |
| for(int row = rl, offT = rl + offR; row < ru; row++, offT++, offS += nCol) |
| for(int j = 0; j < nCol; j++) |
| ret.append(offT, offC + _colIndexes.get(j), values[offS + j]); |
| } |
| |
| private void decompressToSparseBlockSparseData(SparseBlock ret, int rl, int ru, int offR, int offC) { |
| int offT = rl + offR; |
| final SparseBlock sb = _data.getSparseBlock(); |
| for(int row = rl; row < ru; row++, offT++) { |
| if(sb.isEmpty(row)) |
| continue; |
| final int apos = sb.pos(row); |
| final int alen = sb.size(row) + apos; |
| final int[] aix = sb.indexes(row); |
| final double[] avals = sb.values(row); |
| for(int col = apos; col < alen; col++) |
| ret.append(offT, offC + _colIndexes.get(aix[col]), avals[col]); |
| } |
| } |
| |
| @Override |
| public double getIdx(int r, int colIdx) { |
| return _data.quickGetValue(r, colIdx); |
| } |
| |
| @Override |
| public void leftMultByMatrixNoPreAgg(MatrixBlock matrix, MatrixBlock result, int rl, int ru, int cl, int cu) { |
| final int nCol = matrix.getNumColumns(); |
| final int nColRet = result.getNumColumns(); |
| final double[] retV = result.getDenseBlockValues(); |
| if(matrix.isInSparseFormat()) |
| lmmNPSparse(matrix.getSparseBlock(), nCol, retV, nColRet, rl, ru, cl, cu); |
| else { |
| final DenseBlock db = matrix.getDenseBlock(); |
| if(db.isContiguous()) |
| lmmNPDense(db.values(0), nCol, retV, nColRet, rl, ru, cl, cu); |
| else |
| throw new NotImplementedException( |
| "Not implemented support for leftMultByMatrixNoPreAgg non contiguous dense matrix"); |
| } |
| |
| } |
| |
| protected void lmmNPSparse(SparseBlock sb, int nCol, double[] retV, int nColRet, int rl, int ru, int cl, int cu) { |
| if(cl != 0 || cu != _data.getNumRows()) |
| throw new NotImplementedException(); |
| if(_data.isInSparseFormat()) { |
| final SparseBlock dsb = _data.getSparseBlock(); |
| for(int r = rl; r < ru; r++) { |
| if(sb.isEmpty(r)) |
| continue; |
| final int aposL = sb.pos(r); |
| final int alenL = sb.size(r) + aposL; |
| final int[] aixL = sb.indexes(r); |
| final double[] avalL = sb.values(r); |
| final int offR = r * nColRet; |
| for(int j = aposL; j < alenL; j++) { |
| final int c = aixL[j]; |
| if(dsb.isEmpty(c)) |
| continue; |
| final double v = avalL[j]; |
| final int apos = dsb.pos(c); |
| final int alen = dsb.size(c) + apos; |
| final int[] aix = dsb.indexes(c); |
| final double[] aval = dsb.values(c); |
| for(int i = apos; i < alen; i++) |
| retV[offR + _colIndexes.get(aix[i])] += v * aval[i]; |
| } |
| } |
| } |
| else { |
| final double[] dV = _data.getDenseBlockValues(); |
| final int nColD = _colIndexes.size(); |
| for(int r = rl; r < ru; r++) { |
| if(sb.isEmpty(r)) |
| continue; |
| final int aposL = sb.pos(r); |
| final int alenL = sb.size(r) + aposL; |
| final int[] aixL = sb.indexes(r); |
| final double[] avalL = sb.values(r); |
| final int offR = r * nColRet; |
| for(int j = aposL; j < alenL; j++) { |
| final int c = aixL[j]; |
| final double v = avalL[j]; |
| final int offD = c * nColD; |
| for(int i = 0; i < nColD; i++) |
| retV[offR + _colIndexes.get(i)] += v * dV[offD + i]; |
| } |
| } |
| } |
| } |
| |
| protected void lmmNPDense(double[] mV, int nCol, double[] retV, int nColRet, int rl, int ru, int cl, int cu) { |
| |
| if(_data.isInSparseFormat()) { |
| final SparseBlock sb = _data.getSparseBlock(); |
| for(int r = rl; r < ru; r++) { |
| final int off = r * nCol; |
| final int offR = r * nColRet; |
| for(int c = cl; c < cu; c++) { |
| if(sb.isEmpty(c)) |
| continue; |
| final int apos = sb.pos(c); |
| final int alen = sb.size(c) + apos; |
| final int[] aix = sb.indexes(c); |
| final double[] aval = sb.values(c); |
| final double v = mV[off + c]; |
| for(int i = apos; i < alen; i++) |
| retV[offR + _colIndexes.get(aix[i])] += v * aval[i]; |
| |
| } |
| } |
| } |
| else { |
| final double[] dV = _data.getDenseBlockValues(); |
| final int nColD = _colIndexes.size(); |
| for(int r = rl; r < ru; r++) { // I |
| final int off = r * nCol; |
| final int offR = r * nColRet; |
| for(int c = cl; c < cu; c++) { // K |
| final int offD = c * nColD; |
| final double v = mV[off + c]; |
| for(int i = 0; i < nColD; i++) // J |
| retV[offR + _colIndexes.get(i)] += v * dV[offD + i]; |
| } |
| } |
| } |
| } |
| |
| @Override |
| public AColGroup scalarOperation(ScalarOperator op) { |
| MatrixBlock retContent = _data.scalarOperations(op, new MatrixBlock()); |
| return create(retContent, getColIndices()); |
| } |
| |
| @Override |
| public AColGroup unaryOperation(UnaryOperator op) { |
| MatrixBlock retContent = _data.unaryOperations(op, new MatrixBlock()); |
| return create(retContent, getColIndices()); |
| } |
| |
| @Override |
| public AColGroup binaryRowOpLeft(BinaryOperator op, double[] v, boolean isRowSafe) { |
| LOG.warn("Binary row op left is not supported for Uncompressed Matrix, " |
| + "Implement support for VMr in MatrixBlock Binary Cell operations"); |
| MatrixBlockDictionary d = MatrixBlockDictionary.create(_data); |
| ADictionary dm = d.binOpLeft(op, v, _colIndexes); |
| if(dm == null) |
| return create(null, _colIndexes); |
| else |
| return create(((MatrixBlockDictionary) dm).getMatrixBlock(), _colIndexes); |
| } |
| |
| @Override |
| public AColGroup binaryRowOpRight(BinaryOperator op, double[] v, boolean isRowSafe) { |
| MatrixBlock rowVector = Util.extractValues(v, _colIndexes); |
| return create(_data.binaryOperations(op, rowVector, null), _colIndexes); |
| } |
| |
| @Override |
| public void unaryAggregateOperations(AggregateUnaryOperator op, double[] result, int nRows, int rl, int ru) { |
| final ValueFunction fn = op.aggOp.increOp.fn; |
| if(fn instanceof Multiply && op.indexFn instanceof ReduceAll && result[0] == 0) |
| return; // product |
| else if((fn instanceof Builtin && ((Builtin) fn).getBuiltinCode() == BuiltinCode.MAXINDEX) // index |
| || (fn instanceof CM)) |
| throw new DMLRuntimeException("Not supported type of Unary Aggregate on colGroup"); |
| |
| // inefficient since usually uncompressed column groups are used in case of extreme sparsity, it is fine |
| // using a slice, since we don't allocate extra just extract the pointers to the sparse rows. |
| |
| final MatrixBlock tmpData = (rl == 0 && ru == nRows) ? _data : _data.slice(rl, ru - 1, false); |
| MatrixBlock tmp = tmpData.aggregateUnaryOperations(op, new MatrixBlock(), tmpData.getNumRows(), |
| new MatrixIndexes(1, 1), true); |
| |
| if(tmp.isEmpty()) { |
| if(op.aggOp.increOp.fn instanceof Builtin) { |
| Builtin b = (Builtin) op.aggOp.increOp.fn; |
| if(op.indexFn instanceof ReduceRow) |
| for(int i = 0; i < _colIndexes.size(); i++) |
| result[_colIndexes.get(i)] = b.execute(result[_colIndexes.get(i)], 0); |
| else if(op.indexFn instanceof ReduceAll) |
| result[0] = b.execute(result[0], 0); |
| else |
| for(int row = rl; row < ru; row++) |
| result[row] = b.execute(result[row], 0); |
| } |
| else if(op.aggOp.increOp.fn instanceof Multiply) { |
| if(op.indexFn instanceof ReduceRow) |
| for(int i = 0; i < _colIndexes.size(); i++) |
| result[_colIndexes.get(i)] = 0; |
| else if(op.indexFn instanceof ReduceAll) |
| result[0] = 0; |
| else |
| Arrays.fill(result, rl, ru, 0); |
| } |
| // sum etc. |
| return; |
| } |
| |
| tmp.sparseToDense(); |
| // The output is always dense in unary aggregates. |
| final double[] tmpV = tmp.getDenseBlockValues(); |
| |
| if(op.aggOp.increOp.fn instanceof Builtin) { |
| Builtin b = (Builtin) op.aggOp.increOp.fn; |
| if(op.indexFn instanceof ReduceRow) |
| for(int i = 0; i < tmpV.length; i++) |
| result[_colIndexes.get(i)] = b.execute(result[_colIndexes.get(i)], tmpV[i]); |
| else if(op.indexFn instanceof ReduceAll) |
| result[0] = b.execute(result[0], tmpV[0]); |
| else |
| for(int i = 0, row = rl; row < ru; i++, row++) |
| result[row] = b.execute(result[row], tmpV[i]); |
| } |
| else if(op.aggOp.increOp.fn instanceof Multiply) { |
| if(op.indexFn instanceof ReduceRow) |
| for(int i = 0; i < tmpV.length; i++) |
| result[_colIndexes.get(i)] = tmpV[i]; |
| else if(op.indexFn instanceof ReduceAll) |
| result[0] *= tmpV[0]; |
| else |
| for(int i = 0, row = rl; row < ru; i++, row++) |
| result[row] *= tmpV[i]; |
| } |
| else { |
| if(op.indexFn instanceof ReduceRow) |
| for(int i = 0; i < tmpV.length; i++) |
| result[_colIndexes.get(i)] += tmpV[i]; |
| else if(op.indexFn instanceof ReduceAll) |
| result[0] += tmpV[0]; |
| else |
| for(int i = 0, row = rl; row < ru; i++, row++) |
| result[row] += tmpV[i]; |
| } |
| } |
| |
| public static ColGroupUncompressed read(DataInput in) throws IOException { |
| IColIndex cols = ColIndexFactory.read(in); |
| MatrixBlock data = new MatrixBlock(); |
| data.readFields(in); |
| return new ColGroupUncompressed(data, cols); |
| } |
| |
| @Override |
| public void write(DataOutput out) throws IOException { |
| super.write(out); |
| _data.write(out); |
| } |
| |
| @Override |
| public long getExactSizeOnDisk() { |
| return super.getExactSizeOnDisk() + _data.getExactSizeOnDisk(); |
| } |
| |
| @Override |
| public double getMin() { |
| return _data.min(); |
| } |
| |
| @Override |
| public double getMax() { |
| return _data.max(); |
| } |
| |
| @Override |
| public double getSum(int nRows) { |
| return _data.sum(); |
| } |
| |
| @Override |
| public final void tsmm(MatrixBlock ret, int nRows) { |
| |
| final int tCol = _colIndexes.size(); |
| final MatrixBlock tmp = new MatrixBlock(tCol, tCol, true); |
| |
| // tsmm but only upper triangle. |
| LibMatrixMult.matrixMultTransposeSelf(_data, tmp, true, false); |
| |
| // copy that upper triangle part to ret |
| final int numColumns = ret.getNumColumns(); |
| final double[] result = ret.getDenseBlockValues(); |
| final double[] tmpV = tmp.getDenseBlockValues(); |
| for(int row = 0, offTmp = 0; row < tCol; row++, offTmp += tCol) { |
| final int offRet = _colIndexes.get(row) * numColumns; |
| for(int col = row; col < tCol; col++) |
| result[offRet + _colIndexes.get(col)] += tmpV[offTmp + col]; |
| } |
| } |
| |
| @Override |
| public boolean containsValue(double pattern) { |
| return _data.containsValue(pattern); |
| } |
| |
| @Override |
| public long getNumberNonZeros(int nRows) { |
| return _data.getNonZeros(); |
| } |
| |
| @Override |
| public void leftMultByAColGroup(AColGroup lhs, MatrixBlock result, int nRows) { |
| if(lhs instanceof ColGroupUncompressed) |
| leftMultByAColGroupUncompressed((ColGroupUncompressed) lhs, result); |
| else if(lhs instanceof APreAgg) |
| leftMultByAPreAggColGroup((APreAgg) lhs, result); |
| else |
| throw new DMLCompressionException("Not supported leftMult colgroup type: " + lhs.getClass().getSimpleName()); |
| } |
| |
| private void leftMultByAPreAggColGroup(APreAgg paCG, MatrixBlock result) { |
| final int nCols = paCG.getNumCols(); |
| final MatrixBlock dictM = paCG._dict.getMBDict(nCols).getMatrixBlock(); |
| if(dictM == null) |
| return; |
| LOG.warn("\nInefficient transpose of uncompressed to fit to" |
| + " t(AColGroup) %*% UncompressedColGroup mult by colGroup uncompressed column" |
| + "\nCurrently solved by t(t(Uncompressed) %*% AColGroup)"); |
| final int k = InfrastructureAnalyzer.getLocalParallelism(); |
| final MatrixBlock ucCGT = LibMatrixReorg.transpose(getData(), k); |
| final MatrixBlock preAgg = new MatrixBlock(1, paCG.getNumValues(), false); |
| final MatrixBlock tmpRes = new MatrixBlock(1, nCols, false); |
| preAgg.allocateDenseBlock(); |
| tmpRes.allocateDenseBlock(); |
| final int nRowsTransposed = ucCGT.getNumRows(); |
| final double[] retV = result.getDenseBlockValues(); |
| final double[] tmpV = tmpRes.getDenseBlockValues(); |
| final int retCols = result.getNumColumns(); |
| |
| // Process a row at a time in the transposed block. |
| for(int i = 0; i < nRowsTransposed; i++) { |
| if(ucCGT.isInSparseFormat() && ucCGT.getSparseBlock().isEmpty(i)) |
| continue; |
| paCG.preAggregate(ucCGT, preAgg.getDenseBlockValues(), i, i + 1); |
| preAgg.recomputeNonZeros(); |
| if(preAgg.isEmpty()) |
| continue; |
| // Fixed ret to enforce that we do not allocate again. |
| LibMatrixMult.matrixMult(preAgg, dictM, tmpRes, true); |
| |
| final int rowOut = _colIndexes.get(i); |
| for(int j = 0; j < nCols; j++) { |
| final int colOut = paCG._colIndexes.get(j) * retCols; |
| retV[rowOut + colOut] += tmpV[j]; |
| } |
| if(i < nRowsTransposed - 1) { |
| preAgg.reset(1, paCG.getPreAggregateSize()); |
| tmpRes.reset(1, nCols); |
| } |
| } |
| } |
| |
| private void leftMultByAColGroupUncompressed(ColGroupUncompressed lhs, MatrixBlock result) { |
| LOG.warn("Inefficient Left Matrix Multiplication with transpose of left hand side : t(l) %*% r"); |
| final MatrixBlock tmpRet = new MatrixBlock(lhs.getNumCols(), _colIndexes.size(), 0); |
| final int k = InfrastructureAnalyzer.getLocalParallelism(); |
| |
| // multiply to temp |
| MatrixBlock lhData = lhs._data; |
| MatrixBlock transposed = LibMatrixReorg.transpose(lhData, k); |
| transposed.setNonZeros(lhData.getNonZeros()); |
| // do transposed left hand side, matrix multiplication. |
| LibMatrixMult.matrixMult(transposed, this._data, tmpRet); |
| |
| // add temp to output |
| final double[] resV = result.getDenseBlockValues(); |
| final int nColOut = result.getNumColumns(); |
| // Guaranteed not empty both sides, therefore safe to not check for empty |
| if(tmpRet.isInSparseFormat()) { |
| SparseBlock sb = tmpRet.getSparseBlock(); |
| for(int row = 0; row < lhs._colIndexes.size(); row++) { |
| if(sb.isEmpty(row)) |
| continue; |
| final int apos = sb.pos(row); |
| final int alen = sb.size(row) + apos; |
| final int[] aix = sb.indexes(row); |
| final double[] avals = sb.values(row); |
| final int offRes = lhs._colIndexes.get(row) * nColOut; |
| for(int col = apos; col < alen; col++) |
| resV[offRes + _colIndexes.get(aix[col])] += avals[col]; |
| } |
| } |
| else { |
| final double[] tmpRetV = tmpRet.getDenseBlockValues(); |
| for(int row = 0; row < lhs._colIndexes.size(); row++) { |
| final int offRes = lhs._colIndexes.get(row) * nColOut; |
| final int offTmp = _colIndexes.size() * row; |
| for(int col = 0; col < _colIndexes.size(); col++) |
| resV[offRes + _colIndexes.get(col)] += tmpRetV[offTmp + col]; |
| } |
| } |
| } |
| |
| @Override |
| public void tsmmAColGroup(AColGroup lhs, MatrixBlock result) { |
| // this is never empty therefore process: |
| if(lhs instanceof ColGroupUncompressed) |
| tsmmUncompressedColGroup((ColGroupUncompressed) lhs, result); |
| else |
| lhs.tsmmAColGroup(this, result); |
| } |
| |
| private void tsmmUncompressedColGroup(ColGroupUncompressed lhs, MatrixBlock result) { |
| final MatrixBlock tmpRet = new MatrixBlock(lhs.getNumCols(), _colIndexes.size(), 0); |
| final int k = InfrastructureAnalyzer.getLocalParallelism(); |
| |
| if(lhs._data == this._data) |
| LibMatrixMult.matrixMultTransposeSelf(this._data, tmpRet, true, k); |
| else { |
| LOG.warn("Inefficient Left Matrix Multiplication with transpose of left hand side : t(l) %*% r"); |
| LibMatrixMult.matrixMult(LibMatrixReorg.transpose(lhs._data, k), this._data, tmpRet); |
| } |
| |
| final double[] resV = result.getDenseBlockValues(); |
| final int nCols = result.getNumColumns(); |
| // guaranteed non empty |
| if(tmpRet.isInSparseFormat()) { |
| SparseBlock sb = tmpRet.getSparseBlock(); |
| for(int row = 0; row < lhs._colIndexes.size(); row++) { |
| if(sb.isEmpty(row)) |
| continue; |
| final int apos = sb.pos(row); |
| final int alen = sb.size(row) + apos; |
| final int[] aix = sb.indexes(row); |
| final double[] avals = sb.values(row); |
| for(int col = apos; col < alen; col++) |
| DictLibMatrixMult.addToUpperTriangle(nCols, lhs._colIndexes.get(row), _colIndexes.get(aix[col]), resV, |
| avals[col]); |
| } |
| } |
| else { |
| double[] tmpRetV = tmpRet.getDenseBlockValues(); |
| for(int row = 0; row < lhs._colIndexes.size(); row++) { |
| final int offTmp = lhs._colIndexes.size() * row; |
| final int oid = lhs._colIndexes.get(row); |
| for(int col = 0; col < _colIndexes.size(); col++) |
| DictLibMatrixMult.addToUpperTriangle(nCols, oid, _colIndexes.get(col), resV, tmpRetV[offTmp + col]); |
| } |
| } |
| } |
| |
| @Override |
| protected AColGroup sliceSingleColumn(int idx) { |
| return sliceMultiColumns(idx, idx + 1, ColIndexFactory.create(1)); |
| } |
| |
| @Override |
| protected AColGroup sliceMultiColumns(int idStart, int idEnd, IColIndex outputCols) { |
| MatrixBlock newData = _data.slice(0, _data.getNumRows() - 1, idStart, idEnd - 1, true); |
| return create(newData, outputCols); |
| } |
| |
| @Override |
| public AColGroup rightMultByMatrix(MatrixBlock right, IColIndex allCols) { |
| final int nColR = right.getNumColumns(); |
| final IColIndex outputCols = allCols != null ? allCols : ColIndexFactory.create(nColR); |
| |
| if(right.isEmpty()) |
| return null; |
| |
| MatrixBlock subBlockRight; |
| |
| if(right.isInSparseFormat()) { |
| subBlockRight = new MatrixBlock(_data.getNumColumns(), nColR, true); |
| subBlockRight.allocateSparseRowsBlock(); |
| final SparseBlock sbR = right.getSparseBlock(); |
| final SparseBlock subR = subBlockRight.getSparseBlock(); |
| long nnz = 0; |
| for(int i = 0; i < _colIndexes.size(); i++) { |
| if(sbR.isEmpty(_colIndexes.get(i))) |
| continue; |
| subR.set(i, sbR.get(_colIndexes.get(i)), false); |
| nnz += sbR.get(_colIndexes.get(i)).size(); |
| } |
| subBlockRight.setNonZeros(nnz); |
| } |
| else { |
| subBlockRight = new MatrixBlock(_data.getNumColumns(), nColR, false); |
| subBlockRight.allocateDenseBlock(); |
| final double[] sbr = subBlockRight.getDenseBlockValues(); |
| final double[] rightV = right.getDenseBlockValues(); |
| for(int i = 0; i < _colIndexes.size(); i++) { |
| final int offSubBlock = i * nColR; |
| final int offRight = _colIndexes.get(i) * nColR; |
| System.arraycopy(rightV, offRight, sbr, offSubBlock, nColR); |
| } |
| subBlockRight.setNonZeros(_data.getNumColumns() * nColR); |
| } |
| MatrixBlock out = new MatrixBlock(_data.getNumRows(), nColR, false); |
| LibMatrixMult.matrixMult(_data, subBlockRight, out, InfrastructureAnalyzer.getLocalParallelism()); |
| return create(out, outputCols); |
| |
| } |
| |
| @Override |
| public int getNumValues() { |
| return _data.getNumRows(); |
| } |
| |
| @Override |
| public AColGroup replace(double pattern, double replace) { |
| MatrixBlock replaced = _data.replaceOperations(new MatrixBlock(), pattern, replace); |
| return create(replaced, _colIndexes); |
| } |
| |
| @Override |
| public void computeColSums(double[] c, int nRows) { |
| final MatrixBlock colSum = _data.colSum(); |
| if(colSum.isInSparseFormat()) { |
| SparseBlock sb = colSum.getSparseBlock(); |
| double[] rv = sb.values(0); |
| int[] idx = sb.indexes(0); |
| for(int i = 0; i < idx.length; i++) |
| c[_colIndexes.get(idx[i])] += rv[i]; |
| } |
| else { |
| double[] dv = colSum.getDenseBlockValues(); |
| for(int i = 0; i < _colIndexes.size(); i++) |
| c[_colIndexes.get(i)] += dv[i]; |
| } |
| } |
| |
| @Override |
| public CM_COV_Object centralMoment(CMOperator op, int nRows) { |
| return _data.cmOperations(op); |
| } |
| |
| @Override |
| public AColGroup rexpandCols(int max, boolean ignore, boolean cast, int nRows) { |
| MatrixBlock nd = LibMatrixReorg.rexpand(_data, new MatrixBlock(), max, false, cast, ignore, 1); |
| return create(nd, ColIndexFactory.create(max)); |
| } |
| |
| @Override |
| public double getCost(ComputationCostEstimator e, int nRows) { |
| final int nVals = getNumValues(); |
| final int nCols = getNumCols(); |
| return e.getCost(nRows, nRows, nCols, nVals, _data.getSparsity()); |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| return _data.isEmpty(); |
| } |
| |
| @Override |
| public AColGroup sliceRows(int rl, int ru) { |
| final MatrixBlock mb = _data.slice(rl, ru - 1); |
| if(mb.isEmpty()) |
| return null; |
| return new ColGroupUncompressed(mb, _colIndexes); |
| } |
| |
| @Override |
| public AColGroup append(AColGroup g) { |
| if(g instanceof ColGroupUncompressed && g.getColIndices().equals(_colIndexes)) { |
| final ColGroupUncompressed gDDC = (ColGroupUncompressed) g; |
| final MatrixBlock nd = _data.append(gDDC._data, false); |
| return create(nd, _colIndexes); |
| } |
| return null; |
| } |
| |
| @Override |
| public AColGroup appendNInternal(AColGroup[] g) { |
| return null; |
| } |
| |
| @Override |
| public ICLAScheme getCompressionScheme() { |
| return null; |
| } |
| |
| @Override |
| public AColGroup recompress() { |
| MatrixBlock mb = CompressedMatrixBlockFactory.compress(_data).getLeft(); |
| if(mb instanceof CompressedMatrixBlock) { |
| CompressedMatrixBlock cmb = (CompressedMatrixBlock) mb; |
| List<AColGroup> gs = cmb.getColGroups(); |
| if(gs.size() > 1) { |
| LOG.error("The uncompressed column group did compress into multiple groups"); |
| return this; |
| } |
| else { |
| return gs.get(0).copyAndSet(_colIndexes); |
| } |
| } |
| else |
| return this; |
| } |
| |
| @Override |
| public CompressedSizeInfoColGroup getCompressionInfo(int nRow) { |
| final IEncode map = EncodingFactory.createFromMatrixBlock(_data, false, |
| ColIndexFactory.create(_data.getNumColumns())); |
| final int _numRows = _data.getNumRows(); |
| final CompressionSettings _cs = new CompressionSettingsBuilder().create();// default settings |
| final EstimationFactors em = map.extractFacts(_numRows, _data.getSparsity(), _data.getSparsity(), _cs); |
| return new CompressedSizeInfoColGroup(_colIndexes, em, _cs.validCompressions, map); |
| } |
| |
| @Override |
| public AColGroup copyAndSet(IColIndex colIndexes) { |
| return ColGroupUncompressed.create(_data, colIndexes); |
| } |
| |
| @Override |
| protected AColGroup fixColIndexes(IColIndex newColIndex, int[] reordering) { |
| MatrixBlock ret = new MatrixBlock(_data.getNumRows(), _data.getNumColumns(), _data.getNonZeros()); |
| // TODO add sparse optmization |
| for(int r = 0; r < _data.getNumRows(); r++) |
| for(int c = 0; c < _data.getNumColumns(); c++) |
| ret.quickSetValue(r, c, _data.quickGetValue(r, reordering[c])); |
| return create(newColIndex, ret, false); |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(super.toString()); |
| sb.append("\n"); |
| sb.append(" numCols : " + _data.getNumColumns()); |
| sb.append(" numRows : " + _data.getNumRows()); |
| sb.append(" nonZeros: " + _data.getNonZeros()); |
| sb.append(" Sparse : " + _data.isInSparseFormat()); |
| |
| sb.append("\n"); |
| if(_data.getNumRows() < 1000) |
| sb.append(_data.toString()); |
| else |
| sb.append(" don't print uncompressed matrix because it is to big."); |
| |
| return sb.toString(); |
| } |
| } |