134 lines
5.8 KiB
C++
134 lines
5.8 KiB
C++
|
/* -------------------------------------------------------------------------- *
|
||
|
* 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) 2013 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 "CompiledExpression.h"
|
||
|
#include "Operation.h"
|
||
|
#include "ParsedExpression.h"
|
||
|
#include <utility>
|
||
|
#include <cstdint>
|
||
|
|
||
|
using namespace Lepton;
|
||
|
|
||
|
CompiledExpression::CompiledExpression(const ParsedExpression& expression)
|
||
|
{
|
||
|
ParsedExpression expr = expression.optimize(); // Just in case it wasn't already optimized.
|
||
|
std::vector<std::pair<ExpressionTreeNode, int>> temps;
|
||
|
compileExpression(expr.getRootNode(), temps);
|
||
|
}
|
||
|
|
||
|
CompiledExpression& CompiledExpression::operator=(const CompiledExpression& expression)
|
||
|
{
|
||
|
arguments = expression.arguments;
|
||
|
target = expression.target;
|
||
|
variableIndices = expression.variableIndices;
|
||
|
variableNames = expression.variableNames;
|
||
|
workspace.resize(expression.workspace.size());
|
||
|
argValues.resize(expression.argValues.size());
|
||
|
operation.resize(expression.operation.size());
|
||
|
for (size_t i = 0; i < operation.size(); ++i) { operation[i] = expression.operation[i]->clone(); }
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
void CompiledExpression::compileExpression(const ExpressionTreeNode& node, std::vector<std::pair<ExpressionTreeNode, int>>& temps)
|
||
|
{
|
||
|
if (findTempIndex(node, temps) != -1) { return; } // We have already processed a node identical to this one.
|
||
|
|
||
|
// Process the child nodes.
|
||
|
|
||
|
std::vector<int> args;
|
||
|
for (size_t i = 0; i < node.getChildren().size(); ++i)
|
||
|
{
|
||
|
compileExpression(node.getChildren()[i], temps);
|
||
|
args.push_back(findTempIndex(node.getChildren()[i], temps));
|
||
|
}
|
||
|
|
||
|
// Process this node.
|
||
|
|
||
|
if (node.getOperation().getId() == Operation::VARIABLE)
|
||
|
{
|
||
|
variableIndices[node.getOperation().getName()] = int(workspace.size());
|
||
|
variableNames.insert(node.getOperation().getName());
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
int stepIndex = int(arguments.size());
|
||
|
arguments.push_back(std::vector<int>());
|
||
|
target.push_back(int(workspace.size()));
|
||
|
operation.push_back(node.getOperation().clone());
|
||
|
if (args.size() == 0) arguments[stepIndex].push_back(0); // The value won't actually be used. We just need something there.
|
||
|
else
|
||
|
{
|
||
|
// If the arguments are sequential, we can just pass a pointer to the first one.
|
||
|
|
||
|
bool sequential = true;
|
||
|
for (size_t i = 1; i < args.size(); ++i) if (args[i] != args[i - 1] + 1) sequential = false;
|
||
|
if (sequential) arguments[stepIndex].push_back(args[0]);
|
||
|
else
|
||
|
{
|
||
|
arguments[stepIndex] = args;
|
||
|
if (args.size() > argValues.size()) argValues.resize(args.size(), 0.0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
temps.push_back(std::make_pair(node, workspace.size()));
|
||
|
workspace.push_back(0.0);
|
||
|
}
|
||
|
|
||
|
int CompiledExpression::findTempIndex(const ExpressionTreeNode& node, std::vector<std::pair<ExpressionTreeNode, int>>& temps)
|
||
|
{
|
||
|
for (size_t i = 0; i < temps.size(); ++i) { if (temps[i].first == node) { return int(i); } }
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
double& CompiledExpression::getVariableReference(const std::string& name)
|
||
|
{
|
||
|
auto index = variableIndices.find(name);
|
||
|
if (index == variableIndices.end()) throw Exception("getVariableReference: Unknown variable '" + name + "'");
|
||
|
return workspace[index->second];
|
||
|
}
|
||
|
|
||
|
double CompiledExpression::evaluate() const
|
||
|
{
|
||
|
// Loop over the operations and evaluate each one.
|
||
|
|
||
|
for (size_t step = 0; step < operation.size(); ++step)
|
||
|
{
|
||
|
const std::vector<int>& args = arguments[step];
|
||
|
if (args.size() == 1) workspace[target[step]] = operation[step]->evaluate(&workspace[args[0]], dummyVariables);
|
||
|
else
|
||
|
{
|
||
|
for (size_t i = 0; i < args.size(); ++i) argValues[i] = workspace[args[i]];
|
||
|
workspace[target[step]] = operation[step]->evaluate(&argValues[0], dummyVariables);
|
||
|
}
|
||
|
}
|
||
|
return workspace[workspace.size() - 1];
|
||
|
}
|