blob: 632db578e6e2113edf10c65dc71af3fccb48bc1c [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.dictionary;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.MathContext;
import java.util.Arrays;
import org.apache.sysds.hops.OptimizerUtils;
import org.apache.sysds.runtime.compress.DMLCompressionException;
import org.apache.sysds.runtime.compress.colgroup.indexes.IColIndex;
import org.apache.sysds.runtime.data.SparseBlock;
import org.apache.sysds.runtime.functionobjects.Builtin;
import org.apache.sysds.runtime.functionobjects.Plus;
import org.apache.sysds.runtime.functionobjects.ValueFunction;
import org.apache.sysds.runtime.instructions.cp.CM_COV_Object;
import org.apache.sysds.runtime.matrix.data.MatrixBlock;
import org.apache.sysds.runtime.matrix.operators.BinaryOperator;
import org.apache.sysds.runtime.matrix.operators.LeftScalarOperator;
import org.apache.sysds.runtime.matrix.operators.ScalarOperator;
import org.apache.sysds.runtime.matrix.operators.UnaryOperator;
import org.apache.sysds.utils.MemoryEstimates;
/**
* This dictionary class aims to encapsulate the storage and operations over unique floating point values of a column
* group. The primary reason for its introduction was to provide an entry point for specialization such as shared
* dictionaries, which require additional information.
*/
public class Dictionary extends ADictionary {
private static final long serialVersionUID = -6517136537249507753L;
protected final double[] _values;
protected Dictionary(double[] values) {
_values = values;
}
public static Dictionary create(double[] values) {
if(values == null)
throw new DMLCompressionException("Invalid construction of dictionary with null array");
else if(values.length == 0)
throw new DMLCompressionException("Invalid construction of dictionary with empty array");
boolean nonZero = false;
for(double d : values) {
if(d != 0) {
nonZero = true;
break;
}
}
return nonZero ? new Dictionary(values) : null;
}
public static Dictionary createNoCheck(double[] values) {
return new Dictionary(values);
}
@Override
public double[] getValues() {
return _values;
}
@Override
public double getValue(int i) {
return _values[i];
}
@Override
public final double getValue(int r, int c, int nCol) {
return _values[r * nCol + c];
}
@Override
public long getInMemorySize() {
// object + values array + double
return getInMemorySize(size());
}
protected static long getInMemorySize(int valuesCount) {
// object + values array
return 16 + (long) MemoryEstimates.doubleArrayCost(valuesCount);
}
@Override
public double aggregate(double init, Builtin fn) {
// full aggregate can disregard tuple boundaries
double ret = init;
for(int i = 0; i < _values.length; i++)
ret = fn.execute(ret, _values[i]);
return ret;
}
@Override
public double aggregateWithReference(double init, Builtin fn, double[] reference, boolean def) {
final int nCol = reference.length;
double ret = init;
for(int i = 0; i < _values.length; i++)
ret = fn.execute(ret, _values[i] + reference[i % nCol]);
if(def)
for(int i = 0; i < nCol; i++)
ret = fn.execute(ret, reference[i]);
return ret;
}
@Override
public double[] aggregateRows(Builtin fn, int nCol) {
if(nCol == 1)
return _values;
final int nRows = _values.length / nCol;
double[] res = new double[nRows];
for(int i = 0; i < nRows; i++) {
final int off = i * nCol;
res[i] = _values[off];
for(int j = off + 1; j < off + nCol; j++)
res[i] = fn.execute(res[i], _values[j]);
}
return res;
}
@Override
public double[] aggregateRowsWithDefault(Builtin fn, double[] defaultTuple) {
final int nCol = defaultTuple.length;
final int nRows = _values.length / nCol;
double[] res = new double[nRows + 1];
for(int i = 0; i < nRows; i++) {
final int off = i * nCol;
res[i] = _values[off];
for(int j = off + 1; j < off + nCol; j++)
res[i] = fn.execute(res[i], _values[j]);
}
final int def = res.length - 1;
res[def] = defaultTuple[0];
for(int i = 1; i < nCol; i++)
res[def] = fn.execute(res[def], defaultTuple[i]);
return res;
}
@Override
public double[] aggregateRowsWithReference(Builtin fn, double[] reference) {
final int nCol = reference.length;
final int nRows = _values.length / nCol;
double[] res = new double[nRows + 1];
int off = 0;
for(int i = 0; i < nRows; i++) {
res[i] = _values[off++] + reference[0];
for(int j = 1; j < nCol; j++)
res[i] = fn.execute(res[i], _values[off++] + reference[j]);
}
res[nRows] = reference[0];
for(int i = 0; i < nCol; i++)
res[nRows] = fn.execute(res[nRows], reference[i]);
return res;
}
@Override
public Dictionary applyScalarOp(ScalarOperator op) {
final double[] retV = new double[_values.length];
for(int i = 0; i < _values.length; i++)
retV[i] = op.executeScalar(_values[i]);
return create(retV);
}
@Override
public ADictionary applyScalarOpAndAppend(ScalarOperator op, double v0, int nCol) {
final double[] retV = new double[_values.length + nCol];
for(int i = 0; i < _values.length; i++)
retV[i] = op.executeScalar(_values[i]);
for(int i = _values.length; i < retV.length; i++)
retV[i] = v0;
return create(retV);
}
@Override
public Dictionary applyUnaryOp(UnaryOperator op) {
final double[] retV = new double[_values.length];
for(int i = 0; i < _values.length; i++)
retV[i] = op.fn.execute(_values[i]);
return create(retV);
}
@Override
public ADictionary applyUnaryOpAndAppend(UnaryOperator op, double v0, int nCol) {
final double[] retV = new double[_values.length + nCol];
for(int i = 0; i < _values.length; i++)
retV[i] = op.fn.execute(_values[i]);
for(int i = _values.length; i < retV.length; i++)
retV[i] = v0;
return create(retV);
}
@Override
public Dictionary applyScalarOpWithReference(ScalarOperator op, double[] reference, double[] newReference) {
final double[] retV = new double[_values.length];
final int nCol = reference.length;
final int nRow = _values.length / nCol;
int off = 0;
for(int i = 0; i < nRow; i++) {
for(int j = 0; j < nCol; j++) {
retV[off] = op.executeScalar(_values[off] + reference[j]) - newReference[j];
off++;
}
}
return create(retV);
}
@Override
public ADictionary applyUnaryOpWithReference(UnaryOperator op, double[] reference, double[] newReference) {
final double[] retV = new double[_values.length];
final int nCol = reference.length;
final int nRow = _values.length / nCol;
int off = 0;
for(int i = 0; i < nRow; i++) {
for(int j = 0; j < nCol; j++) {
retV[off] = op.fn.execute(_values[off] + reference[j]) - newReference[j];
off++;
}
}
return create(retV);
}
@Override
public Dictionary binOpRight(BinaryOperator op, double[] v, IColIndex colIndexes) {
final ValueFunction fn = op.fn;
final double[] retVals = new double[_values.length];
final int len = size();
final int lenV = colIndexes.size();
for(int i = 0; i < len; i++)
retVals[i] = fn.execute(_values[i], v[colIndexes.get(i % lenV)]);
return create(retVals);
}
@Override
public Dictionary binOpRight(BinaryOperator op, double[] v) {
final ValueFunction fn = op.fn;
final double[] retVals = new double[_values.length];
final int len = size();
final int lenV = v.length;
for(int i = 0; i < len; i++)
retVals[i] = fn.execute(_values[i], v[i % lenV]);
return create(retVals);
}
@Override
public ADictionary binOpRightAndAppend(BinaryOperator op, double[] v, IColIndex colIndexes) {
final ValueFunction fn = op.fn;
final double[] retVals = new double[_values.length + colIndexes.size()];
final int lenV = colIndexes.size();
for(int i = 0; i < _values.length; i++)
retVals[i] = fn.execute(_values[i], v[colIndexes.get(i % lenV)]);
for(int i = _values.length; i < _values.length; i++)
retVals[i] = fn.execute(0, v[colIndexes.get(i % lenV)]);
return create(retVals);
}
@Override
public Dictionary binOpRightWithReference(BinaryOperator op, double[] v, IColIndex colIndexes, double[] reference,
double[] newReference) {
final ValueFunction fn = op.fn;
final double[] retV = new double[_values.length];
final int nCol = reference.length;
final int nRow = _values.length / nCol;
int off = 0;
for(int i = 0; i < nRow; i++) {
for(int j = 0; j < nCol; j++) {
retV[off] = fn.execute(_values[off] + reference[j], v[colIndexes.get(j)]) - newReference[j];
off++;
}
}
return create(retV);
}
@Override
public final Dictionary binOpLeft(BinaryOperator op, double[] v, IColIndex colIndexes) {
final ValueFunction fn = op.fn;
final double[] retVals = new double[_values.length];
final int lenV = colIndexes.size();
for(int i = 0; i < _values.length; i++)
retVals[i] = fn.execute(v[colIndexes.get(i % lenV)], _values[i]);
return create(retVals);
}
@Override
public ADictionary binOpLeftAndAppend(BinaryOperator op, double[] v, IColIndex colIndexes) {
final ValueFunction fn = op.fn;
final double[] retVals = new double[_values.length + colIndexes.size()];
final int lenV = colIndexes.size();
for(int i = 0; i < _values.length; i++)
retVals[i] = fn.execute(v[colIndexes.get(i % lenV)], _values[i]);
for(int i = _values.length; i < _values.length; i++)
retVals[i] = fn.execute(v[colIndexes.get(i % lenV)], 0);
return create(retVals);
}
@Override
public Dictionary binOpLeftWithReference(BinaryOperator op, double[] v, IColIndex colIndexes, double[] reference,
double[] newReference) {
final ValueFunction fn = op.fn;
final double[] retV = new double[_values.length];
final int nCol = reference.length;
final int nRow = _values.length / nCol;
int off = 0;
for(int i = 0; i < nRow; i++) {
for(int j = 0; j < nCol; j++) {
retV[off] = fn.execute(v[colIndexes.get(j)], _values[off] + reference[j]) - newReference[j];
off++;
}
}
return create(retV);
}
@Override
public Dictionary clone() {
return createNoCheck(_values.clone());
}
public static Dictionary read(DataInput in) throws IOException {
int numVals = in.readInt();
// read distinct values
double[] values = new double[numVals];
for(int i = 0; i < numVals; i++)
values[i] = in.readDouble();
return Dictionary.createNoCheck(values);
}
@Override
public void write(DataOutput out) throws IOException {
out.writeByte(DictionaryFactory.Type.FP64_DICT.ordinal());
out.writeInt(size());
for(int i = 0; i < size(); i++)
out.writeDouble(_values[i]);
}
@Override
public long getExactSizeOnDisk() {
return 1 + 4 + 8 * size();
}
private int size() {
return _values.length;
}
@Override
public int getNumberOfValues(int nCol) {
return _values.length / nCol;
}
@Override
public double[] sumAllRowsToDouble(int nrColumns) {
if(nrColumns == 1)
return getValues(); // shallow copy of values
// pre-aggregate value tuple
final int numVals = getNumberOfValues(nrColumns);
double[] ret = new double[numVals];
for(int k = 0; k < numVals; k++)
ret[k] = sumRow(k, nrColumns);
return ret;
}
@Override
public double[] sumAllRowsToDoubleWithDefault(double[] defaultTuple) {
final int nCol = defaultTuple.length;
final int numVals = getNumberOfValues(nCol);
final double[] ret = new double[numVals + 1];
for(int k = 0; k < numVals; k++)
ret[k] = sumRow(k, nCol);
for(int i = 0; i < nCol; i++)
ret[ret.length - 1] += defaultTuple[i];
return ret;
}
@Override
public double[] sumAllRowsToDoubleWithReference(double[] reference) {
final int nCol = reference.length;
final int numVals = getNumberOfValues(nCol);
double[] ret = new double[numVals + 1];
for(int k = 0; k < numVals; k++)
ret[k] = sumRowWithReference(k, nCol, reference);
for(int i = 0; i < nCol; i++)
ret[numVals] += reference[i];
return ret;
}
@Override
public double[] sumAllRowsToDoubleSq(int nrColumns) {
// pre-aggregate value tuple
final int numVals = getNumberOfValues(nrColumns);
final double[] ret = new double[numVals];
for(int k = 0; k < numVals; k++)
ret[k] = sumRowSq(k, nrColumns);
return ret;
}
@Override
public double[] sumAllRowsToDoubleSqWithDefault(double[] defaultTuple) {
final int nCol = defaultTuple.length;
final int numVals = getNumberOfValues(nCol);
final double[] ret = new double[numVals + 1];
for(int k = 0; k < numVals; k++)
ret[k] = sumRowSq(k, nCol);
for(int i = 0; i < nCol; i++)
ret[ret.length - 1] += defaultTuple[i] * defaultTuple[i];
return ret;
}
@Override
public double[] productAllRowsToDouble(int nCol) {
final int numVals = getNumberOfValues(nCol);
final double[] ret = new double[numVals];
for(int k = 0; k < numVals; k++)
ret[k] = prodRow(k, nCol);
return ret;
}
@Override
public double[] productAllRowsToDoubleWithDefault(double[] defaultTuple) {
final int nCol = defaultTuple.length;
final int numVals = getNumberOfValues(nCol);
final double[] ret = new double[numVals + 1];
for(int k = 0; k < numVals; k++)
ret[k] = prodRow(k, nCol);
ret[ret.length - 1] = defaultTuple[0];
for(int i = 1; i < nCol; i++)
ret[ret.length - 1] *= defaultTuple[i];
return ret;
}
@Override
public double[] productAllRowsToDoubleWithReference(double[] reference) {
final int nCol = reference.length;
final int numVals = getNumberOfValues(nCol);
final double[] ret = new double[numVals + 1];
for(int k = 0; k < numVals; k++)
ret[k] = prodRowWithReference(k, nCol, reference);
ret[ret.length - 1] = reference[0];
for(int i = 1; i < nCol; i++)
ret[ret.length - 1] *= reference[i];
return ret;
}
@Override
public double[] sumAllRowsToDoubleSqWithReference(double[] reference) {
final int nCol = reference.length;
final int numVals = getNumberOfValues(nCol);
double[] ret = new double[numVals + 1];
for(int k = 0; k < numVals; k++)
ret[k] = sumRowSqWithReference(k, nCol, reference);
for(int i = 0; i < nCol; i++)
ret[numVals] += reference[i] * reference[i];
return ret;
}
private double sumRow(int k, int nrColumns) {
final int valOff = k * nrColumns;
double res = 0.0;
for(int i = 0; i < nrColumns; i++)
res += _values[valOff + i];
return res;
}
public double sumRowWithReference(int k, int nrColumns, double[] reference) {
final int valOff = k * nrColumns;
double res = 0.0;
for(int i = 0; i < nrColumns; i++)
res += _values[valOff + i] + reference[i];
return res;
}
private double sumRowSq(int k, int nrColumns) {
final int valOff = k * nrColumns;
double res = 0.0;
for(int i = 0; i < nrColumns; i++)
res += _values[valOff + i] * _values[valOff + i];
return res;
}
private double prodRow(int k, int nrColumns) {
final int valOff = k * nrColumns;
double res = _values[valOff];
for(int i = 1; i < nrColumns; i++)
res *= _values[valOff + i];
return res;
}
private double prodRowWithReference(int k, int nrColumns, double[] reference) {
final int valOff = k * nrColumns;
double res = _values[valOff] + reference[0];
for(int i = 1; i < nrColumns; i++)
res *= _values[valOff + i] + reference[i];
return res;
}
private double sumRowSqWithReference(int k, int nrColumns, double[] reference) {
final int valOff = k * nrColumns;
double res = 0.0;
for(int i = 0; i < nrColumns; i++) {
final double v = _values[valOff + i] + reference[i];
res += v * v;
}
return res;
}
@Override
public void colSum(double[] c, int[] counts, IColIndex colIndexes) {
final int nCol = colIndexes.size();
for(int k = 0; k < counts.length; k++) {
final int cntk = counts[k];
final int off = k * nCol;
for(int j = 0; j < nCol; j++)
c[colIndexes.get(j)] += _values[off + j] * cntk;
}
}
@Override
public void colSumSq(double[] c, int[] counts, IColIndex colIndexes) {
final int nCol = colIndexes.size();
final int nRow = counts.length;
int off = 0;
for(int k = 0; k < nRow; k++) {
final int cntk = counts[k];
for(int j = 0; j < nCol; j++) {
final double v = _values[off++];
c[colIndexes.get(j)] += v * v * cntk;
}
}
}
@Override
public void colProduct(double[] res, int[] counts, IColIndex colIndexes) {
final int nCol = colIndexes.size();
for(int k = 0; k < counts.length; k++) {
final int cntk = counts[k];
final int off = k * nCol;
for(int j = 0; j < nCol; j++)
res[colIndexes.get(j)] *= Math.pow(_values[off + j], cntk);
}
correctNan(res, colIndexes);
}
@Override
public void colProductWithReference(double[] res, int[] counts, IColIndex colIndexes, double[] reference) {
final int nCol = colIndexes.size();
for(int k = 0; k < counts.length; k++) {
final int cntk = counts[k];
final int off = k * nCol;
for(int j = 0; j < nCol; j++)
res[colIndexes.get(j)] *= Math.pow(_values[off + j] + reference[j], cntk);
}
correctNan(res, colIndexes);
}
@Override
public void colSumSqWithReference(double[] c, int[] counts, IColIndex colIndexes, double[] reference) {
final int nCol = colIndexes.size();
final int nRow = counts.length;
int off = 0;
for(int k = 0; k < nRow; k++) {
final int cntk = counts[k];
for(int j = 0; j < nCol; j++) {
final double v = _values[off++] + reference[j];
c[colIndexes.get(j)] += v * v * cntk;
}
}
}
@Override
public double sum(int[] counts, int nCol) {
double out = 0;
int valOff = 0;
for(int k = 0; k < counts.length; k++) {
int countK = counts[k];
for(int j = 0; j < nCol; j++) {
out += _values[valOff++] * countK;
}
}
return out;
}
@Override
public double sumSq(int[] counts, int nCol) {
double out = 0;
int valOff = 0;
for(int k = 0; k < counts.length; k++) {
final int countK = counts[k];
for(int j = 0; j < nCol; j++) {
final double val = _values[valOff++];
out += val * val * countK;
}
}
return out;
}
@Override
public double sumSqWithReference(int[] counts, double[] reference) {
final int nCol = reference.length;
final int nRow = counts.length;
double out = 0;
int valOff = 0;
for(int k = 0; k < nRow; k++) {
final int countK = counts[k];
for(int j = 0; j < nCol; j++) {
final double val = _values[valOff++] + reference[j];
out += val * val * countK;
}
}
return out;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(_values.length * 3 + 10);
sb.append("Dictionary : ");
stringArray(sb, _values);
return sb.toString();
}
private static void stringArray(StringBuilder sb, double[] val) {
sb.append("[");
sb.append(doubleToString(val[0]));
for(int i = 1; i < val.length; i++) {
sb.append(", ");
sb.append(doubleToString(val[i]));
}
sb.append("]");
}
public String getString(int colIndexes) {
StringBuilder sb = new StringBuilder();
if(colIndexes == 1)
stringArray(sb, _values);
else {
sb.append("[\n\t");
for(int i = 0; i < _values.length - 1; i++) {
sb.append(doubleToString(_values[i]));
sb.append((i) % (colIndexes) == colIndexes - 1 ? "\n\t" : ", ");
}
sb.append(doubleToString(_values[_values.length - 1]));
sb.append("]");
}
return sb.toString();
}
public ADictionary sliceOutColumnRange(int idxStart, int idxEnd, int previousNumberOfColumns) {
int numberTuples = getNumberOfValues(previousNumberOfColumns);
int tupleLengthAfter = idxEnd - idxStart;
double[] newDictValues = new double[tupleLengthAfter * numberTuples];
int orgOffset = idxStart;
int targetOffset = 0;
for(int v = 0; v < numberTuples; v++) {
for(int c = 0; c < tupleLengthAfter; c++, orgOffset++, targetOffset++) {
newDictValues[targetOffset] = _values[orgOffset];
}
orgOffset += previousNumberOfColumns - idxEnd + idxStart;
}
return create(newDictValues);
}
@Override
public boolean containsValue(double pattern) {
boolean NaNpattern = Double.isNaN(pattern);
if(NaNpattern) {
for(double v : _values)
if(Double.isNaN(v))
return true;
}
else {
for(double v : _values)
if(v == pattern)
return true;
}
return false;
}
@Override
public boolean containsValueWithReference(double pattern, double[] reference) {
final int nCol = reference.length;
for(int i = 0; i < _values.length; i++)
if(_values[i] + reference[i % nCol] == pattern)
return true;
return false;
}
@Override
public long getNumberNonZeros(int[] counts, int nCol) {
long nnz = 0;
final int nRow = counts.length;
for(int i = 0; i < nRow; i++) {
long rowCount = 0;
final int off = i * nCol;
for(int j = off; j < off + nCol; j++) {
if(_values[j] != 0)
rowCount++;
}
nnz += rowCount * counts[i];
}
return nnz;
}
@Override
public long getNumberNonZerosWithReference(int[] counts, double[] reference, int nRows) {
long nnz = 0;
final int nCol = reference.length;
final int nRow = counts.length;
for(int i = 0; i < nRow; i++) {
long rowCount = 0;
final int off = i * nCol;
for(int j = off, jj = 0; j < off + nCol; j++, jj++) {
if(_values[j] + reference[jj] != 0)
rowCount++;
}
nnz += rowCount * counts[i];
}
return nnz;
}
@Override
public final void addToEntry(double[] v, int fr, int to, int nCol) {
final int sf = fr * nCol; // start from
final int st = to * nCol; // start to
addToOffsets(v, sf, st, nCol);
}
private final void addToOffsets(final double[] v, final int sf, final int st, final int nCol) {
for(int i = sf, j = st; i < sf + nCol; i++, j++)
v[j] += _values[i];
}
@Override
public final void addToEntry(double[] v, int fr, int to, int nCol, int rep) {
final int sf = fr * nCol; // start from
final int st = to * nCol; // start to
addToOffsets(v, sf, st, nCol, rep);
}
private final void addToOffsets(double[] v, int sf, int st, int nCol, int rep) {
for(int i = sf, j = st; i < sf + nCol; i++, j++)
v[j] += _values[i] * rep;
}
@Override
public void addToEntryVectorized(double[] v, int f1, int f2, int f3, int f4, int f5, int f6, int f7, int f8, int t1,
int t2, int t3, int t4, int t5, int t6, int t7, int t8, int nCol) {
addToOffsets(v, f1 * nCol, t1 * nCol, nCol);
addToOffsets(v, f2 * nCol, t2 * nCol, nCol);
addToOffsets(v, f3 * nCol, t3 * nCol, nCol);
addToOffsets(v, f4 * nCol, t4 * nCol, nCol);
addToOffsets(v, f5 * nCol, t5 * nCol, nCol);
addToOffsets(v, f6 * nCol, t6 * nCol, nCol);
addToOffsets(v, f7 * nCol, t7 * nCol, nCol);
addToOffsets(v, f8 * nCol, t8 * nCol, nCol);
}
@Override
public DictType getDictType() {
return DictType.Dict;
}
@Override
public ADictionary subtractTuple(double[] tuple) {
double[] newValues = new double[_values.length];
for(int i = 0; i < _values.length;)
for(int j = 0; j < tuple.length; i++, j++)
newValues[i] = _values[i] - tuple[j];
return create(newValues);
}
@Override
public MatrixBlockDictionary getMBDict(int nCol) {
return MatrixBlockDictionary.createDictionary(_values, nCol, true);
}
@Override
public void aggregateCols(double[] c, Builtin fn, IColIndex colIndexes) {
final int nCol = colIndexes.size();
final int rlen = _values.length / nCol;
for(int k = 0; k < rlen; k++)
for(int j = 0, valOff = k * nCol; j < nCol; j++)
c[colIndexes.get(j)] = fn.execute(c[colIndexes.get(j)], _values[valOff + j]);
}
@Override
public void aggregateColsWithReference(double[] c, Builtin fn, IColIndex colIndexes, double[] reference,
boolean def) {
final int nCol = reference.length;
final int rlen = _values.length / nCol;
for(int k = 0; k < rlen; k++)
for(int j = 0, valOff = k * nCol; j < nCol; j++)
c[colIndexes.get(j)] = fn.execute(c[colIndexes.get(j)], _values[valOff + j] + reference[j]);
if(def)
for(int i = 0; i < nCol; i++) {
final int cix = colIndexes.get(i);
c[cix] = fn.execute(c[cix], reference[i]);
}
}
@Override
public ADictionary scaleTuples(int[] scaling, int nCol) {
final double[] scaledValues = new double[_values.length];
int off = 0;
for(int tuple = 0; tuple < _values.length / nCol; tuple++) {
final int scale = scaling[tuple];
for(int v = 0; v < nCol; v++) {
scaledValues[off] = _values[off] * scale;
off++;
}
}
return create(scaledValues);
}
@Override
public Dictionary preaggValuesFromDense(int numVals, IColIndex colIndexes, IColIndex aggregateColumns, double[] b,
int cut) {
double[] ret = new double[numVals * aggregateColumns.size()];
for(int k = 0, off = 0; k < numVals * colIndexes.size(); k += colIndexes.size(), off += aggregateColumns.size()) {
for(int h = 0; h < colIndexes.size(); h++) {
int idb = colIndexes.get(h) * cut;
double v = _values[k + h];
if(v != 0)
for(int i = 0; i < aggregateColumns.size(); i++)
ret[off + i] += v * b[idb + aggregateColumns.get(i)];
}
}
return create(ret);
}
@Override
public ADictionary replace(double pattern, double replace, int nCol) {
double[] retV = new double[_values.length];
for(int i = 0; i < _values.length; i++) {
final double v = _values[i];
retV[i] = v == pattern ? replace : v;
}
return create(retV);
}
@Override
public ADictionary replaceWithReference(double pattern, double replace, double[] reference) {
final double[] retV = new double[_values.length];
final int nCol = reference.length;
final int nRow = _values.length / nCol;
int off = 0;
for(int i = 0; i < nRow; i++) {
for(int j = 0; j < nCol; j++) {
final double ref = reference[j];
final double v = _values[off];
retV[off] = Math.abs(v + ref - pattern) < 0.000001 ? replace - ref : v;
off++;
}
}
return create(retV);
}
@Override
public void product(double[] ret, int[] counts, int nCol) {
if(ret[0] == 0)
return;
final MathContext cont = MathContext.DECIMAL128;
final int len = counts.length;
BigDecimal tmp = BigDecimal.ONE;
for(int i = 0; i < len; i++) {
for(int j = i * nCol; j < (i + 1) * nCol; j++) {
double v = _values[j];
if(v == 0) {
ret[0] = 0;
return;
}
tmp = tmp.multiply(new BigDecimal(v).pow(counts[i], cont), cont);
}
}
if(Math.abs(tmp.doubleValue()) == 0)
ret[0] = 0;
else if(!Double.isInfinite(ret[0]))
ret[0] = new BigDecimal(ret[0]).multiply(tmp, MathContext.DECIMAL128).doubleValue();
}
@Override
public void productWithDefault(double[] ret, int[] counts, double[] def, int defCount) {
if(ret[0] == 0)
return;
final MathContext cont = MathContext.DECIMAL128;
final int len = counts.length;
final int nCol = def.length;
BigDecimal tmp = BigDecimal.ONE;
for(int i = 0; i < len; i++) {
for(int j = i * nCol; j < (i + 1) * nCol; j++) {
double v = _values[j];
if(v == 0) {
ret[0] = 0;
return;
}
tmp = tmp.multiply(new BigDecimal(v).pow(counts[i], cont), cont);
}
}
for(int x = 0; x < def.length; x++)
tmp = tmp.multiply(new BigDecimal(def[x]).pow(defCount, cont), cont);
if(Math.abs(tmp.doubleValue()) == 0)
ret[0] = 0;
else if(!Double.isInfinite(ret[0]))
ret[0] = new BigDecimal(ret[0]).multiply(tmp, MathContext.DECIMAL128).doubleValue();
}
@Override
public void productWithReference(double[] ret, int[] counts, double[] reference, int refCount) {
if(ret[0] == 0)
return;
final MathContext cont = MathContext.DECIMAL128;
final int len = counts.length;
final int nCol = reference.length;
BigDecimal tmp = BigDecimal.ONE;
int off = 0;
for(int i = 0; i < len; i++) {
for(int j = 0; j < nCol; j++) {
final double v = _values[off++] + reference[j];
if(v == 0) {
ret[0] = 0;
return;
}
tmp = tmp.multiply(new BigDecimal(v).pow(counts[i], cont), cont);
}
}
for(int x = 0; x < reference.length; x++)
tmp = tmp.multiply(new BigDecimal(reference[x]).pow(refCount, cont), cont);
if(Math.abs(tmp.doubleValue()) == 0)
ret[0] = 0;
else if(!Double.isInfinite(ret[0]))
ret[0] = new BigDecimal(ret[0]).multiply(tmp, MathContext.DECIMAL128).doubleValue();
}
@Override
public CM_COV_Object centralMoment(CM_COV_Object ret, ValueFunction fn, int[] counts, int nRows) {
// should be guaranteed to only contain one value per tuple in dictionary.
for(int i = 0; i < _values.length; i++)
fn.execute(ret, _values[i], counts[i]);
if(ret.getWeight() < nRows)
fn.execute(ret, 0, nRows - ret.getWeight());
return ret;
}
@Override
public CM_COV_Object centralMomentWithDefault(CM_COV_Object ret, ValueFunction fn, int[] counts, double def,
int nRows) {
// should be guaranteed to only contain one value per tuple in dictionary.
for(int i = 0; i < _values.length; i++)
fn.execute(ret, _values[i], counts[i]);
if(ret.getWeight() < nRows)
fn.execute(ret, def, nRows - ret.getWeight());
return ret;
}
@Override
public CM_COV_Object centralMomentWithReference(CM_COV_Object ret, ValueFunction fn, int[] counts, double reference,
int nRows) {
// should be guaranteed to only contain one value per tuple in dictionary.
for(int i = 0; i < _values.length; i++)
fn.execute(ret, _values[i] + reference, counts[i]);
if(ret.getWeight() < nRows)
fn.execute(ret, reference, nRows - ret.getWeight());
return ret;
}
@Override
public ADictionary rexpandCols(int max, boolean ignore, boolean cast, int nCol) {
if(nCol > 1)
throw new DMLCompressionException("Invalid to rexpand the column groups if more than one column");
MatrixBlockDictionary m = getMBDict(nCol);
return m == null ? null : m.rexpandCols(max, ignore, cast, nCol);
}
@Override
public ADictionary rexpandColsWithReference(int max, boolean ignore, boolean cast, int reference) {
MatrixBlockDictionary m = getMBDict(1);
if(m == null)
return null;
ADictionary a = m.applyScalarOp(new LeftScalarOperator(Plus.getPlusFnObject(), reference));
return a == null ? null : a.rexpandCols(max, ignore, cast, 1);
}
@Override
public double getSparsity() {
int zeros = 0;
for(double v : _values)
if(v == 0.0)
zeros++;
return OptimizerUtils.getSparsity(_values.length, 1L, _values.length - zeros);
}
@Override
public void multiplyScalar(double v, double[] ret, int off, int dictIdx, IColIndex cols) {
final int offD = dictIdx * cols.size();
for(int i = 0; i < cols.size(); i++) {
double a = v * _values[offD + i];
ret[off + cols.get(i)] += a;
}
}
@Override
protected void TSMMWithScaling(int[] counts, IColIndex rows, IColIndex cols, MatrixBlock ret) {
DictLibMatrixMult.TSMMDictsDenseWithScaling(_values, rows, cols, counts, ret);
}
@Override
protected void MMDict(ADictionary right, IColIndex rowsLeft, IColIndex colsRight, MatrixBlock result) {
right.MMDictDense(_values, rowsLeft, colsRight, result);
}
@Override
protected void MMDictDense(double[] left, IColIndex rowsLeft, IColIndex colsRight, MatrixBlock result) {
DictLibMatrixMult.MMDictsDenseDense(left, _values, rowsLeft, colsRight, result);
}
@Override
protected void MMDictSparse(SparseBlock left, IColIndex rowsLeft, IColIndex colsRight, MatrixBlock result) {
DictLibMatrixMult.MMDictsSparseDense(left, _values, rowsLeft, colsRight, result);
}
@Override
protected void TSMMToUpperTriangle(ADictionary right, IColIndex rowsLeft, IColIndex colsRight, MatrixBlock result) {
right.TSMMToUpperTriangleDense(_values, rowsLeft, colsRight, result);
}
@Override
protected void TSMMToUpperTriangleDense(double[] left, IColIndex rowsLeft, IColIndex colsRight, MatrixBlock result) {
DictLibMatrixMult.MMToUpperTriangleDenseDense(left, _values, rowsLeft, colsRight, result);
}
@Override
protected void TSMMToUpperTriangleSparse(SparseBlock left, IColIndex rowsLeft, IColIndex colsRight,
MatrixBlock result) {
DictLibMatrixMult.MMToUpperTriangleSparseDense(left, _values, rowsLeft, colsRight, result);
}
@Override
protected void TSMMToUpperTriangleScaling(ADictionary right, IColIndex rowsLeft, IColIndex colsRight, int[] scale,
MatrixBlock result) {
right.TSMMToUpperTriangleDenseScaling(_values, rowsLeft, colsRight, scale, result);
}
@Override
protected void TSMMToUpperTriangleDenseScaling(double[] left, IColIndex rowsLeft, IColIndex colsRight, int[] scale,
MatrixBlock result) {
DictLibMatrixMult.TSMMToUpperTriangleDenseDenseScaling(left, _values, rowsLeft, colsRight, scale, result);
}
@Override
protected void TSMMToUpperTriangleSparseScaling(SparseBlock left, IColIndex rowsLeft, IColIndex colsRight,
int[] scale, MatrixBlock result) {
DictLibMatrixMult.TSMMToUpperTriangleSparseDenseScaling(left, _values, rowsLeft, colsRight, scale, result);
}
@Override
public boolean equals(ADictionary o) {
if(o instanceof Dictionary)
return Arrays.equals(_values, ((Dictionary) o)._values);
else if(o instanceof MatrixBlockDictionary) {
final MatrixBlock mb = ((MatrixBlockDictionary) o).getMatrixBlock();
if(mb.isInSparseFormat())
return mb.getSparseBlock().equals(_values, mb.getNumColumns());
final double[] dv = mb.getDenseBlockValues();
return Arrays.equals(_values, dv);
}
return false;
}
@Override
public ADictionary cbind(ADictionary that, int nCol){
int nRowThat = that.getNumberOfValues(nCol);
int nColThis = _values.length / nRowThat;
MatrixBlockDictionary mbd = getMBDict(nColThis);
return mbd.cbind(that, nCol);
}
@Override
public ADictionary reorder(int[] reorder){
double[] retV = new double[_values.length];
Dictionary ret = new Dictionary(retV);
int nRows = _values.length / reorder.length;
for(int r = 0; r < nRows; r++){
int off = r * reorder.length;
for(int c = 0; c < reorder.length; c++)
retV[off + c] = _values[off + reorder[c]];
}
return ret;
}
}