void declareDecomposition(const Decomposition& decomposition, std::ostream& out) { out << "% Decomposition facts" << std::endl; out << "currentNode(" << decomposition.getNode().getGlobalId() << ")." << std::endl; for(const auto& v : decomposition.getNode().getBag()) { out << "bag(" << decomposition.getNode().getGlobalId() << ',' << v << "). "; out << "current(" << v << ")." << std::endl; } out << "#const numChildNodes=" << decomposition.getChildren().size() << '.' << std::endl; if(decomposition.getChildren().empty()) out << "initial." << std::endl; else { for(const auto& child : decomposition.getChildren()) { out << "childNode(" << child->getNode().getGlobalId() << ")." << std::endl; for(const auto& v : child->getNode().getBag()) { out << "bag(" << child->getNode().getGlobalId() << ',' << v << "). "; out << "-introduced(" << v << ")." << std::endl; // Redundant } } } if(decomposition.isRoot()) out << "final." << std::endl; if(decomposition.isPostJoinNode()) out << "postJoin." << std::endl; // Redundant predicates for convenience... out << "introduced(X) :- current(X), not -introduced(X)." << std::endl; out << "removed(X) :- childNode(N), bag(N,X), not current(X)." << std::endl; }
void DebugMachineReadable::solverInvocationResult(const Decomposition& decompositionNode, const ItemTree* result) { const auto id = decompositionNode.getNode().getGlobalId(); if(result) { std::cout << "% Facts describing the resulting item tree at node " << id << std::endl; std::ostringstream rootItemSetName; rootItemSetName << 'n' << id; solver::asp::Solver::declareItemTree(std::cout, result, false, id, rootItemSetName.str()); std::cout << std::endl; std::cout << "% Memory locations of the item tree nodes at decomposition node " << id << " (not passed to ASP)" << std::endl; declareItemTreeNodeMemoryAddresses(std::cout, result, rootItemSetName.str()); std::cout << std::endl; std::cout << "% Extension pointers at decomposition node " << id << " (not passed to ASP)" << std::endl; declareExtensionPointers(std::cout, result, rootItemSetName.str()); std::cout << std::endl; std::cout << "% (Derived) costs of non-leaf nodes of the item tree at decomposition node " << id << " (not passed to ASP)" << std::endl; declareDerivedCosts(std::cout, result, rootItemSetName.str()); std::cout << std::endl; } else std::cout << "% Item tree of node " << id << " is empty." << std::endl; }
LeafSolver::LeafSolver(const Decomposition& decomposition, const Application& app) : ::Solver(decomposition, app) { if(decomposition.getNode().getBag().empty() == false) throw std::runtime_error("ASP solver requires empty leaves"); }
Solver::Solver(const Decomposition& decomposition, const Application& app, const std::vector<std::string>& encodingFiles, bool reground, BranchAndBoundLevel bbLevel) : ::LazySolver(decomposition, app, bbLevel) , reground(reground) , encodingFiles(encodingFiles) { Gringo::message_printer()->disable(Gringo::W_ATOM_UNDEFINED); if(!reground) { // Set up ASP solver config.solve.numModels = 0; Clasp::Asp::LogicProgram& claspProgramBuilder = static_cast<Clasp::Asp::LogicProgram&>(clasp.startAsp(config, true)); // TODO In leaves updates might not be necessary. struct LazyGringoOutputProcessor : GringoOutputProcessor { LazyGringoOutputProcessor(Solver* s, Clasp::Asp::LogicProgram& prg) : GringoOutputProcessor(prg), self(s) { } void storeAtom(unsigned int atomUid, Gringo::Value v) override { const std::string& n = *v.name(); if(n == "childItem") { ASP_CHECK(v.args().size() == 1, "'childItem' predicate does not have arity 1"); std::ostringstream argument; v.args().front().print(argument); self->itemsToLitIndices.emplace(String(argument.str()), self->literals.size()); self->literals.push_back(Clasp::posLit(atomUid)); } else if(n == "childAuxItem") { ASP_CHECK(v.args().size() == 1, "'childAuxItem' predicate does not have arity 1"); std::ostringstream argument; v.args().front().print(argument); self->auxItemsToLitIndices.emplace(String(argument.str()), self->literals.size()); self->literals.push_back(Clasp::posLit(atomUid)); } GringoOutputProcessor::storeAtom(atomUid, v); } Solver* self; } gringoOutput(this, claspProgramBuilder); std::unique_ptr<Gringo::Output::OutputBase> out(new Gringo::Output::OutputBase({}, gringoOutput)); Gringo::Input::Program program; asp_utils::DummyGringoModule module; Gringo::Scripts scripts(module); Gringo::Defines defs; Gringo::Input::NongroundProgramBuilder gringoProgramBuilder(scripts, program, *out, defs); Gringo::Input::NonGroundParser parser(gringoProgramBuilder); // Input: Induced subinstance std::unique_ptr<std::stringstream> instanceInput(new std::stringstream); asp_utils::induceSubinstance(*instanceInput, app.getInstance(), decomposition.getNode().getBag()); app.getPrinter().solverInvocationInput(decomposition, instanceInput->str()); // Input: Decomposition std::unique_ptr<std::stringstream> decompositionInput(new std::stringstream); asp_utils::declareDecomposition(decomposition, *decompositionInput); app.getPrinter().solverInvocationInput(decomposition, decompositionInput->str()); // Pass input to ASP solver for(const auto& file : encodingFiles) parser.pushFile(std::string(file)); parser.pushStream("<instance>", std::move(instanceInput)); parser.pushStream("<decomposition>", std::move(decompositionInput)); parser.parse(); // Ground program.rewrite(defs); program.check(); if(Gringo::message_printer()->hasError()) throw std::runtime_error("Grounding stopped because of errors"); auto gPrg = program.toGround(out->domains); Gringo::Ground::Parameters params; params.add("base", {}); gPrg.ground(params, scripts, *out); params.clear(); // Set value of external atoms to free for(const auto& p : literals) claspProgramBuilder.freeze(p.var(), Clasp::value_free); // Finalize ground program and create solver literals claspProgramBuilder.endProgram(); // Map externals to their solver literals for(auto& p : literals) { p = claspProgramBuilder.getLiteral(p.var()); assert(!p.watched()); // Literal must not be watched } for(const auto& atom : gringoOutput.getItemAtomInfos()) itemAtomInfos.emplace_back(ItemAtomInfo(atom, claspProgramBuilder)); for(const auto& atom : gringoOutput.getAuxItemAtomInfos()) auxItemAtomInfos.emplace_back(AuxItemAtomInfo(atom, claspProgramBuilder)); // for(const auto& atom : gringoOutput->getCurrentCostAtomInfos()) // currentCostAtomInfos.emplace_back(CurrentCostAtomInfo(atom, claspProgramBuilder)); // for(const auto& atom : gringoOutput->getCostAtomInfos()) // costAtomInfos.emplace_back(CostAtomInfo(atom, claspProgramBuilder))); // Prepare for solving. clasp.prepare(); } }
void Solver::startSolvingForCurrentRowCombination() { // ++solverSetups; asyncResult.reset(); if(reground) { // Set up ASP solver config.solve.numModels = 0; // TODO The last parameter of clasp.startAsp in the next line is "allowUpdate". Does setting it to false have benefits? // WORKAROUND for BUG in ClaspFacade::startAsp() // TODO remove on update to new version if(clasp.ctx.numVars() == 0 && clasp.ctx.frozen()) clasp.ctx.reset(); Clasp::Asp::LogicProgram& claspProgramBuilder = static_cast<Clasp::Asp::LogicProgram&>(clasp.startAsp(config)); GringoOutputProcessor gringoOutput(claspProgramBuilder); std::unique_ptr<Gringo::Output::OutputBase> out(new Gringo::Output::OutputBase({}, gringoOutput)); Gringo::Input::Program program; asp_utils::DummyGringoModule module; Gringo::Scripts scripts(module); Gringo::Defines defs; Gringo::Input::NongroundProgramBuilder gringoProgramBuilder(scripts, program, *out, defs); Gringo::Input::NonGroundParser parser(gringoProgramBuilder); // Input: Induced subinstance std::unique_ptr<std::stringstream> instanceInput(new std::stringstream); asp_utils::induceSubinstance(*instanceInput, app.getInstance(), decomposition.getNode().getBag()); app.getPrinter().solverInvocationInput(decomposition, instanceInput->str()); // Input: Decomposition std::unique_ptr<std::stringstream> decompositionInput(new std::stringstream); asp_utils::declareDecomposition(decomposition, *decompositionInput); app.getPrinter().solverInvocationInput(decomposition, decompositionInput->str()); // Input: Child rows std::unique_ptr<std::stringstream> childRowsInput(new std::stringstream); *childRowsInput << "% Child row facts" << std::endl; for(const auto& row : getCurrentRowCombination()) { for(const auto& item : row->getItems()) *childRowsInput << "childItem(" << item << ")." << std::endl; for(const auto& item : row->getAuxItems()) *childRowsInput << "childAuxItem(" << item << ")." << std::endl; // TODO costs, etc. } app.getPrinter().solverInvocationInput(decomposition, childRowsInput->str()); // Pass input to ASP solver for(const auto& file : encodingFiles) parser.pushFile(std::string(file)); parser.pushStream("<instance>", std::move(instanceInput)); parser.pushStream("<decomposition>", std::move(decompositionInput)); parser.pushStream("<child_rows>", std::move(childRowsInput)); parser.parse(); // Ground program.rewrite(defs); program.check(); if(Gringo::message_printer()->hasError()) throw std::runtime_error("Grounding stopped because of errors"); auto gPrg = program.toGround(out->domains); Gringo::Ground::Parameters params; params.add("base", {}); gPrg.ground(params, scripts, *out); params.clear(); claspProgramBuilder.endProgram(); itemAtomInfos.clear(); for(const auto& atom : gringoOutput.getItemAtomInfos()) itemAtomInfos.emplace_back(ItemAtomInfo(atom, claspProgramBuilder)); auxItemAtomInfos.clear(); for(const auto& atom : gringoOutput.getAuxItemAtomInfos()) auxItemAtomInfos.emplace_back(AuxItemAtomInfo(atom, claspProgramBuilder)); // TODO costs etc. clasp.prepare(); } else { // Set external variables to the values of the current child row combination clasp.update(false, false); clasp.prepare(); // Mark atoms corresponding to items from the currently extended rows for(const auto& row : getCurrentRowCombination()) { for(const auto& item : row->getItems()) { assert(itemsToLitIndices.find(item) != itemsToLitIndices.end()); assert(itemsToLitIndices.at(item) < literals.size()); #ifdef DISABLE_CHECKS literals[itemsToLitIndices.at(item)].watch(); #else try { literals[itemsToLitIndices.at(item)].watch(); } catch(const std::out_of_range&) { std::ostringstream msg; msg << "Unknown variable; atom childItem(" << *item << ") not shown or not declared as external?"; throw std::runtime_error(msg.str()); } #endif } for(const auto& item : row->getAuxItems()) { assert(auxItemsToLitIndices.find(item) != auxItemsToLitIndices.end()); assert(auxItemsToLitIndices.at(item) < literals.size()); #ifdef DISABLE_CHECKS literals[auxItemsToLitIndices.at(item)].watch(); #else try { literals[auxItemsToLitIndices.at(item)].watch(); } catch(const std::out_of_range&) { std::ostringstream msg; msg << "Unknown variable; atom childAuxItem(" << *item << ") not shown or not declared as external?"; throw std::runtime_error(msg.str()); } #endif } } // Set marked literals to true and all others to false for(auto& lit : literals) { if(lit.watched()) { lit.clearWatch(); clasp.assume(lit); } else clasp.assume(~lit); } } asyncResult.reset(new BasicSolveIter(clasp)); }
void DebugMachineReadable::solverInvocationInput(const Decomposition& decompositionNode, const std::string& input) { std::cout << "% Input for solver at decomposition node " << decompositionNode.getNode().getGlobalId() << std::endl << input << std::endl; }