DoubleEvaluator.java :  » Algebra » symja » org » matheclipse » parser » client » eval » Java Open Source

Java Open Source » Algebra » symja 
symja » org » matheclipse » parser » client » eval » DoubleEvaluator.java
/*
 * Copyright 2005-2008 Axel Kramer (axelclk@gmail.com)
 *
 * Licensed 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.matheclipse.parser.client.eval;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.matheclipse.parser.client.Parser;
import org.matheclipse.parser.client.SyntaxError;
import org.matheclipse.parser.client.ast.ASTNode;
import org.matheclipse.parser.client.ast.FunctionNode;
import org.matheclipse.parser.client.ast.NumberNode;
import org.matheclipse.parser.client.ast.SymbolNode;

/**
 * Evaluate math expressions to <code>double</code> numbers.
 * 
 */
public class DoubleEvaluator {
  private static final boolean DEBUG = false;

  public static double EPSILON = 1.0e-15;

  private static Map<String, Double> SYMBOL_DOUBLE_MAP;

  private static Map<String, Boolean> SYMBOL_BOOLEAN_MAP;

  private static Map<String, Object> FUNCTION_DOUBLE_MAP;

  private static Map<String, Object> FUNCTION_BOOLEAN_MAP;

  private IDoubleCallbackFunction fCallbackFunction = null;

  /**
   * A callback function for unknown function names.
   * 
   * @return the callback function
   */
  public IDoubleCallbackFunction getCallbackFunction() {
    return fCallbackFunction;
  }

  /**
   * Set a callback function for unknown function names.
   * 
   * @param callbackFunction
   *          the callback function to set
   */
  public void setCallbackFunction(IDoubleCallbackFunction callbackFunction) {
    fCallbackFunction = callbackFunction;
  }

  static class ArcTanFunction implements IDouble1Function, IDouble2Function {
    public double evaluate(double arg1) {
      return Math.atan(arg1);
    }

    public double evaluate(double arg1, double arg2) {
      return Math.atan2(arg1, arg2);
    }
  }

  static class LogFunction implements IDouble1Function, IDouble2Function {
    public double evaluate(double arg1) {
      return Math.log(arg1);
    }

    public double evaluate(double base, double z) {
      return Math.log(z) / Math.log(base);
    }
  }

  static class CompoundExpressionFunction implements IDoubleFunction {
    public double evaluate(DoubleEvaluator engine, FunctionNode function) {
      double result = Double.NaN;
      int end = function.size();
      for (int i = 1; i < end; i++) {
        result = engine.evaluateNode(function.getNode(i));
        if (DEBUG) {
          System.out.println(result);
        }
      }
      return result;
    }
  }

  static class SetFunction implements IDoubleFunction {
    public double evaluate(DoubleEvaluator engine, FunctionNode function) {
      if (function.size() != 3) {
        throw new ArithmeticException(
            "SetFunction#evaluate(DoubleEvaluator,FunctionNode) needs 2 arguments: "
                + function.toString());
      }
      if (!(function.getNode(1) instanceof SymbolNode)) {
        throw new ArithmeticException(
            "SetFunction#evaluate(DoubleEvaluator,FunctionNode) symbol required on the left hand side: "
                + function.toString());
      }
      String variableName = ((SymbolNode) function.getNode(1)).getString();
      double result = engine.evaluateNode(function.getNode(2));
      IDoubleValue dv = engine.getVariable(variableName);
      if (dv == null) {
        dv = new DoubleVariable(result);
      } else {
        dv.setValue(result);
      }
      engine.defineVariable(variableName, dv);
      return result;
    }
  }

  static class MaxFunction implements IDoubleFunction, IDouble2Function {
    public double evaluate(double arg1, double arg2) {
      return Math.max(arg1, arg2);
    }

    public double evaluate(DoubleEvaluator engine, FunctionNode function) {
      double result = Double.MIN_VALUE;
      double temp;
      int end = function.size();
      for (int i = 1; i < end; i++) {
        temp = Math.max(result, engine.evaluateNode(function
            .getNode(i)));
        if (temp > result) {
          result = temp;
        }
      }
      return result;
    }
  }

  static class MinFunction implements IDoubleFunction, IDouble2Function {
    public double evaluate(double arg1, double arg2) {
      return Math.min(arg1, arg2);
    }

    public double evaluate(DoubleEvaluator engine, FunctionNode function) {
      double result = Double.MAX_VALUE;
      double temp;
      int end = function.size();
      for (int i = 1; i < end; i++) {
        temp = Math.min(result, engine.evaluateNode(function
            .getNode(i)));
        if (temp < result) {
          result = temp;
        }
      }
      return result;
    }
  }

