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::workerThreadMain() { std::unique_lock<std::mutex> lock(workerMutex); // Set up ASP solver Clasp::ClaspFacade clasp; Clasp::ClaspConfig config; config.solve.numModels = 0; Clasp::Asp::LogicProgram& claspProgramBuilder = dynamic_cast<Clasp::Asp::LogicProgram&>(clasp.startAsp(config, true)); std::unique_ptr<Gringo::Output::LparseOutputter> lpOut(new GringoOutputProcessor(claspProgramBuilder)); claspCallback.reset(new ClaspCallback(dynamic_cast<GringoOutputProcessor&>(*lpOut), app, *this, lock)); std::unique_ptr<Gringo::Output::OutputBase> out(new Gringo::Output::OutputBase({}, *lpOut)); Gringo::Input::Program program; Gringo::Scripts scripts; Gringo::Defines defs; Gringo::Input::NongroundProgramBuilder gringoProgramBuilder(scripts, program, *out, defs); Gringo::Input::NonGroundParser parser(gringoProgramBuilder); // Input: Original problem instance std::unique_ptr<std::stringstream> instanceInput(new std::stringstream); *instanceInput << app.getInputString(); // Input: Decomposition std::unique_ptr<std::stringstream> decompositionInput(new std::stringstream); solver::asp::Solver::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(); // Prepare for solving. (This makes clasp's symbol table available.) clasp.prepare(); claspCallback->prepare(clasp.ctx.symbolTable()); // We need to know which clasp variable corresponds to each childItem(_) atom. for(const auto& pair : clasp.ctx.symbolTable()) { if(!pair.second.name.empty()) { const std::string name = pair.second.name.c_str(); if(name.compare(0, 10, "childItem(") == 0) { const std::string argument = name.substr(10, name.length()-11); itemsToVars.emplace(argument, pair.first); } } } // Let main thread finish the constructor wakeMainThreadRequested = true; wakeMainThread.notify_one(); // Wait until we should do work wakeWorkerThread.wait(lock, [&]() { return wakeWorkerThreadRequested; }); wakeWorkerThreadRequested = false; if(decomposition.getChildren().empty()) { // This is a leaf solver. clasp.solve(claspCallback.get()); } else { // Get the first row from each child node std::unordered_map<unsigned int, ItemTree::Children::const_iterator> childRows; childRows.reserve(decomposition.getChildren().size()); for(const auto& child : decomposition.getChildren()) { const ItemTree::Children::const_iterator newRow = dynamic_cast<Solver&>(child->getSolver()).nextRow(); if(newRow == dynamic_cast<Solver&>(child->getSolver()).getItemTreeSoFar()->getChildren().end()) { // Notify main thread that solving is complete noMoreModels = true; wakeMainThreadRequested = true; wakeMainThread.notify_one(); return; } childRows.emplace(child->getNode().getGlobalId(), newRow); } // Let the combination of these rows be the input for our ASP call ItemTreeNode::ExtensionPointerTuple rootExtensionPointers; for(const auto& child : decomposition.getChildren()) rootExtensionPointers.emplace(child->getNode().getGlobalId(), dynamic_cast<Solver&>(child->getSolver()).getItemTreeSoFar()->getNode()); claspCallback->setRootExtensionPointers(std::move(rootExtensionPointers)); ItemTreeNode::ExtensionPointerTuple extendedRows; for(const auto& nodeIdAndRow : childRows) extendedRows.emplace(nodeIdAndRow.first, (*nodeIdAndRow.second)->getNode()); claspCallback->setExtendedRows(std::move(extendedRows)); { Clasp::Asp::LogicProgram& prg = static_cast<Clasp::Asp::LogicProgram&>(clasp.update()); for(const auto& nodeIdAndRow : childRows) { for(const auto& item : (*nodeIdAndRow.second)->getNode()->getItems()) { prg.freeze(itemsToVars.at(item), Clasp::value_true); } } } clasp.prepare(); claspCallback->prepare(clasp.ctx.symbolTable()); clasp.solve(claspCallback.get()); { // XXX Necessary to update so often? Is the overhead bad? Clasp::Asp::LogicProgram& prg = static_cast<Clasp::Asp::LogicProgram&>(clasp.update()); for(const auto& nodeIdAndRow : childRows) { for(const auto& item : (*nodeIdAndRow.second)->getNode()->getItems()) { prg.freeze(itemsToVars.at(item), Clasp::value_false); } } } // Now there are no models anymore for this row combination // Until there are no new rows anymore, generate one new row at some child node and combine it with all rows from other child nodes bool foundNewRow; do { foundNewRow = false; for(const auto& child : decomposition.getChildren()) { ItemTree::Children::const_iterator newRow = dynamic_cast<Solver&>(child->getSolver()).nextRow(); if(newRow != dynamic_cast<Solver&>(child->getSolver()).getItemTreeSoFar()->getChildren().end()) { foundNewRow = true; aspCallsOnNewRowFromChild(newRow, child, clasp); } } } while(foundNewRow); } // Notify main thread that solving is complete noMoreModels = true; wakeMainThreadRequested = true; wakeMainThread.notify_one(); }
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)); }
ItemTreePtr Solver::compute() { const auto nodeStackElement = app.getPrinter().visitNode(decomposition); // Compute item trees of child nodes ChildItemTrees childItemTrees; for(const auto& child : decomposition.getChildren()) { ItemTreePtr itree = child->getSolver().compute(); if(!itree) return itree; childItemTrees.emplace(child->getNode().getGlobalId(), std::move(itree)); } // Input: Child item trees std::unique_ptr<std::stringstream> childItemTreesInput(new std::stringstream); *childItemTreesInput << "% Child item tree facts" << std::endl; for(const auto& childItemTree : childItemTrees) { std::ostringstream rootItemSetName; rootItemSetName << 'n' << childItemTree.first; asp_utils::declareItemTree(*childItemTreesInput, childItemTree.second.get(), tableMode, childItemTree.first, rootItemSetName.str()); } app.getPrinter().solverInvocationInput(decomposition, childItemTreesInput->str()); // 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()); // Set up ASP solver Clasp::ClaspConfig config; config.solve.numModels = 0; Clasp::ClaspFacade clasp; // TODO The last parameter of clasp.startAsp in the next line is "allowUpdate". Does setting it to false have benefits? Clasp::Asp::LogicProgram& claspProgramBuilder = dynamic_cast<Clasp::Asp::LogicProgram&>(clasp.startAsp(config)); std::unique_ptr<Gringo::Output::LparseOutputter> lpOut(newGringoOutputProcessor(claspProgramBuilder, childItemTrees, tableMode)); std::unique_ptr<Gringo::Output::OutputBase> out(new Gringo::Output::OutputBase({}, *lpOut)); 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); // 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_itrees>", std::move(childItemTreesInput)); parser.parse(); // Ground and solve 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(); // Finalize ground program and create solver variables claspProgramBuilder.endProgram(); std::unique_ptr<asp_utils::ClaspCallback> cb(newClaspCallback(tableMode, *lpOut, childItemTrees, app, decomposition.isRoot(), decomposition, cardinalityCost)); cb->prepare(claspProgramBuilder); clasp.prepare(); clasp.solve(cb.get()); if(printStatistics) { std::cout << "Solver statistics for decomposition node " << decomposition.getNode().getGlobalId() << ':' << std::endl; Clasp::Cli::TextOutput{true, Clasp::Cli::TextOutput::format_asp}.printStatistics(clasp.summary(), true); } ItemTreePtr result = cb->finalize(decomposition.isRoot(), app.isPruningDisabled() == false || decomposition.isRoot()); app.getPrinter().solverInvocationResult(decomposition, result.get()); return result; }