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