  static class PlusFunction implements IDoubleFunction, IDouble2Function {
    public double evaluate(double arg1, double arg2) {
      return arg1 + arg2;
    }

    public double evaluate(DoubleEvaluator engine, FunctionNode function) {
      double result = 0.0;
      for (int i = 1; i < function.size(); i++) {
        result += engine.evaluateNode(function.getNode(i));
      }
      return result;
    }
  }

  static class TimesFunction implements IDoubleFunction, IDouble2Function {
    public double evaluate(double arg1, double arg2) {
      return arg1 * arg2;
    }

    public double evaluate(DoubleEvaluator engine, FunctionNode function) {
      double result = 1.0;
      for (int i = 1; i < function.size(); i++) {
        result *= engine.evaluateNode(function.getNode(i));
      }
      return result;
    }
  }

  static {
    SYMBOL_DOUBLE_MAP = new HashMap<String, Double>();
    SYMBOL_DOUBLE_MAP.put("Degree", new Double(Math.PI / 180));
    SYMBOL_DOUBLE_MAP.put("E", new Double(Math.E));
    SYMBOL_DOUBLE_MAP.put("Pi", new Double(Math.PI));

    SYMBOL_BOOLEAN_MAP = new HashMap<String, Boolean>();
    SYMBOL_BOOLEAN_MAP.put("False", Boolean.FALSE);
    SYMBOL_BOOLEAN_MAP.put("True", Boolean.TRUE);

    FUNCTION_BOOLEAN_MAP = new HashMap<String, Object>();

    FUNCTION_BOOLEAN_MAP.put("And", new IBooleanBoolean2Function() {
      public boolean evaluate(boolean arg1, boolean arg2) {
        return arg1 && arg2;
      }
    });
    FUNCTION_BOOLEAN_MAP.put("Not", new IBooleanBoolean1Function() {
      public boolean evaluate(boolean arg1) {
        return !arg1;
      }
    });
    FUNCTION_BOOLEAN_MAP.put("Or", new IBooleanBoolean2Function() {
      public boolean evaluate(boolean arg1, boolean arg2) {
        return arg1 || arg2;
      }
    });

    FUNCTION_BOOLEAN_MAP.put("Equal", new IBooleanDouble2Function() {
      public boolean evaluate(double arg1, double arg2) {
        return Math.abs(arg1 - arg2) < EPSILON;
      }
    });
    FUNCTION_BOOLEAN_MAP.put("Greater", new IBooleanDouble2Function() {
      public boolean evaluate(double arg1, double arg2) {
        return arg1 > arg2;
      }
    });
    FUNCTION_BOOLEAN_MAP.put("GreaterEqual", new IBooleanDouble2Function() {
      public boolean evaluate(double arg1, double arg2) {
        return arg1 >= arg2;
      }
    });
    FUNCTION_BOOLEAN_MAP.put("Less", new IBooleanDouble2Function() {
      public boolean evaluate(double arg1, double arg2) {
        return arg1 < arg2;
      }
    });
    FUNCTION_BOOLEAN_MAP.put("LessEqual", new IBooleanDouble2Function() {
      public boolean evaluate(double arg1, double arg2) {
        return arg1 <= arg2;
      }
    });
    FUNCTION_BOOLEAN_MAP.put("Unequal", new IBooleanDouble2Function() {
      public boolean evaluate(double arg1, double arg2) {
        return !(Math.abs(arg1 - arg2) < EPSILON);
      }
    });

    FUNCTION_DOUBLE_MAP = new HashMap<String, Object>();
    FUNCTION_DOUBLE_MAP.put("ArcTan", new ArcTanFunction());
    FUNCTION_DOUBLE_MAP.put("CompoundExpression",
        new CompoundExpressionFunction());
    FUNCTION_DOUBLE_MAP.put("Set", new SetFunction());
    FUNCTION_DOUBLE_MAP.put("Log", new LogFunction());
    FUNCTION_DOUBLE_MAP.put("Max", new MaxFunction());
    FUNCTION_DOUBLE_MAP.put("Min", new MinFunction());
    FUNCTION_DOUBLE_MAP.put("Plus", new PlusFunction());
    FUNCTION_DOUBLE_MAP.put("Times", new TimesFunction());
    //
    // Functions with 0 argument
    //
    FUNCTION_DOUBLE_MAP.put("Random", new IDouble0Function() {
      public double evaluate() {
        return Math.random();
      }
    });
    //
    // Functions with 1 argument
    //
    FUNCTION_DOUBLE_MAP.put("ArcCos", new IDouble1Function() {
      public double evaluate(double arg1) {
        return Math.acos(arg1);
      }
    });
    FUNCTION_DOUBLE_MAP.put("ArcSin", new IDouble1Function() {
      public double evaluate(double arg1) {
        return Math.asin(arg1);
      }
    });
    FUNCTION_DOUBLE_MAP.put("Ceiling", new IDouble1Function() {
      public double evaluate(double arg1) {
        return Math.ceil(arg1);
      }
    });
    FUNCTION_DOUBLE_MAP.put("Cos", new IDouble1Function() {
      public double evaluate(double arg1) {
        return Math.cos(arg1);
      }
    });
    FUNCTION_DOUBLE_MAP.put("Cosh", new IDouble1Function() {
      public double evaluate(double arg1) {
        return Math.cosh(arg1);
      }
    });
    FUNCTION_DOUBLE_MAP.put("Exp", new IDouble1Function() {
      public double evaluate(double arg1) {
        return Math.exp(arg1);
      }
    });
    FUNCTION_DOUBLE_MAP.put("Floor", new IDouble1Function() {
      public double evaluate(double arg1) {
        return Math.floor(arg1);
      }
    });
    FUNCTION_DOUBLE_MAP.put("Round", new IDouble1Function() {
      public double evaluate(double arg1) {
        return Math.round(arg1);
      }
    });
    FUNCTION_DOUBLE_MAP.put("Sign", new IDouble1Function() {
      public double evaluate(double arg1) {
        return Math.signum(arg1);
      }
    });
    FUNCTION_DOUBLE_MAP.put("Sin", new IDouble1Function() {
      public double evaluate(double arg1) {
        return Math.sin(arg1);
      }
    });
    FUNCTION_DOUBLE_MAP.put("Sinh", new IDouble1Function() {
      public double evaluate(double arg1) {
        return Math.sinh(arg1);
      }
    });
    FUNCTION_DOUBLE_MAP.put("Sqrt", new IDouble1Function() {
      public double evaluate(double arg1) {
        return Math.sqrt(arg1);
      }
    });
    FUNCTION_DOUBLE_MAP.put("Tan", new IDouble1Function() {
      public double evaluate(double arg1) {
        return Math.tan(arg1);
      }
    });
    FUNCTION_DOUBLE_MAP.put("Tanh", new IDouble1Function() {
      public double evaluate(double arg1) {
        return Math.tanh(arg1);
      }
    });

    //
    // Functions with 2 arguments
    //
    FUNCTION_DOUBLE_MAP.put("Power", new IDouble2Function() {
      public double evaluate(double arg1, double arg2) {
        return Math.pow(arg1, arg2);
      }
    });
  }

