this repo has no description
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