this repo has no description
at develop 10 kB view raw
1/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ 2 3/* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7#ifdef _MSC_VER 8#define _CRT_SECURE_NO_WARNINGS 9#endif 10 11#include <minizinc/file_utils.hh> 12#include <minizinc/process.hh> 13#include <minizinc/solvers/nl/nl_file.hh> 14#include <minizinc/solvers/nl/nl_solreader.hh> 15#include <minizinc/solvers/nl/nl_solverinstance.hh> 16 17#include <cstdio> 18#include <cstring> 19#include <fstream> 20 21using namespace std; 22 23namespace MiniZinc { 24 25/** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 26 * **/ 27// Solver Factory 28 29NLSolverFactory::NLSolverFactory() { 30 SolverConfig sc("org.minizinc.mzn-nl", 31 MZN_VERSION_MAJOR "." MZN_VERSION_MINOR "." MZN_VERSION_PATCH); 32 sc.name("Generic Non Linear driver"); 33 sc.mznlibVersion(1); 34 sc.description("MiniZinc generic Non Linear solver plugin"); 35 sc.supportsFzn(false); 36 sc.supportsNL(true); 37 sc.requiredFlags({"--nl-cmd"}); 38 sc.tags({"__internal__"}); 39 SolverConfigs::registerBuiltinSolver(sc); 40} 41 42string NLSolverFactory::getDescription(SolverInstanceBase::Options* /*opt*/) { 43 string v = "NL solver plugin, compiled " __DATE__ " " __TIME__; 44 return v; 45} 46 47string NLSolverFactory::getVersion(SolverInstanceBase::Options* /*opt*/) { 48 return MZN_VERSION_MAJOR; 49} 50 51string NLSolverFactory::getId() { return "org.minizinc.mzn-nl"; } 52 53void NLSolverFactory::printHelp(ostream& os) { 54 os << "MZN-NL plugin options" << std::endl 55 << " --nl-cmd , --nonlinear-cmd <exe>\n The backend solver filename.\n" 56 << " --nl-flags <options>, --backend-flags <options>\n" 57 " Specify option to be passed to the NL solver.\n" 58 << " --nl-flag <option>, --backend-flag <option>\n" 59 " As above, but for a single option string that needs to be quoted in a shell.\n" 60 << " --hexafloat\n Use hexadecimal format when communicating floating points with the " 61 "solver.\n" 62 << " --keepfile\n Write the nl and sol files next to the input file and don't remove " 63 "them.\n" 64 // << " --nl-sigint\n Send SIGINT instead of SIGTERM.\n" 65 // << " -t <ms>, --solver-time-limit <ms>, --fzn-time-limit <ms>\n Set time limit (in 66 // milliseconds) for solving.\n" 67 // << " -p <n>, --parallel <n>\n Use <n> threads during search. The default is 68 // solver-dependent.\n" 69 // << " -r <n>, --seed <n>, --random-seed <n>\n For compatibility only: use solver flags 70 // instead.\n" 71 ; 72} 73 74SolverInstanceBase::Options* NLSolverFactory::createOptions() { return new NLSolverOptions; } 75 76SolverInstanceBase* NLSolverFactory::doCreateSI(Env& env, std::ostream& log, 77 SolverInstanceBase::Options* opt) { 78 return new NLSolverInstance(env, log, opt); 79} 80 81bool NLSolverFactory::processOption(SolverInstanceBase::Options* opt, int& i, 82 std::vector<std::string>& argv, const std::string& workingDir) { 83 auto& _opt = static_cast<NLSolverOptions&>(*opt); 84 CLOParser cop(i, argv); 85 string buffer; 86 87 int nn = -1; 88 if (cop.getOption("--nl-cmd --nonlinear-cmd", &buffer)) { 89 _opt.nlSolver = buffer; 90 } else if (cop.getOption("--hexafloat")) { 91 _opt.doHexafloat = true; 92 } else if (cop.getOption("--nl-flags --backend-flags", &buffer)) { 93 auto args = FileUtils::parse_cmd_line(buffer); 94 for (const auto& arg : args) { 95 _opt.nlFlags.push_back(arg); 96 } 97 } else if (cop.getOption("--nl-flag --backend-flag", &buffer)) { 98 _opt.nlFlags.push_back(buffer); 99 } else if (cop.getOption("--keepfile")) { 100 _opt.doKeepfile = true; 101 } else if (cop.getOption("-s --solver-statistics")) { 102 // ignore statistics flags for now 103 } else if (cop.getOption("-v --verbose-solving")) { 104 _opt.verbose = true; 105 } else { 106 for (auto& fznf : _opt.nlSolverFlags) { 107 if (fznf.t == MZNFZNSolverFlag::FT_ARG && cop.getOption(fznf.n.c_str(), &buffer)) { 108 _opt.nlFlags.push_back(fznf.n); 109 _opt.nlFlags.push_back(buffer); 110 return true; 111 } 112 if (fznf.t == MZNFZNSolverFlag::FT_NOARG && cop.getOption(fznf.n.c_str())) { 113 _opt.nlFlags.push_back(fznf.n); 114 return true; 115 } 116 } 117 return false; 118 } 119 120 return true; 121} 122 123/** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 124 * **/ 125// Solver Instance 126 127NLSolverInstance::NLSolverInstance(Env& env, std::ostream& log, 128 SolverInstanceBase::Options* options) 129 : SolverInstanceBase(env, log, options), _fzn(env.flat()), _ozn(env.output()) {} 130 131NLSolverInstance::~NLSolverInstance() {} 132 133void NLSolverInstance::processFlatZinc() {} 134 135void NLSolverInstance::resetSolver() {} 136 137SolverInstance::Status NLSolverInstance::solve() { 138 // Get the options 139 auto& opt = static_cast<NLSolverOptions&>(*_options); 140 141 // --- --- --- Prepare the files 142 string file_nl; // Output for the NL, will be the input for the solver 143 string file_sol; // Ouput of the solver 144 FileUtils::TmpDir* tmpdir = nullptr; 145 146 if (opt.doKeepfile) { 147 // Keep file: output next to original file 148 ASTString file_mzn = _env.envi().originalModel != nullptr 149 ? _env.envi().originalModel->filepath() 150 : _env.envi().model->filepath(); 151 string file_base = std::string(file_mzn.substr(0, file_mzn.findLastOf('.'))); 152 file_nl = file_base + ".nl"; 153 file_sol = file_base + ".sol"; 154 } else { 155 // Don't keep file: create a temp directory 156 tmpdir = new FileUtils::TmpDir(); 157 file_nl = tmpdir->name() + "/model.nl"; 158 file_sol = tmpdir->name() + "/model.sol"; 159 } 160 std::ofstream outfile(FILE_PATH(file_nl)); 161 // Configure floating point output 162 if (opt.doHexafloat) { 163 outfile << hexfloat; 164 } else { 165 outfile.precision(numeric_limits<double>::digits10 + 2); 166 } 167 168 // --- --- --- Result of the try/catch block 169 // Use to talk back to minizinc 170 auto* out = getSolns2Out(); 171 // Manage status 172 int exitStatus = -1; 173 174 // --- --- --- All the NL operations in one try/catch 175 try { 176 // --- --- --- Create the NL data 177 // Analyse the variable declarations 178 for (VarDeclIterator it = _fzn->vardecls().begin(); it != _fzn->vardecls().end(); ++it) { 179 if (!it->removed()) { 180 Item& item = *it; 181 analyse(&item); 182 } 183 } 184 // Analyse the contraints 185 for (ConstraintIterator it = _fzn->constraints().begin(); it != _fzn->constraints().end(); 186 ++it) { 187 if (!it->removed()) { 188 Item& item = *it; 189 analyse(&item); 190 } 191 } 192 // Analyse the goal 193 analyse(_fzn->solveItem()); 194 // Phase 2 195 _nlFile.phase2(); 196 // Print to the files 197 _nlFile.printToStream(outfile); 198 199 // --- --- --- Call the solver 200 NLSolns2Out s2o = NLSolns2Out(out, _nlFile, opt.verbose); 201 vector<string> cmd_line; 202 203 if (opt.nlSolver.empty()) { 204 delete tmpdir; 205 tmpdir = nullptr; 206 outfile.close(); 207 throw InternalError("No NL solver specified"); 208 } 209 210 cmd_line.push_back(opt.nlSolver); 211 cmd_line.push_back(file_nl); 212 cmd_line.emplace_back("-AMPL"); 213 214 for (const auto& arg : opt.nlFlags) { 215 cmd_line.push_back(arg); 216 } 217 218 Process<NLSolns2Out> proc(cmd_line, &s2o, 0, true); 219 exitStatus = proc.run(); 220 221 if (exitStatus == 0) { 222 // Parse the result 223 s2o.parseSolution(file_sol); 224 } 225 226 } catch (const NLException& e) { 227 out->getLog() << e.what(); 228 exitStatus = -2; 229 } 230 231 // --- --- --- Cleanup and exit 232 delete tmpdir; 233 tmpdir = nullptr; 234 outfile.close(); 235 return exitStatus == 0 ? out->status : Status::ERROR; 236} 237 238// Unused 239Expression* NLSolverInstance::getSolutionValue(Id* id) { 240 assert(false); 241 return nullptr; 242} 243 244/** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 245 * **/ 246/** Analyse an item 247 * An item is a "top node" in the ast. 248 * In flatzinc, we can only have the cases 'variable declaration' and 'constraint'. 249 */ 250void NLSolverInstance::analyse(const Item* i) { 251 // Guard 252 if (i == nullptr) { 253 return; 254 } 255 256 // Switch on the id of item 257 switch (i->iid()) { 258 case Item::II_INC: { 259 should_not_happen("include \"" << i->cast<IncludeI>()->f() << "\")"); 260 } break; 261 262 // Case of the variable declaration. 263 // Because it is a variable declaration, the expression associated to the item is necessary a 264 // VarDecl. From the VarDecl, we can obtain the type and the RHS expression. Use this to analyse 265 // further. 266 case Item::II_VD: { 267 DEBUG_MSG("II_VD: Variable Declaration."); 268 269 const VarDecl& vd = *i->cast<VarDeclI>()->e(); 270 const TypeInst& ti = *vd.ti()->cast<TypeInst>(); 271 const Expression& rhs = *vd.e(); 272 _nlFile.addVarDecl(vd, ti, rhs); 273 } break; 274 275 case Item::II_ASN: { 276 should_not_happen("item II_ASN should not be present in NL's input."); 277 } break; 278 279 // Case of the constraint. 280 // Constraint are expressed through builtin calls. 281 // Hence, the expression associated to the item must be a E_CALL. 282 case Item::II_CON: { 283 DEBUG_MSG("II_CON: Constraint."); 284 Expression* e = i->cast<ConstraintI>()->e(); 285 if (e->eid() == Expression::E_CALL) { 286 const Call& c = *e->cast<Call>(); 287 DEBUG_MSG(" " << c.id() << " "); 288 _nlFile.analyseConstraint(c); 289 } else { 290 DEBUG_MSG(" Contraint is not a builtin call."); 291 assert(false); 292 } 293 } break; 294 295 // Case of the 'solve' directive 296 case Item::II_SOL: { 297 const SolveI& si = *i->cast<SolveI>(); 298 _nlFile.addSolve(si.st(), si.e()); 299 } break; 300 301 case Item::II_OUT: { 302 should_not_happen("Item II_OUT should not be present in NL's input."); 303 } break; 304 305 case Item::II_FUN: { 306 should_not_happen("Item II_FUN should not be present in NL's input."); 307 } break; 308 } // END OF SWITCH 309} 310 311} // namespace MiniZinc