dlvhex  2.5.0
src/State.cpp
Go to the documentation of this file.
00001 /* dlvhex -- Answer-Set Programming with external interfaces.
00002  * Copyright (C) 2005-2007 Roman Schindlauer
00003  * Copyright (C) 2006-2015 Thomas Krennwallner
00004  * Copyright (C) 2009-2016 Peter Schüller
00005  * Copyright (C) 2011-2016 Christoph Redl
00006  * Copyright (C) 2015-2016 Tobias Kaminski
00007  * Copyright (C) 2015-2016 Antonius Weinzierl
00008  *
00009  * This file is part of dlvhex.
00010  *
00011  * dlvhex is free software; you can redistribute it and/or modify it
00012  * under the terms of the GNU Lesser General Public License as
00013  * published by the Free Software Foundation; either version 2.1 of
00014  * the License, or (at your option) any later version.
00015  *
00016  * dlvhex is distributed in the hope that it will be useful, but
00017  * WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019  * Lesser General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU Lesser General Public
00022  * License along with dlvhex; if not, write to the Free Software
00023  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
00024  * 02110-1301 USA.
00025  */
00026 
00039 #include "dlvhex2/State.h"
00040 
00041 // activate benchmarking if activated by configure option --enable-debug
00042 #ifdef HAVE_CONFIG_H
00043 # include "config.h"
00044 #endif
00045 
00046 #include "dlvhex2/ProgramCtx.h"
00047 #include "dlvhex2/Error.h"
00048 #include "dlvhex2/Printhelpers.h"
00049 #include "dlvhex2/Benchmarking.h"
00050 #include "dlvhex2/ASPSolverManager.h"
00051 #include "dlvhex2/ASPSolver.h"
00052 #include "dlvhex2/HexParser.h"
00053 #include "dlvhex2/Printer.h"
00054 #include "dlvhex2/Registry.h"
00055 #include "dlvhex2/PluginContainer.h"
00056 #include "dlvhex2/LiberalSafetyChecker.h"
00057 #include "dlvhex2/DependencyGraph.h"
00058 #include "dlvhex2/ComponentGraph.h"
00059 #include "dlvhex2/FinalEvalGraph.h"
00060 #include "dlvhex2/EvalGraphBuilder.h"
00061 #include "dlvhex2/DumpingEvalGraphBuilder.h"
00062 #include "dlvhex2/AnswerSetPrinterCallback.h"
00063 #include "dlvhex2/PlainAuxPrinter.h"
00064 #include "dlvhex2/SafetyChecker.h"
00065 #include "dlvhex2/MLPSyntaxChecker.h"
00066 #include "dlvhex2/MLPSolver.h"
00067 
00068 #include <boost/foreach.hpp>
00069 
00070 #include <iostream>
00071 #include <sstream>
00072 #include <fstream>
00073 #include <vector>
00074 
00075 DLVHEX_NAMESPACE_BEGIN
00076 
00077 State::State(StatePtr failureState):
00078 failureState(failureState)
00079 {
00080 }
00081 
00082 
00083 State::~State()
00084 {
00085 }
00086 
00087 
00088 namespace
00089 {
00090     std::ostream& printStatePtr(std::ostream& o, StatePtr ptr) {
00091         if( !ptr )
00092             return o << "NULL";
00093         else
00094             return o << "'" << typeid(*ptr).name() << "'";
00095     }
00096 }
00097 
00098 
00099 void State::changeState(ProgramCtx* ctx, StatePtr s)
00100 {
00101     LOG(INFO,
00102         "State::changeState from " <<
00103         print_function(boost::bind(&printStatePtr, _1, ctx->state)) <<
00104         " to " <<
00105         print_function(boost::bind(&printStatePtr, _1, s)));
00106     ctx->changeState(s);
00107 }
00108 
00109 
00110 // each of these functions skips to the "failureState" and executes the executed function on it
00111 // this is useful for having optional states
00112 // if no failureState is given, an exception is raised
00113 // this is useful for non-optional states
00114 #define STATE_FUNC_DEFAULT_IMPL(function) \
00115     void State:: function (ProgramCtx* ctx) \
00116     { \
00117         if( !!failureState ) \
00118         { \
00119             changeState(ctx, failureState); /* <-- this destructs *this */ \
00120             ctx->state-> function (ctx); \
00121         } \
00122         else \
00123         { \
00124             throw std::runtime_error("tried to skip execution of '" \
00125             #function "' in State!"); \
00126         } \
00127     }
00128 
00129         // all state methods get skipping possibility
00130         // derived classes will decide whether to set the failureState or not
00131         // if it is set, the state is skippable,
00132         // if not, execution of this state is mandatory
00133         STATE_FUNC_DEFAULT_IMPL(showPlugins);
00134 STATE_FUNC_DEFAULT_IMPL(convert);
00135 STATE_FUNC_DEFAULT_IMPL(parse);
00136 STATE_FUNC_DEFAULT_IMPL(moduleSyntaxCheck);
00137 STATE_FUNC_DEFAULT_IMPL(mlpSolver);
00138 STATE_FUNC_DEFAULT_IMPL(rewriteEDBIDB);
00139 STATE_FUNC_DEFAULT_IMPL(safetyCheck);
00140 STATE_FUNC_DEFAULT_IMPL(checkLiberalSafety);
00141 STATE_FUNC_DEFAULT_IMPL(createDependencyGraph);
00142 STATE_FUNC_DEFAULT_IMPL(optimizeEDBDependencyGraph);
00143 STATE_FUNC_DEFAULT_IMPL(createComponentGraph);
00144 STATE_FUNC_DEFAULT_IMPL(strongSafetyCheck);
00145 STATE_FUNC_DEFAULT_IMPL(createEvalGraph);
00146 STATE_FUNC_DEFAULT_IMPL(setupProgramCtx);
00147 STATE_FUNC_DEFAULT_IMPL(evaluate);
00148 STATE_FUNC_DEFAULT_IMPL(postProcess);
00149 
00150 #define MANDATORY_STATE_CONSTRUCTOR(state) \
00151     state :: state (): State() {}
00152 
00153 #define OPTIONAL_STATE_CONSTRUCTOR(state,skiptostate) \
00154     state :: state (): State(StatePtr(new skiptostate)) {}
00155 
00156 OPTIONAL_STATE_CONSTRUCTOR(ShowPluginsState,ConvertState);
00157 
00158 void ShowPluginsState::showPlugins(ProgramCtx* ctx)
00159 {
00160     if( !ctx->config.getOption("Silent") ) {
00161         BOOST_FOREACH(PluginInterfacePtr plugin, ctx->pluginContainer()->getPlugins()) {
00162             LOG(INFO,"opening plugin " << plugin->getPluginName() <<
00163                 " version " <<
00164                 plugin->getVersionMajor() << "." <<
00165                 plugin->getVersionMinor() << "." <<
00166                 plugin->getVersionMicro());
00167         }
00168     }
00169 
00170     boost::shared_ptr<State> next(new ConvertState);
00171     changeState(ctx, next);
00172 }
00173 
00174 
00175 OPTIONAL_STATE_CONSTRUCTOR(ConvertState,ParseState);
00176 
00177 void ConvertState::convert(ProgramCtx* ctx)
00178 {
00179     assert(!!ctx->inputProvider && ctx->inputProvider->hasContent() && "need input provider with content for converting");
00180 
00181     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sid,"Calling plugin converters");
00182 
00183     // get combination of input filenames for creating debug output files and for naming converted input
00184     std::string inputName;
00185     BOOST_FOREACH(const std::string& name, ctx->inputProvider->contentNames()) {
00186         // only use part after last / here
00187         inputName += "_" + name.substr(name.find_last_of("/") + 1);
00188     }
00189     LOG(INFO,"inputName='" << inputName << "'");
00190 
00191     // store it
00192     ctx->config.setStringOption("DebugPrefix","dbg" + inputName);
00193     LOG(DBG,"debugFilePrefix='" << ctx->config.getStringOption("DebugPrefix") << "'");
00194 
00195     std::vector<PluginConverterPtr> converters;
00196     BOOST_FOREACH(PluginInterfacePtr plugin, ctx->pluginContainer()->getPlugins()) {
00197         BOOST_FOREACH(PluginConverterPtr pc, plugin->createConverters(*ctx)) {
00198             LOG(PLUGIN,"got plugin converter from plugin " << plugin->getPluginName());
00199             converters.push_back(pc);
00200         }
00201     }
00202 
00203     if( converters.size() > 1 )
00204         LOG(WARNING,"got more than one plugin converter, using arbitrary order!");
00205 
00206     BOOST_FOREACH(PluginConverterPtr converter, converters) {
00207         DBGLOG(DBG,"calling input converter");
00208         std::stringstream out;
00209         converter->convert(ctx->inputProvider->getAsStream(), out);
00210 
00211         // debug output (if requested)
00212         if( ctx->config.doVerbose(Configuration::DUMP_CONVERTED_PROGRAM) ) {
00213             LOG(DBG,"input conversion result:" << std::endl << out.str() << std::endl);
00214         }
00215 
00216         // replace input provider with converted input provider
00217         ctx->inputProvider.reset(new InputProvider);
00218         ctx->inputProvider->addStringInput(out.str(), "converted" + inputName);
00219     }
00220 
00221     WARNING("TODO realize dlt as a preparser plugin")
00222 
00223         boost::shared_ptr<State> next(new ParseState);
00224     changeState(ctx, next);
00225 }
00226 
00227 
00228 MANDATORY_STATE_CONSTRUCTOR(ParseState);
00229 
00230 void ParseState::parse(ProgramCtx* ctx)
00231 {
00232     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sid,"Parsing input");
00233 
00234     // use alternative parser from plugins, if applicable
00235     assert(!ctx->parser);
00236     BOOST_FOREACH(PluginInterfacePtr plugin, ctx->pluginContainer()->getPlugins()) {
00237         HexParserPtr alternativeParser = plugin->createParser(*ctx);
00238         if( !!alternativeParser ) {
00239             if( !!ctx->parser ) {
00240                 LOG(WARNING,"ignoring alternative parser provided by plugin " <<
00241                     plugin->getPluginName() << " because parser already initialized");
00242             }
00243             else {
00244                 LOG(INFO,"using alternative parser provided by plugin " <<
00245                     plugin->getPluginName());
00246                 ctx->parser = alternativeParser;
00247             }
00248         }
00249     }
00250 
00251     // use default parser if no alternative parsers given
00252     if( !ctx->parser ) {
00253         LOG(INFO,"using default parser (no alternatives provided by plugins)");
00254         ctx->parser.reset(new ModuleHexParser);
00255     }
00256 
00257     // configure parser modules if possible
00258     {
00259         ModuleHexParserPtr mhp =
00260             boost::dynamic_pointer_cast<ModuleHexParser>(ctx->parser);
00261         BOOST_FOREACH(PluginInterfacePtr plugin, ctx->pluginContainer()->getPlugins()) {
00262             std::vector<HexParserModulePtr> modules =
00263                 plugin->createParserModules(*ctx);
00264             if( !modules.empty() ) {
00265                 if( !!mhp ) {
00266                     LOG(INFO,"got " << modules.size() <<
00267                         " parser modules from plugin " << plugin->getPluginName());
00268                     BOOST_FOREACH(HexParserModulePtr module, modules) {
00269                         mhp->registerModule(module);
00270                     }
00271                     LOG(INFO,"registered successfully");
00272                 }
00273                 else {
00274                     LOG(WARNING,"ignoring parser module from plugin '" <<
00275                         plugin->getPluginName() << "' as ModuleHexParser is not used");
00276                 }
00277             }
00278         }
00279     }
00280 
00281     // parse
00282     assert(!!ctx->parser);
00283     ctx->parser->parse(ctx->inputProvider, *ctx);
00284 
00285     // free input provider memory
00286     assert(ctx->inputProvider.use_count() == 1);
00287     ctx->inputProvider.reset();
00288 
00289     WARNING("namespaces were here! (perhaps we should forget namespaces. in the best case implement in a rewriter plugin)")
00290 
00291     // be verbose if requested
00292         if( ctx->config.doVerbose(Configuration::DUMP_PARSED_PROGRAM) &&
00293     Logger::Instance().shallPrint(Logger::INFO) ) {
00294         LOG(INFO,"parsed IDB:");
00295         RawPrinter rp(Logger::Instance().stream(), ctx->registry());
00296         rp.printmany(ctx->idb, "\n");
00297         Logger::Instance().stream() << std::endl;
00298 
00299         LOG(INFO,"parsed EDB:");
00300         Logger::Instance().stream() << *(ctx->edb) << std::endl;
00301     }
00302     if( ctx->config.getOption("MLP") ) {
00303         StatePtr next(new ModuleSyntaxCheckState);
00304         changeState(ctx, next);
00305     }
00306     else {
00307         StatePtr next(new RewriteEDBIDBState);
00308         changeState(ctx, next);
00309     }
00310 }
00311 
00312 
00313 MANDATORY_STATE_CONSTRUCTOR(ModuleSyntaxCheckState);
00314 // MLPSyntaxChecker ..
00315 void ModuleSyntaxCheckState::moduleSyntaxCheck(ProgramCtx* ctx)
00316 {
00317     #ifdef HAVE_MLP
00318     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sid,"Module Syntax Check");
00319     MLPSyntaxChecker sC(*ctx);
00320     bool success = sC.verifySyntax();
00321     #else
00322     bool success = true;
00323     #endif
00324     if (success) {
00325         StatePtr next(new MLPSolverState);
00326         changeState(ctx, next);
00327     }
00328     else {
00329         std::cout << "Does not solve the MLP because of syntax error" << std::endl;
00330         StatePtr next(new PostProcessState);
00331         changeState(ctx, next);
00332     }
00333 }
00334 
00335 
00336 MANDATORY_STATE_CONSTRUCTOR(MLPSolverState);
00337 void MLPSolverState::mlpSolver(ProgramCtx* ctx)
00338 {
00339     #ifdef HAVE_MLP
00340     MLPSolver m(*ctx);
00341     m.setNASReturned(ctx->config.getOption("NumberOfModels"));
00342     m.setPrintLevel(ctx->config.getOption("Verbose"));
00343     m.setForget(ctx->config.getOption("Forget"));
00344     m.setInstSplitting(ctx->config.getOption("Split"));
00345     m.solve();
00346     #endif
00347     StatePtr next(new PostProcessState);
00348     changeState(ctx, next);
00349 }
00350 
00351 
00352 OPTIONAL_STATE_CONSTRUCTOR(RewriteEDBIDBState,SafetyCheckState);
00353 
00354 void
00355 RewriteEDBIDBState::rewriteEDBIDB(ProgramCtx* ctx)
00356 {
00357     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sid,"Calling plugin rewriters");
00358     DBGLOG_SCOPE(DBG,"rewrite",false);
00359 
00360     // get rewriter from each plugin
00361     BOOST_FOREACH(PluginInterfacePtr plugin, ctx->pluginContainer()->getPlugins()) {
00362         PluginRewriterPtr rewriter = plugin->createRewriter(*ctx);
00363         if( !rewriter )
00364             continue;
00365 
00366         LOG(PLUGIN,"got plugin rewriter from plugin " << plugin->getPluginName());
00367 
00368         rewriter->rewrite(*ctx);
00369 
00370         // be verbose if requested
00371         if( ctx->config.doVerbose(Configuration::DUMP_REWRITTEN_PROGRAM) &&
00372         Logger::Instance().shallPrint(Logger::INFO) ) {
00373             LOG(INFO,"rewritten IDB:");
00374             RawPrinter rp(Logger::Instance().stream(), ctx->registry());
00375             rp.printmany(ctx->idb, "\n");
00376             Logger::Instance().stream() << std::endl;
00377 
00378             LOG(INFO,"rewritten EDB:");
00379             Logger::Instance().stream() << *(ctx->edb) << std::endl;
00380         }
00381     }
00382 
00383     StatePtr next(new SafetyCheckState);
00384     changeState(ctx, next);
00385 }
00386 
00387 
00388 OPTIONAL_STATE_CONSTRUCTOR(SafetyCheckState,CheckLiberalSafetyState);
00389 
00390 void
00391 SafetyCheckState::safetyCheck(ProgramCtx* ctx)
00392 {
00393     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sid,"Safety checking");
00394 
00395     //
00396     // Performing the safety check
00397     //
00398     SafetyChecker schecker(*ctx);
00399     // check by calling the object
00400     schecker();
00401 
00402     StatePtr next(new CheckLiberalSafetyState);
00403     changeState(ctx, next);
00404 }
00405 
00406 
00407 MANDATORY_STATE_CONSTRUCTOR(CheckLiberalSafetyState);
00408 
00409 void CheckLiberalSafetyState::checkLiberalSafety(ProgramCtx* ctx)
00410 {
00411     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sid,"checking liberal safety");
00412 
00413     ctx->liberalSafetyChecker = LiberalSafetyCheckerPtr(new LiberalSafetyChecker(ctx->registry(), ctx->idb, ctx->liberalSafetyPlugins));
00414 
00415     if( ctx->config.getOption("DumpAttrGraph") ) {
00416         std::string fnamev = ctx->config.getStringOption("DebugPrefix")+"_AttrGraphVerbose.dot";
00417         LOG(INFO,"dumping verbose attribute graph to " << fnamev);
00418         std::ofstream filev(fnamev.c_str());
00419         ctx->liberalSafetyChecker->writeGraphViz(filev, true);
00420     }
00421 
00422     if (ctx->config.getOption("LiberalSafety")) {
00423         if (!ctx->liberalSafetyChecker->isDomainExpansionSafe()) {
00424             throw SyntaxError("Program is not liberally domain-expansion safe");
00425         }
00426     }
00427 
00428     StatePtr next(new CreateDependencyGraphState);
00429     changeState(ctx, next);
00430 }
00431 
00432 
00433 MANDATORY_STATE_CONSTRUCTOR(CreateDependencyGraphState);
00434 
00435 void CreateDependencyGraphState::createDependencyGraph(ProgramCtx* ctx)
00436 {
00437     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sid,"building dependency graph");
00438 
00439     DependencyGraphPtr depgraph(new DependencyGraph(*ctx, ctx->registry()));
00440     std::vector<dlvhex::ID> auxRules;
00441     depgraph->createDependencies(ctx->idb, auxRules);
00442 
00443     if( ctx->config.getOption("DumpDepGraph") ) {
00444         std::string fnamev = ctx->config.getStringOption("DebugPrefix")+"_DepGraphVerbose.dot";
00445         LOG(INFO,"dumping verbose dependency graph to " << fnamev);
00446         std::ofstream filev(fnamev.c_str());
00447         depgraph->writeGraphViz(filev, true);
00448 
00449         std::string fnamet = ctx->config.getStringOption("DebugPrefix")+"_DepGraphTerse.dot";
00450         LOG(INFO,"dumping terse dependency graph to " << fnamet);
00451         std::ofstream filet(fnamet.c_str());
00452         depgraph->writeGraphViz(filet, false);
00453     }
00454 
00455     ctx->depgraph = depgraph;
00456 
00457     StatePtr next(new OptimizeEDBDependencyGraphState);
00458     changeState(ctx, next);
00459 }
00460 
00461 
00462 OPTIONAL_STATE_CONSTRUCTOR(OptimizeEDBDependencyGraphState,CreateComponentGraphState);
00463 
00464 void
00465 OptimizeEDBDependencyGraphState::optimizeEDBDependencyGraph(ProgramCtx* ctx)
00466 {
00467     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sid,"Calling plugin optimizers");
00468 
00469     // get optimizer from each plugin
00470     bool optimized = false;
00471     BOOST_FOREACH(PluginInterfacePtr plugin, ctx->pluginContainer()->getPlugins()) {
00472         PluginOptimizerPtr optimizer = plugin->createOptimizer(*ctx);
00473         if( !optimizer )
00474             continue;
00475 
00476         LOG(PLUGIN,"got plugin optimizer from plugin " << plugin->getPluginName());
00477 
00478         optimizer->optimize(ctx->edb, ctx->depgraph);
00479         optimized = true;
00480     }
00481 
00482     if( optimized && ctx->config.getOption("DumpDepGraph") ) {
00483         std::string fnamev = ctx->config.getStringOption("DebugPrefix")+
00484             "_DepGraphOptimizedVerbose.dot";
00485         LOG(INFO,"dumping optimized verbose dependency graph to " << fnamev);
00486         std::ofstream filev(fnamev.c_str());
00487         ctx->depgraph->writeGraphViz(filev, true);
00488 
00489         std::string fnamet = ctx->config.getStringOption("DebugPrefix")+
00490             "_DepGraphOptimizedTerse.dot";
00491         LOG(INFO,"dumping optimized terse dependency graph to " << fnamet);
00492         std::ofstream filet(fnamet.c_str());
00493         ctx->depgraph->writeGraphViz(filet, false);
00494     }
00495 
00496     StatePtr next(new CreateComponentGraphState);
00497     changeState(ctx, next);
00498 }
00499 
00500 
00501 MANDATORY_STATE_CONSTRUCTOR(CreateComponentGraphState);
00502 
00503 void CreateComponentGraphState::createComponentGraph(ProgramCtx* ctx)
00504 {
00505     assert(!!ctx->depgraph && "need depgraph for building component graph");
00506     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sid,"building component graph");
00507 
00508     ComponentGraphPtr compgraph(
00509         new ComponentGraph(*ctx->depgraph, *ctx, ctx->registry()));
00510 
00511     if( ctx->config.getOption("DumpCompGraph") ) {
00512         std::string fnamev = ctx->config.getStringOption("DebugPrefix")+"_CompGraphVerbose.dot";
00513         LOG(INFO,"dumping verbose component graph to " << fnamev);
00514         std::ofstream filev(fnamev.c_str());
00515         compgraph->writeGraphViz(filev, true);
00516 
00517         std::string fnamet = ctx->config.getStringOption("DebugPrefix")+"_CompGraphTerse.dot";
00518         LOG(INFO,"dumping terse component graph to " << fnamet);
00519         std::ofstream filet(fnamet.c_str());
00520         compgraph->writeGraphViz(filet, false);
00521     }
00522 
00523     ctx->compgraph = compgraph;
00524 
00525     StatePtr next(new StrongSafetyCheckState);
00526     changeState(ctx, next);
00527 }
00528 
00529 
00530 OPTIONAL_STATE_CONSTRUCTOR(StrongSafetyCheckState,CreateEvalGraphState);
00531 
00532 void StrongSafetyCheckState::strongSafetyCheck(ProgramCtx* ctx)
00533 {
00534     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sid,"Strong safety checking");
00535 
00536     StrongSafetyChecker sschecker(*ctx);
00537     // check by calling the object
00538     sschecker();
00539 
00540     StatePtr next(new CreateEvalGraphState);
00541     changeState(ctx, next);
00542 }
00543 
00544 
00545 MANDATORY_STATE_CONSTRUCTOR(CreateEvalGraphState);
00546 
00547 void CreateEvalGraphState::createEvalGraph(ProgramCtx* ctx)
00548 {
00549     assert(!!ctx->compgraph &&
00550         "need component graph for creating evaluation graph");
00551     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sid,"creating evaluation graph");
00552 
00553     FinalEvalGraphPtr evalgraph(new FinalEvalGraph);
00554 
00555     EvalGraphBuilderPtr egbuilder;
00556     if( ctx->config.getOption("DumpEvaluationPlan") ) {
00557         egbuilder.reset(new DumpingEvalGraphBuilder(
00558             *ctx, *ctx->compgraph, *evalgraph, ctx->aspsoftware,
00559             ctx->config.getStringOption("DumpEvaluationPlanFile")));
00560     }
00561     else {
00562         egbuilder.reset(new EvalGraphBuilder(
00563             *ctx, *ctx->compgraph, *evalgraph, ctx->aspsoftware));
00564     }
00565 
00566     // dump component graph again, this time the cloned version
00567     // (it has different addresses which we might need for debugging)
00568     if( ctx->config.getOption("DumpCompGraph") ) {
00569         std::string fnamev = ctx->config.getStringOption("DebugPrefix")+"_ClonedCompGraphVerbose.dot";
00570         LOG(INFO,"dumping verbose cloned component graph to " << fnamev);
00571         std::ofstream filev(fnamev.c_str());
00572         egbuilder->getComponentGraph().writeGraphViz(filev, true);
00573 
00574         std::string fnamet = ctx->config.getStringOption("DebugPrefix")+"_ClonedCompGraphTerse.dot";
00575         LOG(INFO,"dumping terse cloned component graph to " << fnamet);
00576         std::ofstream filet(fnamet.c_str());
00577         egbuilder->getComponentGraph().writeGraphViz(filet, false);
00578     }
00579 
00580     // use configured eval heuristic
00581     assert(!!ctx->evalHeuristic && "need configured heuristic");
00582     DBGLOG(DBG,"invoking build() on eval heuristic");
00583     ctx->evalHeuristic->build(*egbuilder);
00584     // do not destruct heuristic because we might reuse it in evaluateSubprogram
00585     //DBGLOG(DBG,"destructing eval heuristic");
00586     //ctx->evalHeuristic.reset();
00587     // destruct eval graph builder
00588     egbuilder.reset();
00589 
00590     // setup final unit used to get full models
00591     WARNING("TODO if we project answer sets, or do querying, we could reduce the number of units used here!")
00592         FinalEvalGraph::EvalUnit ufinal =
00593         evalgraph->addUnit(FinalEvalGraph::EvalUnitPropertyBundle());
00594     LOG(DBG,"created virtual final unit ufinal = " << ufinal);
00595 
00596     FinalEvalGraph::EvalUnitIterator it, itend;
00597     boost::tie(it, itend) = evalgraph->getEvalUnits();
00598     for(; it != itend && *it != ufinal; ++it) {
00599         DBGLOG(DBG,"adding dependency from ufinal to unit " << *it <<
00600             " join order " << *it);
00601         // we can do this because we know that eval units
00602         // (= vertices of a vecS adjacency list) are unsigned integers
00603         evalgraph->addDependency(
00604             ufinal, *it,
00605             FinalEvalGraph::EvalUnitDepPropertyBundle(*it));
00606     }
00607 
00608     ctx->ufinal = ufinal;
00609     ctx->evalgraph = evalgraph;
00610 
00611     if( ctx->config.getOption("DumpEvalGraph") ) {
00612         std::string fnamev = ctx->config.getStringOption("DebugPrefix")+"_EvalGraphVerbose.dot";
00613         LOG(INFO,"dumping verbose eval graph to " << fnamev);
00614         std::ofstream filev(fnamev.c_str());
00615         ctx->evalgraph->writeGraphViz(filev, true);
00616 
00617         std::string fnamet = ctx->config.getStringOption("DebugPrefix")+"_EvalGraphTerse.dot";
00618         LOG(INFO,"dumping terse eval graph to " << fnamet);
00619         std::ofstream filet(fnamet.c_str());
00620         ctx->evalgraph->writeGraphViz(filet, false);
00621     }
00622 
00623     StatePtr next(new SetupProgramCtxState);
00624     changeState(ctx, next);
00625 }
00626 
00627 
00628 MANDATORY_STATE_CONSTRUCTOR(SetupProgramCtxState);
00629 
00630 void SetupProgramCtxState::setupProgramCtx(ProgramCtx* ctx)
00631 {
00632     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sid,"setupProgramCtx");
00633 
00634     // what to snapshot at first model
00635     ctx->benchmarksToSnapshotAtFirstModel.insert(std::make_pair("BenchmarkController lifetime", "time to first model"));
00636     ctx->benchmarksToSnapshotAtFirstModel.insert(std::make_pair("Grounder time", "Grounder time to first model"));
00637     ctx->benchmarksToSnapshotAtFirstModel.insert(std::make_pair("Solver time", "Solver time to first model"));
00638     ctx->benchmarksToSnapshotAtFirstModel.insert(std::make_pair("HEX grounder time", "HEX grounder time to first mdl"));
00639     ctx->benchmarksToSnapshotAtFirstModel.insert(std::make_pair("HEX solver time", "HEX solver time to first model"));
00640     ctx->benchmarksToSnapshotAtFirstModel.insert(std::make_pair("PluginAtom retrieve", "PluginAtom retr to first model"));
00641     ctx->benchmarksToSnapshotAtFirstModel.insert(std::make_pair("Candidate compatible sets", "CandCompat sets to first model"));
00642 
00643     // default model outputting callback
00644     if (ctx->modelCallbacks.size() == 0){
00645         ModelCallbackPtr asprinter(new AnswerSetPrinterCallback(*ctx));
00646         ctx->modelCallbacks.push_back(asprinter);
00647     }
00648 
00649     // setup printing of auxiliaries
00650     if( 1 == ctx->config.getOption("KeepAuxiliaryPredicates") ) {
00651         AuxPrinterPtr plainAuxPrinter(new PlainAuxPrinter(ctx->registry()));
00652         ctx->registry()->registerUserDefaultAuxPrinter(plainAuxPrinter);
00653     }
00654 
00655     // let plugins setup the program ctx (removing the default hooks is permitted)
00656     ctx->setupByPlugins();
00657 
00658     StatePtr next(new EvaluateState);
00659     changeState(ctx, next);
00660 }
00661 
00662 
00663 MANDATORY_STATE_CONSTRUCTOR(EvaluateState);
00664 
00665 namespace
00666 {
00667     typedef ModelBuilder<FinalEvalGraph>::Model Model;
00668     typedef ModelBuilder<FinalEvalGraph>::OptionalModel OptionalModel;
00669     typedef ModelBuilder<FinalEvalGraph>::MyModelGraph MyModelGraph;
00670 
00671     void snapShotBenchmarking(ProgramCtx& ctx) {
00672         static bool alreadyDidIt = false;
00673 
00674         // do this really only once in the lifetime of a dlvhex binary
00675         if( alreadyDidIt ) return;
00676         alreadyDidIt = true;
00677 
00678         std::map<std::string, std::string>::const_iterator snapit;
00679         for(snapit = ctx.benchmarksToSnapshotAtFirstModel.begin();
00680         snapit != ctx.benchmarksToSnapshotAtFirstModel.end(); ++snapit) {
00681             dlvhex::benchmark::BenchmarkController::Instance().snapshot(snapit->first, snapit->second);
00682         }
00683     }
00684 
00685     ModelBuilder<FinalEvalGraph>& createModelBuilder(ProgramCtx* ctx) {
00686         LOG(INFO,"creating model builder");
00687         {
00688             DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidmb, "create model builder");
00689             ModelBuilderConfig<FinalEvalGraph> cfg(*ctx->evalgraph);
00690             cfg.redundancyElimination = true;
00691             cfg.constantSpace = ctx->config.getOption("UseConstantSpace") == 1;
00692             ctx->modelBuilder = ModelBuilderPtr(ctx->modelBuilderFactory(cfg));
00693         }
00694         return *ctx->modelBuilder;
00695     }
00696 
00697     bool callModelCallbacks(ProgramCtx* ctx, AnswerSetPtr answerset) {
00698         // process all answer sets via callback mechanism
00699         // processing a model this way gives it as a result, so we snapshot the first model here
00700         snapShotBenchmarking(*ctx);
00701 
00702         bool abort = false;
00703         BOOST_FOREACH(ModelCallbackPtr mcb, ctx->modelCallbacks) {
00704             bool aborthere = !(*mcb)(answerset);
00705             abort |= aborthere;
00706             if( aborthere )
00707                 LOG(DBG,"callback '" << typeid(*mcb).name() << "' signalled abort");
00708         }
00709         return abort;
00710     }
00711 
00712     // evaluate the hex program to find the optimum
00713     // (this will only be used for OptimizationTwoStep because in other cases it might not yield correct results)
00714     // * enumerate models better than current cost
00715     // * ignore model limits/callbacks
00716     // * remember last found model and its cost
00717     // * when not finding model, set current cost to last found model
00718     // returns the first optimal answer set or NULL if there is no answer set
00719     AnswerSetPtr evaluateFindOptimum(ProgramCtx* ctx) {
00720 
00721         DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sid, "evaluateFindOptimum");
00722         DLVHEX_BENCHMARK_REGISTER(sidgetnextmodel, "evaluateFindOptimum::gNM");
00723         DBGLOG_SCOPE(DBG,"eFO",false);
00724         DBGLOG(DBG,"eFO = evaluateFindOptimum");
00725 
00726         assert(ctx->config.getOption("OptimizationTwoStep") == 1);
00727         AnswerSetPtr lastAnswerSet;
00728         ModelBuilder<FinalEvalGraph>& mb = createModelBuilder(ctx);
00729         OptionalModel om;
00730         do {
00731             DBGLOG(DBG,"requesting omodel");
00732             {
00733                 DLVHEX_BENCHMARK_SCOPE(sidgetnextmodel);
00734                 om = mb.getNextIModel(ctx->ufinal);
00735             }
00736             if( !!om ) {
00737                 Model m = om.get();
00738                 InterpretationConstPtr interpretation =
00739                     mb.getModelGraph().propsOf(m).interpretation;
00740 
00741                 // if the program is empty, we may get a NULL interpretation
00742                 if( !interpretation ) {
00743                     assert(mb.getModelGraph().propsOf(m).dummy == true);
00744                     interpretation.reset(new Interpretation(ctx->registry()));
00745                 }
00746 
00747                 AnswerSetPtr answerset(new AnswerSet(ctx->registry()));
00748                 // copy interpretation! (we and callbacks might modify it after returning from this method)
00749                 answerset->interpretation->getStorage() = interpretation->getStorage();
00750                 answerset->computeWeightVector();
00751                 LOG(INFO, "new global best weight vector: " << printvector(answerset->getWeightVector()) << ", old best: " << printvector(ctx->currentOptimum));
00752                 assert(ctx->currentOptimum.empty() || answerset->strictlyBetterThan(ctx->currentOptimum));
00753                 ctx->currentOptimum = answerset->getWeightVector();
00754                 // if we have at least one weight we need to complete the vector
00755                 // in order to obtain bounds for all levels
00756                 // (if we do not do this, clasp will not set a boud if we find a cost-free model)
00757                 // TODO set currentOptimumRelevantLevels not in ClaspSolver but in WeakPlugin during rewriting (should be possible!)
00758                 while (ctx->currentOptimum.size() < (ctx->currentOptimumRelevantLevels+1))
00759                     ctx->currentOptimum.push_back(0);
00760                 lastAnswerSet = answerset;
00761             }
00762             // exit if we get no model
00763             // if we get a model with zero cost, the next iteration will set 0 as bound in clasp, so no further model will be found
00764         } while( !!om );
00765         // we got no model so we left the loop:
00766         // * either there never was any model with any weight
00767         // * or we got models and found the optimum (ctx->currentOptimum) and lastAnswerSet is the first optimal one
00768         // our caller will handle these cases
00769         DBGLOG(DBG,"returning answer set " << reinterpret_cast<void*>(lastAnswerSet.get()));
00770         return lastAnswerSet;
00771     }
00772 
00773     // evaluate the hex program
00774     // * enumerate models
00775     // * honor model limits
00776     // * including model callbacks
00777     // * including final callbacks
00778     void evaluateOnce(ProgramCtx* ctx) {
00779 
00780         DLVHEX_BENCHMARK_REGISTER(sidgetnextmodel, "evaluate::get next model");
00781         DBGLOG_SCOPE(DBG,"eO",false);
00782         DBGLOG(DBG,"eO = evaluateOnce");
00783 
00784         // this implementation requires that there is no optimization OR
00785         // that the optimal cost has been found and set and that we use two-stop optimization mode
00786         assert(ctx->config.getOption("Optimization") == 0 ||
00787             (!ctx->currentOptimum.empty() && ctx->config.getOption("OptimizationTwoStep") == 2));
00788 
00789         ModelBuilder<FinalEvalGraph>& mb = createModelBuilder(ctx);
00790         unsigned mcount = 0;
00791         bool abort = false;
00792         const unsigned mcountLimit = ctx->config.getOption("NumberOfModels");
00793         OptionalModel om;
00794         do {
00795             DBGLOG(DBG,"requesting imodel");
00796             {
00797                 DLVHEX_BENCHMARK_SCOPE(sidgetnextmodel);
00798                 om = mb.getNextIModel(ctx->ufinal);
00799             }
00800             if( !!om ) {
00801                 Model m = om.get();
00802                 InterpretationConstPtr interpretation =
00803                     mb.getModelGraph().propsOf(m).interpretation;
00804 
00805                 // if the program is empty, we may get a NULL interpretation
00806                 if( !interpretation ) {
00807                     assert(mb.getModelGraph().propsOf(m).dummy == true);
00808                     interpretation.reset(new Interpretation(ctx->registry()));
00809                 }
00810 
00811                 DBGLOG(DBG,"got model#" << mcount << ":" << *interpretation);
00812 
00813                 // model callbacks
00814                 AnswerSetPtr answerset(new AnswerSet(ctx->registry()));
00815                 // copy interpretation! (we and callbacks might modify it)
00816                 answerset->interpretation->getStorage() = interpretation->getStorage();
00817                 answerset->computeWeightVector();
00818                 LOG(DBG, "weight vector of this answer set: " << printvector(answerset->getWeightVector()));
00819                 // TODO this assertion should be done, but only if optimizing and perhaps even then we might have vector length difference problems
00820                 //assert( ctx->currentOptimum == answerset->getWeightVector() );
00821 
00822                 // add EDB if configured that way
00823                 if( !ctx->config.getOption("NoFacts") )
00824                     answerset->interpretation->getStorage() |= ctx->edb->getStorage();
00825 
00826                 abort |= callModelCallbacks(ctx, answerset);
00827                 mcount++;
00828             }
00829         } while( !!om && !abort && (mcountLimit == 0 || mcount < mcountLimit) );
00830 
00831         LOG(INFO,"got " << mcount << " models");
00832         if( abort ) {
00833             LOG(INFO,"model building was aborted by callback");
00834         }
00835         else {
00836             if( mcountLimit == 0 ) {
00837                 LOG(INFO,"model building finished after enumerating all models");
00838             }
00839             else {
00840                 LOG(INFO,"model building finished after a maximum of " << mcountLimit << " models");
00841             }
00842         }
00843     }
00844 
00845     // evaluate the hex program using naive optimization
00846     // * enumerate all models of a certain cost or better
00847     //   store all models of the currently known best cost
00848     //   until no more models are found, then output
00849     // * during output:
00850     //   * honor model limits
00851     //   * call model callbacks
00852     // * then call  final callbacks
00853     void evaluateOnceExpspace(ProgramCtx* ctx) {
00854 
00855         DLVHEX_BENCHMARK_REGISTER(sidgetnextmodel, "evaluate::get next model");
00856         DBGLOG_SCOPE(DBG,"eOE",false);
00857         DBGLOG(DBG,"eOE = evaluateOnceExpspace");
00858 
00859         // this implementation should only be used for naive optimization
00860         assert(ctx->config.getOption("Optimization") == 1 &&
00861             ctx->config.getOption("OptimizationTwoStep") == 0 /*&&
00862             ctx->config.getOption("OptimizationByDlvhex") == 1*/);
00863         ModelBuilder<FinalEvalGraph>& mb = createModelBuilder(ctx);
00864         const unsigned mcountLimit = ctx->config.getOption("NumberOfModels");
00865         unsigned mcount = 0;
00866         bool abort = false;
00867         std::list<AnswerSetPtr> bestModels;
00868         OptionalModel om;
00869         do {
00870             DBGLOG(DBG,"requesting imodel");
00871             {
00872                 DLVHEX_BENCHMARK_SCOPE(sidgetnextmodel);
00873                 om = mb.getNextIModel(ctx->ufinal);
00874             }
00875             if( !!om ) {
00876                 Model m = om.get();
00877                 InterpretationConstPtr interpretation =
00878                     mb.getModelGraph().propsOf(m).interpretation;
00879                 // if the program is empty, we may get a NULL interpretation
00880                 if( !interpretation ) {
00881                     assert(mb.getModelGraph().propsOf(m).dummy == true);
00882                     interpretation.reset(new Interpretation(ctx->registry()));
00883                 }
00884 
00885                 DBGLOG(DBG,"got model#" << mcount << ":" << *interpretation);
00886 
00887                 // model callbacks
00888                 AnswerSetPtr answerset(new AnswerSet(ctx->registry()));
00889                 // copy interpretation! (we and callbacks might modify it)
00890                 answerset->interpretation->getStorage() = interpretation->getStorage();
00891                 answerset->computeWeightVector();
00892                 LOG(DBG, "weight vector of this answer set: " << printvector(answerset->getWeightVector()));
00893 
00894                 // add EDB if configured that way
00895                 if( !ctx->config.getOption("NoFacts") )
00896                     answerset->interpretation->getStorage() |= ctx->edb->getStorage();
00897 
00898                 // cost check
00899                 // compare the solution to the best known model
00900                 // 3 Options:
00901                 // - ctx->config.getOption("OptimizationByDlvhex"):
00902                 //   Let dlvhex manage optimization. Setting this option to true suffices to get the correct result.
00903                 // - ctx->config.getOption("OptimizationFilterNonOptimal"):
00904                 //   Avoid that non-optimal models are printed before the best model appears; option is only relevant if "OptimizationByDlvhex" is also set.
00905                 // - ctx->config.getOption("OptimizationByBackend"):
00906                 //   Let solver backends manage optimization (if the specific backends supports it).
00907                 //   This option is optional but might prune the search space already in single units while dlvhex can optimize only after the final models have been found.
00908                                  // betterThan does not necessarily mean strictly better, i.e., it includes solutions of the same quality!
00909                 bool equalOrBetter = (ctx->currentOptimum.size() == 0 || answerset->betterThan(ctx->currentOptimum));
00910 
00911                 // keep track of the current optimum
00912                 if( equalOrBetter ) {
00913                     ctx->currentOptimum = answerset->getWeightVector();
00914                     LOG(DBG, "Current global optimum (equalOrBetter = True): " << printvector(answerset->getWeightVector()));
00915                 }
00916 
00917                 if (ctx->config.getOption("OptimizationByDlvhex")){
00918                     if( !equalOrBetter ) continue;
00919 
00920                     // in this block we do not need to count models as we need to enumerate all of them
00921                     // only afterwards the requested number of best models can be output
00922 
00923                     // is there a previous model and the new model is (strictly!) better than the best known one?
00924                     if( !bestModels.empty() && !bestModels.front()->betterThan(answerset->getWeightVector()) ) {
00925                         // new model is better than all previous ones --> clear cache
00926                         LOG(DBG, "clearing bestModels because new model is strictly better");
00927                         bestModels.clear();
00928                     }
00929 
00930                     // also show some non-optimal models?
00931                     if( ctx->config.getOption("OptimizationFilterNonOptimal") == 0 ) {
00932                         // yes: output model immediately
00933                         abort |= callModelCallbacks(ctx, answerset);
00934                         mcount++;
00935                     }else{
00936                         // store this one in cache and decide at the end upon optimality
00937                         LOG(DBG, "recording answer set in bestModels: " << *answerset);
00938                         bestModels.push_back(answerset);
00939                     }
00940                 }else{
00941                     abort |= callModelCallbacks(ctx, answerset);
00942                     mcount++;
00943                 }
00944                 if (mcountLimit != 0 && mcount >= mcountLimit) abort = true;
00945             }
00946         }
00947         while( !!om && !abort );
00948 
00949         // process cached models
00950         BOOST_FOREACH(AnswerSetPtr answerset, bestModels) {
00951             mcount++;
00952             abort |= callModelCallbacks(ctx, answerset);
00953             // respect model count limit for cached models
00954             if( abort || (mcountLimit != 0 && mcount >= mcountLimit) )
00955                 break;
00956         }
00957 
00958         LOG(INFO,"got " << mcount << " models");
00959         if( abort ) {
00960             LOG(INFO,"model building was aborted by callback");
00961         }
00962         else {
00963             if( mcountLimit == 0 ) {
00964                 LOG(INFO,"model building finished after enumerating all models");
00965             }
00966             else {
00967                 LOG(INFO,"model building finished after enumerating a maximum of " << mcountLimit << " models");
00968             }
00969         }
00970     }
00971 }
00972 
00973 
00974 void
00975 EvaluateState::evaluate(ProgramCtx* ctx)
00976 {
00977     do {
00978         if( ctx->config.getOption("Optimization") ) {
00979             if( ctx->config.getOption("OptimizationTwoStep") > 0 ) {
00980                 // special optimization method (see dlvhex.cpp)
00981                 AnswerSetPtr firstBest = evaluateFindOptimum(ctx);
00982                 if( !!firstBest ) {
00983                     LOG(INFO,"first optimal answer set: " << *firstBest);
00984                     // enumerate all answer sets equal to previously found optimum
00985                     // TODO if we just want to find one answer set, do not call evaluateOnce but directly use firstBest
00986                     ctx->config.setOption("OptimizationTwoStep", 2);
00987                     evaluateOnce(ctx);
00988                 }
00989             }
00990             else {
00991                 evaluateOnceExpspace(ctx);
00992             }
00993         }
00994         else {
00995             // no optimization required
00996             evaluateOnce(ctx);
00997         }
00998 
00999         // call final callbacks
01000         BOOST_FOREACH(FinalCallbackPtr fcb, ctx->finalCallbacks) {
01001             DBGLOG(DBG,"calling final callback " << printptr(fcb));
01002             (*fcb)();
01003         }
01004 
01005         // TODO this repetition business should be solved in a more state-machine-ish way
01006         // if repetition counter is set, decrease it and repeat
01007         // this value might change in model/final callbacks, so we need to load it again here
01008         unsigned repeatEvaluation = ctx->config.getOption("RepeatEvaluation");
01009         if( repeatEvaluation > 0 ) {
01010             LOG(INFO,"repeating evaluation because RepeatEvaluation=" << repeatEvaluation);
01011             ctx->config.setOption("RepeatEvaluation", repeatEvaluation-1);
01012         } else
01013         break;
01014     } while(true);
01015 
01016     //mb.printEvalGraphModelGraph(std::cerr);
01017 
01018     StatePtr next(new PostProcessState);
01019     changeState(ctx, next);
01020 }
01021 
01022 
01023 MANDATORY_STATE_CONSTRUCTOR(PostProcessState);
01024 
01025 void PostProcessState::postProcess(ProgramCtx* ctx)
01026 {
01027     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sid,"postProcess");
01028 
01029     // cleanup some stuff that is better not automatically destructed
01030     DBGLOG(DBG,"usage count of model builder before reset is " <<
01031         ctx->modelBuilder.use_count());
01032     ctx->modelBuilder.reset();
01033 
01034     // use base State class with no failureState -> calling it will always throw an exception
01035     boost::shared_ptr<State> next(new State);
01036     changeState(ctx, next);
01037 
01038     if( ctx->config.getOption("BenchmarkEAstderr") == 1 ) {
01039         benchmark::BenchmarkController& bmc = benchmark::BenchmarkController::Instance();
01040         //benchmark::ID eeval = bmc.getInstrumentationID("evaluate external atom");
01041         benchmark::ID eeval = bmc.getInstrumentationID("PluginAtom retrieve");
01042         const benchmark::BenchmarkController::Stat& stat = bmc.getStat(eeval);
01043         std::cerr << stat.count << " ";
01044         bmc.printInSecs(std::cerr, stat.duration, 3);
01045         std::cerr << std::endl;
01046     }
01047     if( ctx->config.getOption("DumpStats") ) {
01048         // dump number of ground atoms, number of rules (in registry)
01049         // dump certain time stats
01050         benchmark::BenchmarkController& bmc = benchmark::BenchmarkController::Instance();
01051         unsigned noAtoms = 0;
01052         unsigned noRules = 0;
01053         if( ctx && ctx->registry() ) {
01054             noAtoms = ctx->registry()->ogatoms.getSize();
01055             noRules = ctx->registry()->rules.getSize();
01056         }
01057 
01058         const char* overallName = "BenchmarkController lifetime";
01059         benchmark::ID overall = bmc.getInstrumentationID(overallName);
01060         bmc.stop(overall);
01061         std::cerr << "STATS;ogatoms;" << noAtoms << ";rules;" << noRules;
01062         //std::cerr << ";plain_mg;" << bmc.duration("genuine plain mg construction", 3);
01063         //std::cerr << ";gnc_mg;" << bmc.duration("genuine g&c mg construction", 3);
01064         std::cerr << ";grounder;" << bmc.duration("Grounder time", 3);
01065         std::cerr << ";solver;" << bmc.duration("Solver time", 3);
01066         std::cerr << ";overall;" << bmc.duration(overallName, 3);
01067         std::cerr << std::endl;
01068     }
01069 }
01070 
01071 
01072 DLVHEX_NAMESPACE_END
01073 
01074 // vim:tabstop=4:shiftwidth=4:noexpandtab:
01075 
01076 
01077 // vim:expandtab:ts=4:sw=4:
01078 // mode: C++
01079 // End: