Commit e1eb4356 authored by Peter Eastman's avatar Peter Eastman
Browse files

Implemented ExpressionProgram

parent 4aabb908
#ifndef LEPTON_EXPRESSION_PROGRAM_H_
#define LEPTON_EXPRESSION_PROGRAM_H_
/* -------------------------------------------------------------------------- *
* Lepton *
* -------------------------------------------------------------------------- *
* This is part of the Lepton expression parser originating from *
* Simbios, the NIH National Center for Physics-Based Simulation of *
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2009 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
* Permission is hereby granted, free of charge, to any person obtaining a *
* copy of this software and associated documentation files (the "Software"), *
* to deal in the Software without restriction, including without limitation *
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
* and/or sell copies of the Software, and to permit persons to whom the *
* Software is furnished to do so, subject to the following conditions: *
* *
* The above copyright notice and this permission notice shall be included in *
* all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
#include "ExpressionTreeNode.h"
#include "windowsIncludes.h"
#include <map>
#include <string>
#include <vector>
namespace Lepton {
/**
* An ExpressionProgram is a linear sequence of Operations for evaluating an expression. The evaluation
* is done with a stack. The arguments to each Operation are first taken off the stack in order, then it is
* evaluated and the result is pushed back onto the stack. At the end, the stack contains a single value,
* which is the value of the expression.
*
* An ExpressionProgram is created by calling createProgram() on a ParsedExpression. It can generally be evaluated
* more quickly than the ParsedExpression itself, so when you need to evaluate an expression many times, it is
* most efficient to create an ExpressionProgram from it.
*/
class LEPTON_EXPORT ExpressionProgram {
public:
ExpressionProgram(const ExpressionProgram& program);
~ExpressionProgram();
ExpressionProgram& operator=(const ExpressionProgram& program);
/**
* Get the number of Operations that make up this program.
*/
int getNumOperations() const;
/**
* Get an Operation in this program.
*/
const Operation& getOperation(int index) const;
/**
* Get the size of the stack needed to execute this program. This is the largest number of elements present
* on the stack at any point during evaluation.
*/
int getStackSize() const;
/**
* Evaluate the expression. If the expression involves any variables, this method will throw an exception.
*/
double evaluate() const;
/**
* Evaluate the expression.
*
* @param variables a map specifying the values of all variables that appear in the expression. If any
* variable appears in the expression but is not included in this map, an exception
* will be thrown.
*/
double evaluate(const std::map<std::string, double>& variables) const;
private:
friend class ParsedExpression;
ExpressionProgram(const ParsedExpression& expression);
void buildProgram(const ExpressionTreeNode& node);
std::vector<Operation*> operations;
int maxArgs, stackSize;
};
} // namespace Lepton
#endif /*LEPTON_EXPRESSION_PROGRAM_H_*/
...@@ -39,6 +39,8 @@ ...@@ -39,6 +39,8 @@
namespace Lepton { namespace Lepton {
class ExpressionProgram;
/** /**
* This class represents the result of parsing an expression. It provides methods for working with the * This class represents the result of parsing an expression. It provides methods for working with the
* expression in various ways, such as evaluating it, getting the tree representation of the expresson, etc. * expression in various ways, such as evaluating it, getting the tree representation of the expresson, etc.
...@@ -50,7 +52,7 @@ public: ...@@ -50,7 +52,7 @@ public:
* Create a ParsedExpression. Normally you will not call this directly. Instead, use the Parser class * Create a ParsedExpression. Normally you will not call this directly. Instead, use the Parser class
* to parse expression. * to parse expression.
*/ */
ParsedExpression(ExpressionTreeNode rootNode); ParsedExpression(const ExpressionTreeNode& rootNode);
/** /**
* Get the root node of the expression's abstract syntax tree. * Get the root node of the expression's abstract syntax tree.
*/ */
...@@ -86,6 +88,10 @@ public: ...@@ -86,6 +88,10 @@ public:
* @param variable the variable with respect to which the derivate should be taken * @param variable the variable with respect to which the derivate should be taken
*/ */
ParsedExpression differentiate(const std::string& variable) const; ParsedExpression differentiate(const std::string& variable) const;
/**
* Create an ExpressionProgram that represents the same calculation as this expression.
*/
ExpressionProgram createProgram() const;
private: private:
static double evaluate(const ExpressionTreeNode& node, const std::map<std::string, double>& variables); static double evaluate(const ExpressionTreeNode& node, const std::map<std::string, double>& variables);
static ExpressionTreeNode preevaluateVariables(const ExpressionTreeNode& node, const std::map<std::string, double>& variables); static ExpressionTreeNode preevaluateVariables(const ExpressionTreeNode& node, const std::map<std::string, double>& variables);
......
/* -------------------------------------------------------------------------- *
* Lepton *
* -------------------------------------------------------------------------- *
* This is part of the Lepton expression parser originating from *
* Simbios, the NIH National Center for Physics-Based Simulation of *
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2009 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
* Permission is hereby granted, free of charge, to any person obtaining a *
* copy of this software and associated documentation files (the "Software"), *
* to deal in the Software without restriction, including without limitation *
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
* and/or sell copies of the Software, and to permit persons to whom the *
* Software is furnished to do so, subject to the following conditions: *
* *
* The above copyright notice and this permission notice shall be included in *
* all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
#include "ExpressionProgram.h"
#include "Operation.h"
#include "ParsedExpression.h"
using namespace Lepton;
using namespace std;
ExpressionProgram::ExpressionProgram(const ParsedExpression& expression) : maxArgs(0), stackSize(0) {
buildProgram(expression.getRootNode());
int currentStackSize = 0;
for (int i = 0; i < operations.size(); i++) {
int args = operations[i]->getNumArguments();
if (args > maxArgs)
maxArgs = args;
currentStackSize += 1-args;
if (currentStackSize > stackSize)
stackSize = currentStackSize;
}
}
ExpressionProgram::~ExpressionProgram() {
for (int i = 0; i < operations.size(); i++)
delete operations[i];
}
ExpressionProgram::ExpressionProgram(const ExpressionProgram& program) {
*this = program;
}
ExpressionProgram& ExpressionProgram::operator=(const ExpressionProgram& program) {
maxArgs = program.maxArgs;
stackSize = program.stackSize;
operations.resize(program.operations.size());
for (int i = 0; i < operations.size(); i++)
operations[i] = program.operations[i]->clone();
return *this;
}
void ExpressionProgram::buildProgram(const ExpressionTreeNode& node) {
for (int i = node.getChildren().size()-1; i >= 0; i--)
buildProgram(node.getChildren()[i]);
operations.push_back(node.getOperation().clone());
}
int ExpressionProgram::getNumOperations() const {
return operations.size();
}
const Operation& ExpressionProgram::getOperation(int index) const {
return *operations[index];
}
int ExpressionProgram::getStackSize() const {
return stackSize;
}
double ExpressionProgram::evaluate() const {
return evaluate(map<string, double>());
}
double ExpressionProgram::evaluate(const std::map<std::string, double>& variables) const {
vector<double> args(maxArgs);
vector<double> stack(stackSize);
int stackPointer = 0;
for (int i = 0; i < operations.size(); i++) {
int numArgs = operations[i]->getNumArguments();
for (int j = 0; j < numArgs; j++)
args[j] = stack[--stackPointer];
stack[stackPointer++] = operations[i]->evaluate(&args[0], variables);
}
return stack[0];
}
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
* -------------------------------------------------------------------------- */ * -------------------------------------------------------------------------- */
#include "ParsedExpression.h" #include "ParsedExpression.h"
#include "ExpressionProgram.h"
#include "Operation.h" #include "Operation.h"
#include <limits> #include <limits>
#include <vector> #include <vector>
...@@ -37,7 +38,7 @@ ...@@ -37,7 +38,7 @@
using namespace Lepton; using namespace Lepton;
using namespace std; using namespace std;
ParsedExpression::ParsedExpression(ExpressionTreeNode rootNode) : rootNode(rootNode) { ParsedExpression::ParsedExpression(const ExpressionTreeNode& rootNode) : rootNode(rootNode) {
} }
const ExpressionTreeNode& ParsedExpression::getRootNode() const { const ExpressionTreeNode& ParsedExpression::getRootNode() const {
...@@ -105,6 +106,7 @@ ExpressionTreeNode ParsedExpression::substituteSimplerExpression(const Expressio ...@@ -105,6 +106,7 @@ ExpressionTreeNode ParsedExpression::substituteSimplerExpression(const Expressio
children[i] = substituteSimplerExpression(node.getChildren()[i]); children[i] = substituteSimplerExpression(node.getChildren()[i]);
switch (node.getOperation().getId()) { switch (node.getOperation().getId()) {
case Operation::ADD: case Operation::ADD:
{
double first = getConstantValue(children[0]); double first = getConstantValue(children[0]);
double second = getConstantValue(children[1]); double second = getConstantValue(children[1]);
if (first == 0.0) if (first == 0.0)
...@@ -116,19 +118,23 @@ ExpressionTreeNode ParsedExpression::substituteSimplerExpression(const Expressio ...@@ -116,19 +118,23 @@ ExpressionTreeNode ParsedExpression::substituteSimplerExpression(const Expressio
if (second == 1.0) if (second == 1.0)
return ExpressionTreeNode(new Operation::Increment(), children[0]); return ExpressionTreeNode(new Operation::Increment(), children[0]);
break; break;
}
case Operation::SUBTRACT: case Operation::SUBTRACT:
first = getConstantValue(children[0]); {
double first = getConstantValue(children[0]);
if (first == 0.0) if (first == 0.0)
return ExpressionTreeNode(new Operation::Negate(), children[1]); return ExpressionTreeNode(new Operation::Negate(), children[1]);
second = getConstantValue(children[1]); double second = getConstantValue(children[1]);
if (second == 0.0) if (second == 0.0)
return children[0]; return children[0];
if (second == 1.0) if (second == 1.0)
return ExpressionTreeNode(new Operation::Decrement(), children[0]); return ExpressionTreeNode(new Operation::Decrement(), children[0]);
break; break;
}
case Operation::MULTIPLY: case Operation::MULTIPLY:
first = getConstantValue(children[0]); {
second = getConstantValue(children[1]); double first = getConstantValue(children[0]);
double second = getConstantValue(children[1]);
if (first == 0.0 || second == 0.0) if (first == 0.0 || second == 0.0)
return ExpressionTreeNode(new Operation::Constant(0.0)); return ExpressionTreeNode(new Operation::Constant(0.0));
if (first == 1.0) if (first == 1.0)
...@@ -136,7 +142,9 @@ ExpressionTreeNode ParsedExpression::substituteSimplerExpression(const Expressio ...@@ -136,7 +142,9 @@ ExpressionTreeNode ParsedExpression::substituteSimplerExpression(const Expressio
if (second == 1.0) if (second == 1.0)
return children[0]; return children[0];
break; break;
}
case Operation::DIVIDE: case Operation::DIVIDE:
{
double numerator = getConstantValue(children[0]); double numerator = getConstantValue(children[0]);
if (numerator = 0.0) if (numerator = 0.0)
return ExpressionTreeNode(new Operation::Constant(0.0)); return ExpressionTreeNode(new Operation::Constant(0.0));
...@@ -146,7 +154,9 @@ ExpressionTreeNode ParsedExpression::substituteSimplerExpression(const Expressio ...@@ -146,7 +154,9 @@ ExpressionTreeNode ParsedExpression::substituteSimplerExpression(const Expressio
if (denominator == 1.0) if (denominator == 1.0)
return children[0]; return children[0];
break; break;
}
case Operation::POWER: case Operation::POWER:
{
double base = getConstantValue(children[0]); double base = getConstantValue(children[0]);
if (base == 0.0) if (base == 0.0)
return ExpressionTreeNode(new Operation::Constant(0.0)); return ExpressionTreeNode(new Operation::Constant(0.0));
...@@ -165,6 +175,7 @@ ExpressionTreeNode ParsedExpression::substituteSimplerExpression(const Expressio ...@@ -165,6 +175,7 @@ ExpressionTreeNode ParsedExpression::substituteSimplerExpression(const Expressio
return ExpressionTreeNode(new Operation::Sqrt(), children[0]); return ExpressionTreeNode(new Operation::Sqrt(), children[0]);
break; break;
} }
}
return ExpressionTreeNode(node.getOperation().clone(), children); return ExpressionTreeNode(node.getOperation().clone(), children);
} }
...@@ -185,6 +196,10 @@ double ParsedExpression::getConstantValue(const ExpressionTreeNode& node) { ...@@ -185,6 +196,10 @@ double ParsedExpression::getConstantValue(const ExpressionTreeNode& node) {
return numeric_limits<double>::quiet_NaN(); return numeric_limits<double>::quiet_NaN();
} }
ExpressionProgram ParsedExpression::createProgram() const {
return ExpressionProgram(*this);
}
ostream& Lepton::operator<<(ostream& out, const ExpressionTreeNode& node) { ostream& Lepton::operator<<(ostream& out, const ExpressionTreeNode& node) {
out << node.getOperation().getName(); out << node.getOperation().getName();
if (node.getChildren().size() > 0) { if (node.getChildren().size() > 0) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment