add eoEvalCmd, an eval that call a system command

This commit is contained in:
Johann Dreo 2020-02-08 16:34:34 +01:00
commit ccb4b3787b
4 changed files with 173 additions and 0 deletions

View file

@ -78,6 +78,7 @@
#include "eoEvalCounterThrowException.h"
#include "eoEvalTimeThrowException.h"
#include "eoEvalUserTimeThrowException.h"
#include "eoEvalCmd.h"
// Continuators - all include eoContinue.h
#include "eoCombinedContinue.h"

142
eo/src/eoEvalCmd.h Normal file
View file

@ -0,0 +1,142 @@
/*
(c) Thales group, 2020
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation;
version 2 of the License.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Contact: http://eodev.sourceforge.net
Authors:
Johann Dréo <johann.dreo@thalesgroup.com>
*/
#ifndef eoEvalCmd_H
#define eoEvalCmd_H
#include <cstdlib>
#include <array>
#include "eoEvalFunc.h"
#include "eoExceptions.h"
/** Call a system command to evaluate an individual.
*
* @note Tested only under Unix systems, may not be portable as is.
*
* Use the default string serialization of the EOT
* and pass it as command line arguments of the command.
* The command is expected to output on stdout a string
* that can be interpreted as a float by `atof`.
*
* @todo Use the serialization of fitness instead of atof.
*
* For example, an eoReal would lead to a call like:
* ```>&2 cmd INVALID 3 1.0 2.1 3.2```
*
* Throw an eoSystemError exception if the command exited with
* a return code different from zero.
*
*@ingroup Evaluation
*/
template<class EOT, int BUFFER_SIZE = 128>
class eoEvalCmd : public eoEvalFunc< EOT >
{
public:
using Fitness = typename EOT::Fitness;
/** Constructor
*
* @note The prefix and suffix are automatically
* separated from the command by a space.
*
* The formated command looks like: `prefix cmd infix sol suffix`
*
* The default prefix allows to redirect any output to stdout under Unix.
*
* @param cmd The command to run.
* @param prefix Inserted before the command
* @param suffix Inserted between cmd and the serialized solution.
* @param suffix Append after the solution.
*/
eoEvalCmd( const std::string cmd,
const std::string prefix = ">&1",
const std::string infix = "",
const std::string suffix = ""
) :
_cmd(cmd),
_suffix(suffix),
_infix(infix),
_prefix(prefix),
_last_call("")
{}
virtual void operator()( EOT& sol )
{
// Any modification to sol would makes it invalid,
// it is thus useless to evaluate it, if it is not invalid.
if(not sol.invalid()) {
return;
}
sol.fitness( call( sol ) );
}
//! Return the last command string that was called.
std::string last_call() const
{
return _last_call;
}
private:
const std::string _cmd;
const std::string _prefix;
const std::string _infix;
const std::string _suffix;
std::string _last_call;
Fitness call( EOT& sol )
{
std::array<char, BUFFER_SIZE> buffer;
std::string result;
std::ostringstream cmd;
cmd << _prefix << " " << _cmd << " "
<< _infix << " " << sol << " " << _suffix;
// Keep track of the built command for debugging purpose.
_last_call = cmd.str();
FILE* pipe = popen(cmd.str().c_str(), "r");
if(not pipe) {
throw eoSystemError(cmd.str());
}
while(fgets(buffer.data(), BUFFER_SIZE, pipe) != NULL) {
result += buffer.data();
}
auto return_code = pclose(pipe);
if(return_code != 0) {
throw eoSystemError(cmd.str(), return_code, result);
}
// FIXME Use serialized input for the fitness instead of atof.
Fitness f = std::atof(result.c_str());
return f;
}
};
#endif // eoEvalCmd_H

View file

@ -70,6 +70,7 @@ set (TEST_LIST
#t-eoDualFitness
t-eoParser
t-eoPartiallyMappedXover
t-eoEvalCmd
)

29
eo/test/t-eoEvalCmd.cpp Normal file
View file

@ -0,0 +1,29 @@
#include <iostream>
#include <eo>
#include <es.h>
using namespace std;
int main(int /*argc*/, char* /*argv[]*/)
{
typedef eoReal<eoMinimizingFitness> EOT;
// Build something like: ">&1 echo 1.2; # INVALID 2 1 2"
eoEvalCmd<EOT> eval("echo 1.2", ">&1", "; #");
EOT sol = {1,2};
std::clog << sol << std::endl;
try {
eval(sol);
} catch(eoSystemError& e) {
std::cerr << e.what() << std::endl;
exit(1);
}
std::clog << eval.last_call() << std::endl;
EOT::Fitness f = sol.fitness();
std::clog << "fitness: " << f << std::endl;
assert(f = 1.2);
}