Skip to content

Commit

Permalink
Boost Matheval expression parser
Browse files Browse the repository at this point in the history
Also make Timer.cpp actually compile (is this still needed?  It's at least not
used anywhere)
  • Loading branch information
hmenke committed Nov 28, 2017
1 parent 8680969 commit 291f4a5
Show file tree
Hide file tree
Showing 11 changed files with 229 additions and 11 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[submodule "libs/h5xx"]
path = libs/h5xx
url = https://github.com/h5md/h5xx.git
[submodule "libs/boost_matheval"]
path = libs/boost_matheval
url = https://github.com/hmenke/boost_matheval.git
13 changes: 13 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,19 @@ if(WITH_VALGRIND_INSTRUMENTATION)
endif(VALGRIND_FOUND)
endif(WITH_VALGRIND_INSTRUMENTATION)

# Check out the Boost Matheval submodule
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/libs/boost_matheval/.git")
# Try to find git
find_package(Git)
if(GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
message(STATUS "Initializing/updating Boost Matheval")
execute_process(
COMMAND ${GIT_EXECUTABLE} submodule update --init -- libs/boost_matheval
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
endif()
endif()
include_directories(${CMAKE_SOURCE_DIR}/libs/boost_matheval/include)

include(RequireCXX11)

#######################################################################
Expand Down
2 changes: 1 addition & 1 deletion doc/doxygen/main.dox
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ i. e. a client-server model is utilized. More details can be found in
however, the communication is done synchronously. For more details
see \ref integrate.cpp "integrate.cpp".

\section Copyright and License of the Code Documentation
\section copyright Copyright and License of the Code Documentation
Copyright (C) 2010,2011,2012 The ESPResSo project
Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010
Max-Planck-Institute for Polymer Research, Theory Group
Expand Down
1 change: 1 addition & 0 deletions libs/boost_matheval
Submodule boost_matheval added at 72edf4
4 changes: 1 addition & 3 deletions src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
file(GLOB EspressoCore_SRC
"*.cpp"
)
file(GLOB EspressoCore_SRC "*.cpp" "utils/*.cpp")

if( WITH_COVERAGE )
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Og --coverage -fprofile-arcs -ftest-coverage")
Expand Down
1 change: 1 addition & 0 deletions src/core/unit_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,4 @@ unit_test(NAME for_each_pair_test SRC for_each_pair_test.cpp)
unit_test(NAME all_compare_test SRC all_compare_test.cpp NUM_PROC 3)
unit_test(NAME None_test SRC None_test.cpp)
unit_test(NAME keys_test SRC keys_test.cpp)
unit_test(NAME ExpressionParser_test SRC ExpressionParser_test.cpp ../utils/ExpressionParser.cpp)
72 changes: 72 additions & 0 deletions src/core/unit_tests/ExpressionParser_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
Copyright (C) 2017 The ESPResSo project
This file is part of ESPResSo.
ESPResSo 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.
ESPResSo 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/>.
*/

#define BOOST_TEST_MODULE Expression parser test
#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>

#include <cmath>
#include <limits>
#include <map>
#include <stdexcept>
#include <string>

#include "utils/ExpressionParser.hpp"

// We only test the integration into ESPResSo as Boost Matheval has
// plenty of tests itself.

BOOST_AUTO_TEST_CASE(integration1) {
std::string expr = "cbrt(x/2 + sqrt(x**2/4 + y**3/24))";
double x = 2.0, y = -1.0;

Utils::ExpressionParser parser;
BOOST_CHECK_NO_THROW(parser.parse(expr));

std::map<std::string,double> symbol_table = {
std::make_pair("x",x),
std::make_pair("y",y),
};

double result = 0;
BOOST_CHECK_NO_THROW(result = parser.evaluate(symbol_table));

double expected = std::cbrt(x/2. + std::sqrt(std::pow(x,2.)/4. + std::pow(y,3.)/24.));
BOOST_CHECK_CLOSE_FRACTION(result, expected, std::numeric_limits<double>::epsilon());
}

BOOST_AUTO_TEST_CASE(integration2) {
std::string expr = "(";

Utils::ExpressionParser parser;

// Parsing should fail
BOOST_CHECK_THROW(parser.parse(expr), std::runtime_error);
}

BOOST_AUTO_TEST_CASE(integration3) {
std::string expr = "x";

Utils::ExpressionParser parser;

BOOST_CHECK_NO_THROW(parser.parse(expr));

// Evaluating should fail
BOOST_CHECK_THROW(parser.evaluate({}), std::invalid_argument);
}
61 changes: 61 additions & 0 deletions src/core/utils/ExpressionParser.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
Copyright (C) 2017 The ESPResSo project
This file is part of ESPResSo.
ESPResSo 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.
ESPResSo 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/>.
*/
#include <map>
#include <string>

#include "ExpressionParser.hpp"
#include "utils/make_unique.hpp"

#include "matheval.hpp"

namespace Utils
{

class ExpressionParser::impl
{
matheval::Parser<double> parser;
public:
void parse(std::string const &expr)
{
parser.parse(expr);
}

double evaluate(std::map<std::string,double> const &st)
{
return parser.evaluate(st);
}
};

ExpressionParser::ExpressionParser()
: pimpl{Utils::make_unique<ExpressionParser::impl>()}
{}

ExpressionParser::~ExpressionParser() {}

void ExpressionParser::parse(std::string const &expr)
{
pimpl->parse(expr);
}

double ExpressionParser::evaluate(std::map<std::string,double> const &st)
{
return pimpl->evaluate(st);
}

} // namespace Utils
72 changes: 72 additions & 0 deletions src/core/utils/ExpressionParser.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
Copyright (C) 2017 The ESPResSo project
This file is part of ESPResSo.
ESPResSo 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.
ESPResSo 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/>.
*/
#ifndef CORE_UTILS_EXPRESSION_PARSER_HPP
#define CORE_UTILS_EXPRESSION_PARSER_HPP

#include <map>
#include <memory>
#include <string>

namespace Utils
{

/** @brief Parse a mathematical expression
*
* This can parse and evaluate a mathematical expression for a given
* symbol table using Boost Matheval. The templates of Boost.Spirit
* (which is the basis of Boost Matheval) are very expensive to parse
* and instantiate, which is why we hide it behind an opaque pointer.
*
* The drawback of this approach is that calls can no longer be
* inlined and because the pointer crosses translation unit
* boundaries, dereferencing it can also not be optimized out at
* compile time. We have to rely entirely on link-time optimization
* which might be not as good.
*
* The pointer to the implementation is a std::unique_ptr which makes
* the class not copyable but only moveable. Copying shouldn't be
* required but is easy to implement.
*/
class ExpressionParser
{
class impl;
std::unique_ptr<impl> pimpl;
public:
/** @brief Constructor */
ExpressionParser();

/** @brief Destructor */
~ExpressionParser();

/** @brief Parse the mathematical expression into an abstract syntax tree
*
* @param[in] expr The expression given as a std::string
*/
void parse(std::string const &expr);

/** @brief Evaluate the abstract syntax tree for a given symbol table
*
* @param[in] st The symbol table
*/
double evaluate(std::map<std::string,double> const &st);
};

} // namespace Utils

#endif // CORE_UTILS_EXPRESSION_PARSER_HPP
8 changes: 3 additions & 5 deletions src/core/utils/Timer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ namespace Timing {

std::vector<std::map<std::string, Utils::Timing::Timer::Stats>>
Timer::get_all_stats() {
boost::mpi::communicator const &comm = Communication::mpiCallbacks().comm();
boost::mpi::communicator const &comm = Communication::MpiCallbacks(comm_cart).comm();
assert(comm.rank() == 0);

std::vector<std::map<std::string, Utils::Timing::Timer::Stats>> ret(
comm.size());

ret[0] = get_stats();

Communication::mpiCallbacks().call(Timer::mpi_callback);
Communication::MpiCallbacks(comm_cart).call(Timer::mpi_callback);

for (unsigned i = 1; i < ret.size(); ++i) {
comm.recv(i, 42, ret[i]);
Expand All @@ -48,11 +48,9 @@ Timer::get_all_stats() {
}

void Timer::mpi_callback(int, int) {
Communication::mpiCallbacks().comm().send(0, 42, get_stats());
Communication::MpiCallbacks(comm_cart).comm().send(0, 42, get_stats());
}

std::unordered_map<std::string, Timer> Timer::m_timers;

const Communication::CallbackAdder Timer::cb_adder{Timer::mpi_callback};
}
}
3 changes: 1 addition & 2 deletions src/core/utils/Timer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include <mpi.h>

#include "communication.hpp"
#include "MpiCallbacks.hpp"
#include "utils/statistics/RunningAverage.hpp"

Expand Down Expand Up @@ -95,8 +96,6 @@ class Timer {
friend Communication::MpiCallbacks;
static void mpi_callback(int, int);

static const Communication::CallbackAdder cb_adder;

static std::unordered_map<std::string, Timer> m_timers;
Statistics::RunningAverage<double> m_running_average;
double m_mark;
Expand Down

0 comments on commit 291f4a5

Please sign in to comment.