  private Map<String, IDoubleValue> fVariableMap;

  private Map<String, BooleanVariable> fBooleanVariables;

  private ASTNode fNode;

  public DoubleEvaluator() {
    this(null);
  }

  public DoubleEvaluator(ASTNode node) {
    fVariableMap = new HashMap<String, IDoubleValue>();
    fBooleanVariables = new HashMap<String, BooleanVariable>();
    fNode = node;
  }

  /**
   * Parse the given <code>expression String</code> and store the resulting
   * ASTNode in this DoubleEvaluator
   * 
   * @param expression
   * @return
   * @throws SyntaxError
   */
  public ASTNode parse(String expression) {
    Parser p = new Parser();
    fNode = p.parse(expression);
    if (fNode instanceof FunctionNode) {
      fNode = optimizeFunction((FunctionNode) fNode);
    }
    return fNode;
  }

  /**
   * Parse the given <code>expression String</code> and return the resulting
   * ASTNode
   * 
   * @param expression
   * @return
   * @throws SyntaxError
   */
  public static ASTNode parseNode(String expression) {
    DoubleEvaluator doubleEvaluator = new DoubleEvaluator();
    return doubleEvaluator.parse(expression);
  }

  /**
   * Parse the given <code>expression String</code> and evaluate it to a double
   * value
   * 
   * @param expression
   * @return
   * @throws SyntaxError
   */
  public double evaluate(String expression) {
    Parser p = new Parser();
    fNode = p.parse(expression);
    if (fNode instanceof FunctionNode) {
      fNode = optimizeFunction((FunctionNode) fNode);
    }
    return evaluateNode(fNode);
  }

  /**
   * Reevaluate the <code>expression</code> (possibly after a new Variable
   * assignment)
   * 
   * @param expression
   * @return
   * @throws SyntaxError
   */
  public double evaluate() {
    if (fNode == null) {
      throw new SyntaxError(0, 0, 0, " ", "No parser input defined", 1);
    }
    return evaluateNode(fNode);
  }

