dlvhex  2.5.0
src/GenuineWellfoundedModelGenerator.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 
00034 #define DLVHEX_BENCHMARK
00035 
00036 #include "dlvhex2/GenuineWellfoundedModelGenerator.h"
00037 #include "dlvhex2/Logger.h"
00038 #include "dlvhex2/Registry.h"
00039 #include "dlvhex2/Printer.h"
00040 #include "dlvhex2/ASPSolver.h"
00041 #include "dlvhex2/ProgramCtx.h"
00042 #include "dlvhex2/PluginInterface.h"
00043 #include "dlvhex2/Benchmarking.h"
00044 #include "dlvhex2/GenuineSolver.h"
00045 
00046 #include <boost/foreach.hpp>
00047 
00048 DLVHEX_NAMESPACE_BEGIN
00049 
00050 GenuineWellfoundedModelGeneratorFactory::GenuineWellfoundedModelGeneratorFactory(
00051 ProgramCtx& ctx,
00052 const ComponentInfo& ci,
00053 ASPSolverManager::SoftwareConfigurationPtr externalEvalConfig):
00054 BaseModelGeneratorFactory(),
00055 externalEvalConfig(externalEvalConfig),
00056 ctx(ctx),
00057 ci(ci),
00058 outerEatoms(ci.outerEatoms),
00059 innerEatoms(ci.innerEatoms),
00060 idb(),
00061 xidb()
00062 {
00063     RegistryPtr reg = ctx.registry();
00064 
00065     // this model generator can handle:
00066     // components with outer eatoms
00067     // components with inner eatoms
00068     // components with inner rules
00069     // components with inner constraints
00070     // iff all inner eatoms are monotonic and there are no negative dependencies within idb
00071 
00072     // copy rules and constraints to idb
00073     // TODO we do not really need this except for debugging (tiny optimization possibility)
00074     idb.reserve(ci.innerRules.size() + ci.innerConstraints.size());
00075     idb.insert(idb.end(), ci.innerRules.begin(), ci.innerRules.end());
00076     idb.insert(idb.end(), ci.innerConstraints.begin(), ci.innerConstraints.end());
00077 
00078     // create program for domain exploration
00079     if (ctx.config.getOption("LiberalSafety")) {
00080         // add domain predicates for all external atoms which are necessary to establish liberal domain-expansion safety
00081         // and extract the domain-exploration program from the IDB
00082         addDomainPredicatesAndCreateDomainExplorationProgram(ci, ctx, idb, deidb, deidbInnerEatoms, outerEatoms);
00083     }
00084 
00085     // transform original innerRules and innerConstraints to xidb with only auxiliaries
00086     xidb.reserve(idb.size());
00087     std::back_insert_iterator<std::vector<ID> > inserter(xidb);
00088     std::transform(idb.begin(), idb.end(),
00089         inserter, boost::bind(
00090         &GenuineWellfoundedModelGeneratorFactory::convertRule, this, ctx, _1));
00091 
00092     // this calls print()
00093     DBGLOG(DBG,"GenuineWellfoundedModelGeneratorFactory(): " << *this);
00094 }
00095 
00096 
00097 std::ostream& GenuineWellfoundedModelGeneratorFactory::print(
00098 std::ostream& o) const
00099 {
00100     RawPrinter printer(o, ctx.registry());
00101     if( !outerEatoms.empty() ) {
00102         o << " outer Eatoms={";
00103         printer.printmany(outerEatoms,",");
00104         o << "}";
00105     }
00106     if( !innerEatoms.empty() ) {
00107         o << " inner Eatoms={";
00108         printer.printmany(innerEatoms,",");
00109         o << "}";
00110     }
00111     if( !idb.empty() ) {
00112         o << " idb={";
00113         printer.printmany(idb,"\n");
00114         o << "}";
00115     }
00116     if( !xidb.empty() ) {
00117         o << " xidb={";
00118         printer.printmany(xidb,"\n");
00119         o << "}";
00120     }
00121     return o;
00122 }
00123 
00124 
00125 GenuineWellfoundedModelGenerator::GenuineWellfoundedModelGenerator(
00126 Factory& factory,
00127 InterpretationConstPtr input):
00128 BaseModelGenerator(input),
00129 factory(factory), firstcall(true)
00130 {
00131 }
00132 
00133 
00134 GenuineWellfoundedModelGenerator::~GenuineWellfoundedModelGenerator()
00135 {
00136 }
00137 
00138 
00139 GenuineWellfoundedModelGenerator::InterpretationPtr
00140 GenuineWellfoundedModelGenerator::generateNextModel()
00141 {
00142     RegistryPtr reg = factory.ctx.registry();
00143 
00144     if( !firstcall ) {
00145         return InterpretationPtr();
00146     }
00147     else {
00148         // we need to create currentResults
00149         firstcall = false;
00150 
00151         // create new interpretation as copy
00152         Interpretation::Ptr postprocessedInput;
00153         if( input == 0 ) {
00154             // empty construction
00155             postprocessedInput.reset(new Interpretation(reg));
00156         }
00157         else {
00158             // copy construction
00159             postprocessedInput.reset(new Interpretation(*input));
00160         }
00161 
00162         // augment input with edb
00163         postprocessedInput->add(*factory.ctx.edb);
00164 
00165         // remember which facts we have to remove from each output interpretation
00166         InterpretationConstPtr mask(new Interpretation(*postprocessedInput));
00167 
00168         // manage outer external atoms
00169         if( !factory.outerEatoms.empty() ) {
00170             DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidhexground, "HEX grounder time (GenuineWfMG)");
00171             DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidhexground2, "HEX grounder time");
00172             // augment input with result of external atom evaluation
00173             // use newint as input and as output interpretation
00174             IntegrateExternalAnswerIntoInterpretationCB cb(postprocessedInput);
00175             evaluateExternalAtoms(factory.ctx, factory.outerEatoms, postprocessedInput, cb);
00176             DLVHEX_BENCHMARK_REGISTER(sidcountexternalatomcomps,
00177                 "outer eatom computations");
00178             DLVHEX_BENCHMARK_COUNT(sidcountexternalatomcomps,1);
00179 
00180             assert(!factory.xidb.empty() && "the wellfounded model generator is not required for non-idb components! (use plain)");
00181         }
00182 
00183         // compute extensions of domain predicates and add it to the input
00184         if (factory.ctx.config.getOption("LiberalSafety")) {
00185             InterpretationConstPtr domPredictaesExtension = computeExtensionOfDomainPredicates(factory.ci, factory.ctx, postprocessedInput, factory.deidb, factory.deidbInnerEatoms);
00186             postprocessedInput->add(*domPredictaesExtension);
00187         }
00188 
00189         // now we have postprocessed input in postprocessedInput
00190         DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidwfsolve, "wellfounded solver loop");
00191 
00192         WARNING("make wellfounded iteration limit configurable")
00193             unsigned limit = 1000;
00194         bool inconsistent = false;
00195 
00196         // we store two interpretations "ints" and
00197         // one "src" integer for the current source interpretation
00198         //
00199         // the loop below uses ints[current] as source and stores into ints[1-current]
00200         // then current = 1 - current
00201         std::vector<InterpretationPtr> ints(2);
00202         unsigned current = 0;
00203         // the following creates a copy! (we need the postprocessedInput later)
00204         ints[0] = InterpretationPtr(new Interpretation(*postprocessedInput));
00205         // the following creates a copy!
00206         ints[1] = InterpretationPtr(new Interpretation(*postprocessedInput));
00207 
00208         //for (int k = 0; k < 10; k++){
00209         do {
00210             InterpretationPtr src = ints[current];
00211             InterpretationPtr dst = ints[1-current];
00212             DBGLOG(DBG,"starting loop with source" << *src);
00213             DBGLOG(DBG,"starting loop with dst" << *dst);
00214 
00215             // evaluate inner external atoms
00216             IntegrateExternalAnswerIntoInterpretationCB cb(dst);
00217             {
00218                 DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidhexsolve, "HEX solver time (inner EAs GenuineWfMG)");
00219                 DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidhexsolve2, "HEX solver time");
00220                 evaluateExternalAtoms(factory.ctx, factory.innerEatoms, src, cb);
00221             }
00222             DBGLOG(DBG,"after evaluateExternalAtoms: dst is " << *dst);
00223 
00224             // solve program
00225             {
00226                 // we don't use a mask here!
00227                 // -> we receive all facts
00228                 OrdinaryASPProgram program(reg, factory.xidb, dst, factory.ctx.maxint);
00229                 GenuineSolverPtr solver = GenuineSolver::getInstance(factory.ctx, program);
00230 
00231                 // Search space pruning: the idea is to set the current global optimum as upper limit in the solver instance (of this unit) to eliminate interpretations with higher costs.
00232                 // Note that this optimization is conservative such that the algorithm remains complete even when the program is split. Because costs can be only positive,
00233                 // if the costs of a partial model are greater than the current global optimum then also any completion of this partial model (by combining it with other units)
00234                 // would be non-optimal.
00235                 if (factory.ctx.config.getOption("OptimizationByBackend")) solver->setOptimum(factory.ctx.currentOptimum);
00236 
00237                 // there must be either no or exactly one answer set
00238                 InterpretationConstPtr model = solver->getNextModel();
00239 
00240                 if( model == InterpretationPtr() ) {
00241                     LOG(DBG,"got no answer set -> inconsistent");
00242                     inconsistent = true;
00243                     break;
00244                 }
00245                 InterpretationConstPtr model2 = solver->getNextModel();
00246                 if( model2 != InterpretationConstPtr() )
00247                     throw FatalError("got more than one model in Wellfounded model generator -> use other model generator!");
00248 
00249                 // cheap exchange -> thisret1 will then be free'd
00250                 {
00251                     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidhexsolve, "HEX solver time (cp mdl GenuineWfMG)");
00252                     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidhexsolve2, "HEX solver time");
00253                     dst->getStorage() = model->getStorage();
00254                 }
00255                 DBGLOG(DBG,"after evaluating ASP: dst is " << *dst);
00256                 DBGLOG(DBG, "Final Statistics:" << std::endl << solver->getStatistics());
00257             }
00258 
00259             //          reg->eliminateHomomorphicAtoms(dst, src);
00260 
00261             // check whether new interpretation is superset of old one
00262             // break if they are equal (i.e., if the fixpoint is reached)
00263             // error if new one is smaller (i.e., fixpoint is not allowed)
00264             // (TODO do this error check, and do it only in debug mode)
00265             {
00266                 DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidhexsolve, "HEX solver time (fp check GenuineWfMG)");
00267                 DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidhexsolve2, "HEX solver time");
00268                 int cmpresult = dst->getStorage().compare(src->getStorage());
00269                 if( cmpresult == 0 ) {
00270                     DBGLOG(DBG,"reached fixpoint");
00271                     break;
00272                 }
00273             }
00274 
00275             // switch interpretations
00276             current = 1 - current;
00277             limit--;
00278             // loop if limit is not reached
00279         }while( limit != 0 && !inconsistent );
00280         /*
00281         if (inconsistent) break;
00282         current=0;
00283         reg->freezeNullTerms(ints[0]);
00284         reg->freezeNullTerms(ints[1]);
00285         }
00286         */
00287         if( limit == 0 )
00288             throw FatalError("reached wellfounded limit!");
00289 
00290         if( inconsistent ) {
00291             DBGLOG(DBG,"leaving loop with result 'inconsistent'");
00292             return InterpretationPtr();
00293         }
00294         else {
00295             DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidhexsolve, "HEX solver time (make result GenuineWfMG)");
00296             DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidhexsolve2, "HEX solver time");
00297             // does not matter which one we take, they are equal
00298             InterpretationPtr result = ints[0];
00299             DBGLOG(DBG,"leaving loop with result " << *result);
00300 
00301             // remove mask from result!
00302             result->getStorage() -= mask->getStorage();
00303             DBGLOG(DBG,"after removing input facts: result is " << *result);
00304 
00305             // return single answer set (there can only be one)
00306             return result;
00307         }
00308     }
00309 }
00310 
00311 
00312 DLVHEX_NAMESPACE_END
00313 
00314 // vim:expandtab:ts=4:sw=4:
00315 // mode: C++
00316 // End: