de.bund.bfr.knime.pmm.common/src/de/bund/bfr/knime/pmm/common/math/MathUtilities.java
/*******************************************************************************
* Copyright (c) 2015 Federal Institute for Risk Assessment (BfR), Germany
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Contributors:
* Department Biological Safety - BfR
*******************************************************************************/
package de.bund.bfr.knime.pmm.common.math;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.math3.distribution.TDistribution;
import org.lsmp.djep.djep.DJep;
import org.lsmp.djep.djep.DiffRulesI;
import org.lsmp.djep.djep.diffRules.MacroDiffRules;
import org.lsmp.djep.xjep.MacroFunction;
import org.nfunk.jep.ASTConstant;
import org.nfunk.jep.ASTFunNode;
import org.nfunk.jep.ASTVarNode;
import org.nfunk.jep.Node;
import org.nfunk.jep.ParseException;
public class MathUtilities {
public static double EPSILON = 0.00001;
private static Random random = null;
private MathUtilities() {
}
public static String nodeToString(Node n) throws Exception {
if (n instanceof ASTFunNode) {
String s = n.toString() + "(";
for (int i = 0; i < n.jjtGetNumChildren(); i++) {
if (i != 0) {
s += ",";
}
s += nodeToString(n.jjtGetChild(i));
}
return s + ")";
} else if (n instanceof ASTConstant) {
return n.toString();
} else if (n instanceof ASTVarNode) {
return n.toString();
} else {
throw new Exception("Unknown Node");
}
}
public static void removeNullValues(List<Double> targetValues, List<List<Double>> argumentValues) {
for (int i = 0; i < targetValues.size(); i++) {
boolean remove = false;
if (targetValues.get(i) == null) {
remove = true;
continue;
}
if (!remove) {
for (int j = 0; j < argumentValues.size(); j++) {
if (argumentValues.get(j).get(i) == null) {
remove = true;
break;
}
}
}
if (remove) {
targetValues.remove(i);
for (int j = 0; j < argumentValues.size(); j++) {
argumentValues.get(j).remove(i);
}
i--;
}
}
}
public static Random getRandomGenerator() {
if (random == null) {
random = new Random();
}
return random;
}
public static int getRandomNegativeInt() {
if (random == null)
random = new Random();
int value = random.nextInt();
if (value > 0)
value = -value;
return value;
}
public static double computeSum(List<Double> values) {
double sum = 0.0;
for (double v : values) {
sum += v;
}
return sum;
}
public static DJep createParser() {
DJep parser = new DJep();
parser.setAllowAssignment(true);
parser.setAllowUndeclared(true);
parser.setImplicitMul(true);
parser.addStandardFunctions();
parser.addStandardDiffRules();
parser.removeVariable("x");
try {
parser.addFunction("log10", new MacroFunction("log10", 1, "ln(x)/ln(10)", parser));
parser.addDiffRule(new MacroDiffRules(parser, "log10", "1/(x*ln(10))"));
parser.addDiffRule(new ZeroDiffRule("<"));
parser.addDiffRule(new ZeroDiffRule(">"));
parser.addDiffRule(new ZeroDiffRule("<="));
parser.addDiffRule(new ZeroDiffRule(">="));
parser.addDiffRule(new ZeroDiffRule("&&"));
parser.addDiffRule(new ZeroDiffRule("||"));
parser.addDiffRule(new ZeroDiffRule("=="));
parser.addDiffRule(new ZeroDiffRule("!="));
} catch (ParseException e) {
e.printStackTrace();
}
return parser;
}
public static String replaceVariable(String formula, String var, String newVar) {
if (var.equals(newVar)) {
return formula;
}
String newFormular = " " + formula + " ";
for (int i = 1; i < newFormular.length() - var.length(); i++) {
boolean matches = newFormular.substring(i, i + var.length()).equals(var);
boolean start = !isVariableCharacter(newFormular.charAt(i - 1));
boolean end = !isVariableCharacter(newFormular.charAt(i + var.length()));
if (matches && start && end) {
String prefix = newFormular.substring(0, i);
String postfix = newFormular.substring(i + var.length());
newFormular = prefix + newVar + postfix;
i = prefix.length() + newVar.length();
}
}
return newFormular.replace(" ", "");
}
public static boolean isFunctionDefinedFor(String formula, List<String> parameters, List<Double> parameterValues,
String variable, double minValue, double maxValue, int steps) {
DJep parser = createParser();
Node function = null;
parser.addVariable(variable, 0.0);
for (int i = 0; i < parameters.size(); i++) {
parser.addConstant(parameters.get(i), parameterValues.get(i));
}
try {
function = parser.parse(formula.substring(formula.indexOf("=") + 1));
for (int i = 0; i < steps; i++) {
double value = minValue + (double) i / (double) (steps - 1) * (maxValue - minValue);
parser.setVarValue(variable, value);
if (!(parser.evaluate(function) instanceof Double)) {
return false;
}
}
} catch (ParseException e) {
e.printStackTrace();
}
return true;
}
public static List<Double> evaluateFunction(String formula, List<String> parameters, List<Double> parameterValues,
List<String> variables, List<List<Double>> variableValues) {
List<Double> values = new ArrayList<>();
DJep parser = createParser();
for (int i = 0; i < parameters.size(); i++) {
parser.addConstant(parameters.get(i), parameterValues.get(i));
}
for (int i = 0; i < variables.size(); i++) {
parser.addVariable(variables.get(i), 0.0);
}
try {
Node function = parser.parse(formula.substring(formula.indexOf("=") + 1));
for (int i = 0; i < variableValues.get(0).size(); i++) {
for (int j = 0; j < variables.size(); j++) {
parser.setVarValue(variables.get(j), variableValues.get(j).get(i));
}
values.add((Double) parser.evaluate(function));
}
} catch (ParseException e) {
e.printStackTrace();
}
return values;
}
private static class ZeroDiffRule implements DiffRulesI {
private String name;
public ZeroDiffRule(String name) {
this.name = name;
}
@Override
public Node differentiate(ASTFunNode node, String var, Node[] children, Node[] dchildren, DJep djep)
throws ParseException {
return djep.getNodeFactory().buildConstantNode(0.0);
}
@Override
public String getName() {
return name;
}
}
public static Double getMSE(Double rmse) {
if (rmse == null) {
return null;
}
return rmse * rmse;
}
public static Double getRMSE(double sse, double numParam, double numSample) {
if (numParam >= numSample) {
return null;
}
return Math.sqrt(sse / (numSample - numParam));
}
public static Double getRMSE(double sse, double numSample) {
return Math.sqrt(sse / numSample);
}
public static Double getRSquared(double sse, List<Double> targetValues) {
double targetMean = MathUtilities.computeSum(targetValues) / targetValues.size();
double targetTotalSumOfSquares = 0.0;
for (int i = 0; i < targetValues.size(); i++) {
targetTotalSumOfSquares += Math.pow(targetValues.get(i) - targetMean, 2.0);
}
double rSquared = 1 - sse / targetTotalSumOfSquares;
// rSquare < 0 möglich, siehe hier:
// http://mars.wiwi.hu-berlin.de/mediawiki/sk/index.php/Bestimmtheitsmass
return Math.max(rSquared, 0.0);
}
public static Double akaikeCriterion(final int numParam, final int numSample, final double sse) {
if (numSample <= numParam + 2) {
return null;
}
return numSample * Math.log(sse / numSample) + 2 * (numParam + 1)
+ 2 * (numParam + 1) * (numParam + 2) / (numSample - numParam - 2);
}
public static double getPValue(double tValue, int degreesOfFreedom) {
TDistribution dist = new TDistribution(degreesOfFreedom);
return 1.0 - dist.probability(-Math.abs(tValue), Math.abs(tValue));
}
public static boolean isVariableCharacter(char ch) {
return Character.isLetterOrDigit(ch) || ch == '_' || ch == '$';
}
public static int generateID(int seed) {
Random rand = new Random(seed);
int id = rand.nextInt();
if (id > 0) {
id = -id;
}
return id;
}
public static String getBoundaryCondition(String formula) {
Pattern p = Pattern.compile("\\*\\(\\(\\(\\(\\(.+\\)\\)\\)\\)\\)$");
Matcher m = p.matcher(formula);
while (m.find()) {
String s = m.group();
s = s.substring(6, s.length() - 5);
if (countOccurences(s, '(') == countOccurences(s, ')')) {
return s;
}
}
return null;
}
public static String getAllButBoundaryCondition(String formula) {
String cond = getBoundaryCondition(formula);
if (cond != null) {
String f = formula.replace("*(((((" + cond + ")))))", "");
int i = f.indexOf("=");
if (f.charAt(i + 1) == '(' && f.endsWith(")")) {
String withoutBrackets = f.substring(i + 2, f.length() - 1);
try {
createParser().parse(withoutBrackets);
f = f.substring(0, i + 1) + withoutBrackets;
} catch (ParseException e) {
// Do not remove brackets when expression without brackets
// cannot be parsed
}
}
return f;
} else {
return formula;
}
}
public static String getFormula(String formula, String boundaryCondition) {
if (boundaryCondition != null && !boundaryCondition.isEmpty()) {
int i = formula.indexOf("=");
return formula.substring(0, i + 1) + "(" + formula.substring(i + 1) + ")*(((((" + boundaryCondition
+ ")))))";
} else {
return formula;
}
}
public static boolean isValid(Object value) {
return value instanceof Double && !((Double) value).isNaN() && !((Double) value).isInfinite();
}
private static int countOccurences(String s, char c) {
int n = 0;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == c) {
n++;
}
}
return n;
}
}