package com.linkedin.thirdeye.client.diffsummary;
public class CostFunction {
public static double err(double v1, double v2, double parentRatio) {
double expectedValue = parentRatio * v1;
return (v2 - expectedValue) * Math.log(v2 / expectedValue);
}
/**
* Auto fill in v1 and v2 using parentRatio when one of them is zero.
* If v1 and v2 both are zero or parentRatio is not finite, this function returns 0.
*/
public static double err4EmptyValues(double v1, double v2, double parentRatio) {
if (Double.compare(0., v1) != 0 && Double.compare(0., v2) != 0) {
return CostFunction.err(v1, v2, parentRatio);
} else if (Double.compare(v1, 0d) == 0 || Double.compare(v2, 0d) == 0) {
double filledInRatio = Math.abs(v1 - v2);
if (Double.compare(filledInRatio, Math.E) < 0) {
filledInRatio = 1d;
} else {
filledInRatio = Math.log(filledInRatio);
}
if (Double.compare(0., v1) == 0) {
return CostFunction.err(v2 / Math.max(filledInRatio, parentRatio + (1/filledInRatio)), v2, parentRatio);
} else {
filledInRatio = 1d / filledInRatio; // because Double.compare(v1, v2) > 0
return CostFunction.err(v1, v1 * Math.min(1/filledInRatio, parentRatio + filledInRatio), parentRatio);
}
} else { // v1 and v2 are zeros. Set cost to zero so the node will be naturally aggregated to its parent.
return 0.;
}
}
public static double errWithPercentageRemoval(double v1, double v2, double parentRatio,
double threshold, double currentBaselineSum) {
double percentageContribution = (((v1 + v2) / currentBaselineSum) * 100);
if (Double.compare(percentageContribution, threshold) < 0) {
return 0d;
} else {
return err4EmptyValues(v1, v2, parentRatio) * Math.log(percentageContribution);
}
}
}