dlvhex
2.5.0
|
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: