blob: ffa9656f54334e97ce1667dfc388e9e8495245c5 [file] [log] [blame]
/*
* 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();
}
}