  /**
   * Evaluate an already parsed in abstract syntax tree node into a
   * <code>double</code> number value.
   * 
   * @param node
   *          abstract syntax tree node
   * 
   * @return the evaluated double number
   * 
   * @throws ArithmeticException
   *           if the <code>node</code> cannot be evaluated.
   */
  public double evaluateNode(final ASTNode node) {
    if (node instanceof DoubleNode) {
      return ((DoubleNode) node).doubleValue();
    }
    if (node instanceof FunctionNode) {
      return evaluateFunction((FunctionNode) node);
    }
    if (node instanceof SymbolNode) {
      IDoubleValue v = fVariableMap.get(node.toString());
      if (v != null) {
        return v.getValue();
      }
      Double dbl = SYMBOL_DOUBLE_MAP.get(node.toString());
      if (dbl != null) {
        return dbl.doubleValue();
      }
    } else if (node instanceof NumberNode) {
      return ((NumberNode) node).doubleValue();
    }

    throw new ArithmeticException(
        "EvalDouble#evaluate(ASTNode) not possible for: " + node.toString());
  }

  /**
   * Evaluate an already parsed in <code>FunctionNode</code> into a
   * <code>souble</code> number value.
   * 
   * @param functionNode
   * @return
   * 
   * @throws ArithmeticException
   *           if the <code>functionNode</code> cannot be evaluated.
   */
  public double evaluateFunction(final FunctionNode functionNode) {
    if (functionNode.size() > 0
        && functionNode.getNode(0) instanceof SymbolNode) {
      String symbol = functionNode.getNode(0).toString();
      if (symbol.equals("If")) {
        if (functionNode.size() == 3) {
          if (evaluateNodeLogical(functionNode.getNode(1))) {
            return evaluateNode(functionNode.getNode(2));
          }
        } else if (functionNode.size() == 4) {
          if (evaluateNodeLogical(functionNode.getNode(1))) {
            return evaluateNode(functionNode.getNode(2));
          } else {
            return evaluateNode(functionNode.getNode(3));
          }
        }
      } else {
        Object obj = FUNCTION_DOUBLE_MAP.get(symbol);
        if (obj instanceof IDoubleFunction) {
          return ((IDoubleFunction) obj).evaluate(this, functionNode);
        }
        if (functionNode.size() == 1) {
          if (obj instanceof IDouble0Function) {
            return ((IDouble0Function) obj).evaluate();
          }
        } else if (functionNode.size() == 2) {
          if (obj instanceof IDouble1Function) {
            return ((IDouble1Function) obj).evaluate(evaluateNode(functionNode
                .getNode(1)));
          }
        } else if (functionNode.size() == 3) {
          if (obj instanceof IDouble2Function) {
            return ((IDouble2Function) obj).evaluate(
                evaluateNode(functionNode.getNode(1)),
                evaluateNode(functionNode.getNode(2)));
          }
        }
        if (fCallbackFunction != null) {
          double doubleArgs[] = new double[functionNode.size() - 1];
          for (int i = 0; i < doubleArgs.length; i++) {
            doubleArgs[i] = evaluateNode(functionNode.getNode(i + 1));
          }
          return fCallbackFunction.evaluate(this, functionNode, doubleArgs);
        }
      }
    }
    throw new ArithmeticException(
        "EvalDouble#evaluateFunction(FunctionNode) not possible for: "
            + functionNode.toString());
  }

  public boolean evaluateNodeLogical(final ASTNode node) {
    if (node instanceof FunctionNode) {
      return evaluateFunctionLogical((FunctionNode) node);
    }
    if (node instanceof SymbolNode) {
      BooleanVariable v = fBooleanVariables.get(node.toString());
      if (v != null) {
        return v.getValue();
      }
      Boolean boole = SYMBOL_BOOLEAN_MAP.get(node.toString());
      if (boole != null) {
        return boole.booleanValue();
      }
    }

    throw new ArithmeticException(
        "EvalDouble#evaluateNodeLogical(ASTNode) not possible for: "
            + node.toString());
  }

