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#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