  public boolean evaluateFunctionLogical(final FunctionNode functionNode) {
    if (functionNode.size() > 0
        && functionNode.getNode(0) instanceof SymbolNode) {
      String symbol = functionNode.getNode(0).toString();
      if (functionNode.size() == 2) {
        Object obj = FUNCTION_BOOLEAN_MAP.get(symbol);
        if (obj instanceof IBooleanBoolean1Function) {
          return ((IBooleanBoolean1Function) obj)
              .evaluate(evaluateNodeLogical(functionNode.getNode(1)));
        }
      } else if (functionNode.size() == 3) {
        Object obj = FUNCTION_BOOLEAN_MAP.get(symbol);
        if (obj instanceof IBooleanDouble2Function) {
          return ((IBooleanDouble2Function) obj).evaluate(
              evaluateNode(functionNode.getNode(1)),
              evaluateNode(functionNode.getNode(2)));
        } else if (obj instanceof IBooleanBoolean2Function) {
          return ((IBooleanBoolean2Function) obj).evaluate(
              evaluateNodeLogical(functionNode.getNode(1)),
              evaluateNodeLogical(functionNode.getNode(2)));
        }
        // } else {
        // Object obj = FUNCTION_BOOLEAN_MAP.get(symbol);
        // if (obj instanceof IBooleanDoubleFunction) {
        // return ((IBooleanDoubleFunction) obj).evaluate(this, functionNode);
        // }
      }
    }
    throw new ArithmeticException(
        "EvalDouble#evaluateFunctionLogical(FunctionNode) not possible for: "
            + functionNode.toString());

  }

  /**
   * Optimize an already parsed in <code>functionNode</code> into an
   * <code>ASTNode</code>.
   * 
   * @param functionNode
   * @return
   * 
   */
  public ASTNode optimizeFunction(final FunctionNode functionNode) {
    if (functionNode.size() > 0) {
      boolean doubleOnly = true;
      ASTNode node;
      for (int i = 1; i < functionNode.size(); i++) {
        node = functionNode.getNode(i);
        if (node instanceof NumberNode) {
          functionNode.set(i, new DoubleNode(((NumberNode) functionNode
              .getNode(i)).doubleValue()));
        } else if (functionNode.getNode(i) instanceof FunctionNode) {
          ASTNode optNode = optimizeFunction((FunctionNode) functionNode
              .getNode(i));
          if (!(optNode instanceof DoubleNode)) {
            doubleOnly = false;
          }
          functionNode.set(i, optNode);
        } else if (node instanceof SymbolNode) {
          Double dbl = SYMBOL_DOUBLE_MAP.get(node.toString());
          if (dbl != null) {
            functionNode.set(i, new DoubleNode(dbl.doubleValue()));
          } else {
            doubleOnly = false;
          }
        } else {
          doubleOnly = false;
        }
      }
      if (doubleOnly) {
        try {
          return new DoubleNode(evaluateFunction(functionNode));
        } catch (Exception e) {

        }
      }
    }
    return functionNode;
  }

  /**
   * Define a value for a given variable name.
   * 
   * @param variableName
   * @param value
   */
  public void defineVariable(String variableName, IDoubleValue value) {
    fVariableMap.put(variableName, value);
  }

  /**
   * Returns the double variable value to which the specified variableName is
   * mapped, or {@code null} if this map contains no mapping for the
   * variableName.
   * 
   * @param variableName
   * @return
   */
  public IDoubleValue getVariable(String variableName) {
    return fVariableMap.get(variableName);
  }

  /**
   * Define a boolean value for a given variable name.
   * 
   * @param variableName
   * @param value
   */
  public void defineVariable(String variableName, BooleanVariable value) {
    fBooleanVariables.put(variableName, value);
  }

  /**
   * Clear all defined variables for this evaluator.
   */
  public void clearVariables() {
    fVariableMap.clear();
    fBooleanVariables.clear();
  }

  /**
   * Get the variable names from the given expression.
   * 
   * @param expression
   * @param result
   *          a set which contains the variable names
   */
  public static void getVariables(String expression, Set<String> result) {
    Parser p = new Parser();
    ASTNode node = p.parse(expression);
    getVariables(node, result);
  }

  /**
   * Get the variable names from the given AST node.
   * 
   * @param node
   *          an already parsed AST node
   * @param result
   *          a set which contains the variable names
   */
  public static void getVariables(final ASTNode node, Set<String> result) {
    if (node instanceof FunctionNode) {
      FunctionNode functionNode = (FunctionNode) node;
      if (functionNode.size() > 0
          && functionNode.getNode(0) instanceof SymbolNode) {
        for (int i = 1; i < functionNode.size(); i++) {
          getVariables(functionNode.getNode(i), result);
        }
      }
    }
    if (node instanceof SymbolNode) {
      Object obj = SYMBOL_DOUBLE_MAP.get(node.toString());
      if (obj == null) {
        obj = SYMBOL_BOOLEAN_MAP.get(node.toString());
        if (obj == null) {
          result.add(node.toString());
        }
      }

    }
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.