this repo has no description
1
2/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
3
4/*
5 * Main authors:
6 * Guido Tack <guido.tack@monash.edu>
7 * Gleb Belov <gleb.belov@monash.edu>
8 */
9
10/* This Source Code Form is subject to the terms of the Mozilla Public
11 * License, v. 2.0. If a copy of the MPL was not distributed with this
12 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
13
14/* This (main) file coordinates flattening and solving.
15 * The corresponding modules are flexibly plugged in
16 * as derived classes, prospectively from DLLs.
17 * A flattening module should provide MinZinc::GetFlattener()
18 * A solving module should provide an object of a class derived from SolverFactory.
19 * Need to get more flexible for multi-pass & multi-solving stuff TODO
20 */
21
22#ifdef _MSC_VER
23#define _CRT_SECURE_NO_WARNINGS
24#endif
25
26#include <minizinc/param_config.hh>
27#include <minizinc/solver.hh>
28
29#include <chrono>
30#include <cstdlib>
31#include <ctime>
32#include <fstream>
33#include <iomanip>
34#include <iostream>
35#include <ratio>
36
37#ifdef HAS_GUROBI
38#include <minizinc/solvers/MIP/MIP_gurobi_solverfactory.hh>
39#endif
40#ifdef HAS_CPLEX
41#include <minizinc/solvers/MIP/MIP_cplex_solverfactory.hh>
42#endif
43#ifdef HAS_OSICBC
44#include <minizinc/solvers/MIP/MIP_osicbc_solverfactory.hh>
45#endif
46#ifdef HAS_XPRESS
47#include <minizinc/solvers/MIP/MIP_xpress_solverfactory.hh>
48#endif
49#ifdef HAS_GECODE
50#include <minizinc/solvers/gecode_solverfactory.hh>
51#endif
52#ifdef HAS_GEAS
53#include <minizinc/solvers/geas_solverfactory.hh>
54#endif
55#ifdef HAS_SCIP
56#include <minizinc/solvers/MIP/MIP_scip_solverfactory.hh>
57#endif
58#include <minizinc/solvers/fzn_solverfactory.hh>
59#include <minizinc/solvers/fzn_solverinstance.hh>
60#include <minizinc/solvers/mzn_solverfactory.hh>
61#include <minizinc/solvers/mzn_solverinstance.hh>
62#include <minizinc/solvers/nl/nl_solverfactory.hh>
63#include <minizinc/solvers/nl/nl_solverinstance.hh>
64
65using namespace std;
66using namespace MiniZinc;
67
68SolverInitialiser::SolverInitialiser() {
69#ifdef HAS_GUROBI
70 GurobiSolverFactoryInitialiser _gurobi_init;
71#endif
72#ifdef HAS_CPLEX
73 static CplexSolverFactoryInitialiser _cplex_init;
74#endif
75#ifdef HAS_OSICBC
76 static OSICBCSolverFactoryInitialiser _osicbc_init;
77#endif
78#ifdef HAS_XPRESS
79 static XpressSolverFactoryInitialiser _xpress_init;
80#endif
81#ifdef HAS_GECODE
82 static GecodeSolverFactoryInitialiser _gecode_init;
83#endif
84#ifdef HAS_GEAS
85 static GeasSolverFactoryInitialiser _geas_init;
86#endif
87#ifdef HAS_SCIP
88 static SCIPSolverFactoryInitialiser _scip_init;
89#endif
90 static FZNSolverFactoryInitialiser _fzn_init;
91 static MZNSolverFactoryInitialiser _mzn_init;
92 static NLSolverFactoryInitialiser _nl_init;
93}
94
95MZNFZNSolverFlag MZNFZNSolverFlag::std(const std::string& n0) {
96 const std::string argFlags("-I -n -p -r -n-o");
97 if (argFlags.find(n0) != std::string::npos) {
98 return MZNFZNSolverFlag(FT_ARG, n0);
99 }
100 return MZNFZNSolverFlag(FT_NOARG, n0);
101}
102
103MZNFZNSolverFlag MZNFZNSolverFlag::extra(const SolverConfig::ExtraFlag& ef) {
104 return MZNFZNSolverFlag(
105 ef.flagType == SolverConfig::ExtraFlag::FlagType::T_BOOL && ef.range.empty() ? FT_NOARG
106 : FT_ARG,
107 ef.flag);
108}
109
110// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
111SolverRegistry* MiniZinc::get_global_solver_registry() {
112 static SolverRegistry sr;
113 return &sr;
114}
115
116void SolverRegistry::addSolverFactory(SolverFactory* pSF) {
117 assert(pSF);
118 _sfstorage.push_back(pSF);
119}
120
121void SolverRegistry::removeSolverFactory(SolverFactory* pSF) {
122 auto it = find(_sfstorage.begin(), _sfstorage.end(), pSF);
123 assert(pSF);
124 _sfstorage.erase(it);
125}
126
127void SolverRegistry::addFactoryFlag(const std::string& flag, SolverFactory* sf) {
128 assert(sf);
129 _factoryFlagStorage.push_back(std::make_pair(flag, sf));
130}
131
132void SolverRegistry::removeFactoryFlag(const std::string& flag, SolverFactory* sf) {
133 assert(sf);
134 auto it = find(_factoryFlagStorage.begin(), _factoryFlagStorage.end(), std::make_pair(flag, sf));
135 _factoryFlagStorage.erase(it);
136}
137
138/// Function createSI also adds each SI to the local storage
139SolverInstanceBase* SolverFactory::createSI(Env& env, std::ostream& log,
140 SolverInstanceBase::Options* opt) {
141 SolverInstanceBase* pSI = doCreateSI(env, log, opt);
142 if (pSI == nullptr) {
143 throw InternalError("SolverFactory: failed to initialize solver " + getDescription());
144 }
145 _sistorage.resize(_sistorage.size() + 1);
146 _sistorage.back().reset(pSI);
147 return pSI;
148}
149
150/// also providing a destroy function for a DLL or just special allocator etc.
151void SolverFactory::destroySI(SolverInstanceBase* pSI) {
152 auto it = _sistorage.begin();
153 for (; it != _sistorage.end(); ++it) {
154 if (it->get() == pSI) {
155 break;
156 }
157 }
158 if (_sistorage.end() == it) {
159 cerr << " SolverFactory: failed to remove solver at " << pSI << endl;
160 throw InternalError(" SolverFactory: failed to remove solver");
161 }
162 _sistorage.erase(it);
163}
164
165MznSolver::MznSolver(std::ostream& os0, std::ostream& log0)
166 : _solverConfigs(log0),
167 _flt(os0, log0, _solverConfigs.mznlibDir()),
168 _executableName("<executable>"),
169 _os(os0),
170 _log(log0),
171 s2out(os0, log0, _solverConfigs.mznlibDir()) {}
172
173MznSolver::~MznSolver() {
174 // if (si) // first the solver
175 // CleanupSolverInterface(si);
176 // TODO cleanup the used solver interfaces
177 _si = nullptr;
178 _siOpt = nullptr;
179 GC::trigger();
180}
181
182bool MznSolver::ifMzn2Fzn() const { return _isMzn2fzn; }
183
184bool MznSolver::ifSolns2out() const { return s2out.opt.flagStandaloneSolns2Out; }
185
186void MznSolver::addSolverInterface(SolverFactory* sf) {
187 _si = sf->createSI(*_flt.getEnv(), _log, _siOpt);
188 assert(_si);
189 if (s2out.getEnv() == nullptr) {
190 s2out.initFromEnv(_flt.getEnv());
191 }
192 _si->setSolns2Out(&s2out);
193 if (flagCompilerVerbose) {
194 _log
195 // << " ---------------------------------------------------------------------------\n"
196 << " % SOLVING PHASE\n"
197 << sf->getDescription(_siOpt) << endl;
198 }
199}
200
201void MznSolver::addSolverInterface() {
202 GCLock lock;
203 if (_sf == nullptr) {
204 if (get_global_solver_registry()->getSolverFactories().empty()) {
205 _log << " MznSolver: NO SOLVER FACTORIES LINKED." << endl;
206 assert(0);
207 }
208 _sf = get_global_solver_registry()->getSolverFactories().back();
209 }
210 addSolverInterface(_sf);
211}
212
213void MznSolver::printUsage() {
214 _os << _executableName << ": ";
215 if (ifMzn2Fzn()) {
216 _os << "MiniZinc to FlatZinc converter.\n"
217 << "Usage: " << _executableName
218 << " [<options>] [-I <include path>] <model>.mzn [<data>.dzn ...]" << std::endl;
219 } else if (ifSolns2out()) {
220 _os << "Solutions to output translator.\n"
221 << "Usage: " << _executableName << " [<options>] <model>.ozn" << std::endl;
222 } else {
223 _os << "MiniZinc driver.\n"
224 << "Usage: " << _executableName
225 << " [<options>] [-I <include path>] <model>.mzn [<data>.dzn ...] or just <flat>.fzn"
226 << std::endl;
227 }
228}
229
230void MznSolver::printHelp(const std::string& selectedSolver) {
231 printUsage();
232 _os << "General options:" << std::endl
233 << " --help, -h\n Print this help message." << std::endl
234 << " --version\n Print version information." << std::endl
235 << " --solvers\n Print list of available solvers." << std::endl
236 << " --time-limit <ms>\n Stop after <ms> milliseconds (includes compilation and solving)."
237 << std::endl
238 << " --solver <solver id>, --solver <solver config file>.msc\n Select solver to use."
239 << std::endl
240 << " --help <solver id>\n Print help for a particular solver." << std::endl
241 << " -v, -l, --verbose\n Print progress/log statements. Note that some solvers may log "
242 "to "
243 "stdout."
244 << std::endl
245 << " --verbose-compilation\n Print progress/log statements for compilation." << std::endl
246 << " -s, --statistics\n Print statistics." << std::endl
247 << " --compiler-statistics\n Print statistics for compilation." << std::endl
248 << " -c, --compile\n Compile only (do not run solver)." << std::endl
249 << " --config-dirs\n Output configuration directories." << std::endl
250 << " --param-file <file>\n Load parameters from the given JSON file." << std::endl;
251
252 if (selectedSolver.empty()) {
253 _flt.printHelp(_os);
254 _os << endl;
255 if (!ifMzn2Fzn()) {
256 Solns2Out::printHelp(_os);
257 _os << endl;
258 }
259 _os << "Available solvers (get help using --help <solver id>):" << endl;
260 std::vector<std::string> solvers = _solverConfigs.solvers();
261 if (solvers.empty()) {
262 cout << " none.\n";
263 }
264 for (auto& solver : solvers) {
265 cout << " " << solver << endl;
266 }
267 } else {
268 const SolverConfig& sc = _solverConfigs.config(selectedSolver);
269 string solverId = sc.executable().empty() ? sc.id()
270 : (sc.supportsMzn() ? string("org.minizinc.mzn-mzn")
271 : string("org.minizinc.mzn-fzn"));
272 bool found = false;
273 for (auto it = get_global_solver_registry()->getSolverFactories().rbegin();
274 it != get_global_solver_registry()->getSolverFactories().rend(); ++it) {
275 if ((*it)->getId() == solverId) {
276 _os << endl;
277 (*it)->printHelp(_os);
278 if (!sc.executable().empty() && !sc.extraFlags().empty()) {
279 _os << "Extra solver flags (use with ";
280 _os << (sc.supportsMzn() ? "--mzn-flags" : "--fzn-flags") << ")" << endl;
281 for (const SolverConfig::ExtraFlag& ef : sc.extraFlags()) {
282 _os << " " << ef.flag << endl << " " << ef.description << endl;
283 }
284 }
285 found = true;
286 }
287 }
288 if (!found) {
289 _os << "No help found for solver " << selectedSolver << endl;
290 }
291 }
292}
293
294void add_flags(const std::string& sep, const std::vector<std::string>& in_args,
295 std::vector<std::string>& out_args) {
296 for (const std::string& arg : in_args) {
297 out_args.push_back(sep);
298 out_args.push_back(arg);
299 }
300}
301
302MznSolver::OptionStatus MznSolver::processOptions(std::vector<std::string>& argv) {
303 _executableName = argv[0];
304 _executableName = _executableName.substr(_executableName.find_last_of("/\\") + 1);
305 size_t lastdot = _executableName.find_last_of('.');
306 if (lastdot != std::string::npos) {
307 _executableName = _executableName.substr(0, lastdot);
308 }
309 string solver;
310 bool load_params = false;
311 bool mzn2fzn_exe = (_executableName == "mzn2fzn");
312 if (mzn2fzn_exe) {
313 _isMzn2fzn = true;
314 } else if (_executableName == "solns2out") {
315 s2out.opt.flagStandaloneSolns2Out = true;
316 flagIsSolns2out = true;
317 }
318 bool compileSolutionChecker = false;
319 int i = 1;
320 int j = 1;
321 int argc = static_cast<int>(argv.size());
322 std::vector<std::string> workingDirs = {""};
323 if (argc < 2) {
324 return OPTION_ERROR;
325 }
326
327 // Add params from a file if necessary
328 std::vector<std::string> paramFiles;
329 for (i = 1; i < argc; ++i) {
330 string paramFile;
331 bool usedFlag = false;
332 bool pushWorkingDir = true;
333
334 if (argv[i] == "--param-file") {
335 usedFlag = true;
336 ++i;
337 if (i == argc) {
338 _log << "Argument required for --param-file" << endl;
339 return OPTION_ERROR;
340 }
341 paramFile = argv[i];
342 } else if (argv[i] == "--param-file-no-push") {
343 usedFlag = true;
344 pushWorkingDir = false;
345 ++i;
346 if (i == argc) {
347 _log << "Argument required for --param-file-no-push" << endl;
348 return OPTION_ERROR;
349 }
350 paramFile = argv[i];
351 } else if (argv[i] == "--push-working-directory") {
352 ++i;
353 workingDirs.push_back(argv[i]);
354 } else if (argv[i] == "--pop-working-directory") {
355 workingDirs.pop_back();
356 } else {
357 size_t last_dot = argv[i].find_last_of('.');
358 if (last_dot != string::npos && argv[i].substr(last_dot, string::npos) == ".mpc") {
359 paramFile = argv[i];
360 }
361 }
362
363 if (!paramFile.empty()) {
364 try {
365 auto paramFilePath = FileUtils::file_path(paramFile, workingDirs.back());
366 if (std::find(paramFiles.begin(), paramFiles.end(), paramFilePath) != paramFiles.end()) {
367 throw ParamException("Cyclic parameter configuration file");
368 }
369 // add parameter file arguments
370 ParamConfig pc;
371 pc.blacklist(
372 {"--solvers", "--solvers-json", "--solver-json", "--help", "-h", "--config-dirs"});
373 pc.negatedFlag("-i", "-n-i");
374 pc.negatedFlag("--intermediate", "--no-intermediate");
375 pc.negatedFlag("--intermediate-solutions", "--no-intermediate-solutions");
376 pc.negatedFlag("--all-satisfaction", "--disable-all-satisfaction");
377 pc.load(paramFilePath);
378
379 // Insert the new options
380 auto toInsert = pc.argv();
381 auto remove = argv.begin() + (usedFlag ? i - 1 : i);
382 auto position = argv.erase(remove, argv.begin() + i + 1);
383 if (pushWorkingDir) {
384 position =
385 argv.insert(position, {"--push-working-directory",
386 FileUtils::file_path(FileUtils::dir_name(paramFilePath))}) +
387 2;
388 }
389 position = argv.insert(position, toInsert.begin(), toInsert.end()) + toInsert.size();
390 if (pushWorkingDir) {
391 position = argv.insert(position, "--pop-working-directory") + 1;
392 }
393 paramFiles.push_back(paramFilePath);
394 argc = argv.size();
395
396 // Have to process the newly added options
397 if (usedFlag) {
398 i -= 2;
399 } else {
400 i--;
401 }
402 } catch (ParamException& e) {
403 _log << "Solver parameter exception: " << e.msg() << endl;
404 return OPTION_ERROR;
405 }
406 }
407 }
408
409 // Process any registered factory flags
410 const auto& factoryFlags = get_global_solver_registry()->getFactoryFlags();
411 std::unordered_map<std::string, std::vector<std::string>> reducedSolverDefaults;
412 if (!factoryFlags.empty()) {
413 // Process solver default factory flags
414 for (const auto& factoryFlag : factoryFlags) {
415 auto factoryId = factoryFlag.second->getId();
416 if (reducedSolverDefaults.count(factoryId) == 0) {
417 reducedSolverDefaults.insert({factoryId, _solverConfigs.defaultOptions(factoryId)});
418 }
419 auto& defaultArgs = reducedSolverDefaults.find(factoryId)->second;
420 std::vector<std::string> keep;
421 for (i = 0; i < defaultArgs.size(); i++) {
422 if (defaultArgs[i] != factoryFlag.first ||
423 !factoryFlag.second->processFactoryOption(i, defaultArgs)) {
424 keep.push_back(defaultArgs[i]);
425 }
426 }
427 defaultArgs = keep;
428 }
429 // Process command line factory flags
430 std::vector<std::string> remaining = {argv[0]};
431 for (i = 1; i < argc; i++) {
432 bool ok = false;
433 if (argv[i] == "--push-working-directory") {
434 remaining.push_back(argv[i]);
435 i++;
436 workingDirs.push_back(argv[i]);
437 } else if (argv[i] == "--pop-working-directory") {
438 workingDirs.pop_back();
439 } else {
440 for (const auto& factoryFlag : factoryFlags) {
441 if (argv[i] == factoryFlag.first &&
442 factoryFlag.second->processFactoryOption(i, argv, workingDirs.back())) {
443 ok = true;
444 break;
445 }
446 }
447 }
448 if (!ok) {
449 remaining.push_back(argv[i]);
450 }
451 }
452 argv = remaining;
453 argc = remaining.size();
454 }
455 for (auto* sf : get_global_solver_registry()->getSolverFactories()) {
456 // Notify solver factories that factory flags are done
457 sf->factoryOptionsFinished();
458 }
459
460 // After this point all solver configurations must be available
461 _solverConfigs.populate(_log);
462
463 for (i = 1; i < argc; ++i) {
464 if (argv[i] == "-h" || argv[i] == "--help") {
465 if (argc > i + 1) {
466 printHelp(argv[i + 1]);
467 } else {
468 printHelp();
469 }
470 return OPTION_FINISH;
471 }
472 if (argv[i] == "--version") {
473 Flattener::printVersion(cout);
474 return OPTION_FINISH;
475 }
476 if (argv[i] == "--solvers") {
477 cout << "MiniZinc driver.\nAvailable solver configurations:\n";
478 std::vector<std::string> solvers = _solverConfigs.solvers();
479 if (solvers.empty()) {
480 cout << " none.\n";
481 }
482 for (auto& solver : solvers) {
483 cout << " " << solver << endl;
484 }
485 cout << "Search path for solver configurations:\n";
486 for (const string& p : _solverConfigs.solverConfigsPath()) {
487 cout << " " << p << endl;
488 }
489 return OPTION_FINISH;
490 }
491 if (argv[i] == "--solvers-json") {
492 cout << _solverConfigs.solverConfigsJSON();
493 return OPTION_FINISH;
494 }
495 if (argv[i] == "--solver-json") {
496 ++i;
497 if (i == argc) {
498 _log << "Argument required for --solver-json" << endl;
499 return OPTION_ERROR;
500 }
501 if (!solver.empty() && solver != argv[i]) {
502 _log << "Only one --solver-json option allowed" << endl;
503 return OPTION_ERROR;
504 }
505 solver = argv[i];
506 const SolverConfig& sc = _solverConfigs.config(solver);
507 cout << sc.toJSON(_solverConfigs);
508 return OPTION_FINISH;
509 }
510 if (argv[i] == "--config-dirs") {
511 GCLock lock;
512 cout << "{\n";
513 cout << " \"globalConfigFile\" : \""
514 << Printer::escapeStringLit(FileUtils::global_config_file()) << "\",\n";
515 cout << " \"userConfigFile\" : \"" << Printer::escapeStringLit(FileUtils::user_config_file())
516 << "\",\n";
517 cout << " \"userSolverConfigDir\" : \""
518 << Printer::escapeStringLit(FileUtils::user_config_dir()) << "/solvers\",\n";
519 cout << " \"mznStdlibDir\" : \"" << Printer::escapeStringLit(_solverConfigs.mznlibDir())
520 << "\"\n";
521 cout << "}\n";
522 return OPTION_FINISH;
523 }
524 if (argv[i] == "--time-limit") {
525 ++i;
526 if (i == argc) {
527 _log << "Argument required for --time-limit" << endl;
528 return OPTION_ERROR;
529 }
530 flagOverallTimeLimit = atoi(argv[i].c_str());
531 } else if (argv[i] == "--solver") {
532 ++i;
533 if (i == argc) {
534 _log << "Argument required for --solver" << endl;
535 return OPTION_ERROR;
536 }
537 if (!solver.empty() && solver != argv[i]) {
538 _log << "Only one --solver option allowed" << endl;
539 return OPTION_ERROR;
540 }
541 solver = argv[i];
542 } else if (argv[i] == "-c" || argv[i] == "--compile") {
543 _isMzn2fzn = true;
544 } else if (argv[i] == "-v" || argv[i] == "--verbose" || argv[i] == "-l") {
545 flagVerbose = true;
546 flagCompilerVerbose = true;
547 } else if (argv[i] == "--verbose-compilation") {
548 flagCompilerVerbose = true;
549 } else if (argv[i] == "-s" || argv[i] == "--statistics") {
550 flagStatistics = true;
551 flagCompilerStatistics = true;
552 } else if (argv[i] == "--compiler-statistics") {
553 flagCompilerStatistics = true;
554 } else {
555 if ((argv[i] == "--fzn-cmd" || argv[i] == "--flatzinc-cmd") && solver.empty()) {
556 solver = "org.minizinc.mzn-fzn";
557 }
558 if (argv[i] == "--compile-solution-checker") {
559 compileSolutionChecker = true;
560 }
561 if (argv[i] == "--ozn-file") {
562 flagIsSolns2out = true;
563 }
564 argv[j++] = argv[i];
565 }
566 }
567 argv.resize(j);
568 argc = j;
569
570 if ((mzn2fzn_exe || compileSolutionChecker) && solver.empty()) {
571 solver = "org.minizinc.mzn-fzn";
572 }
573
574 if (flagVerbose) {
575 argv.emplace_back("--verbose-solving");
576 argc++;
577 }
578 if (flagStatistics) {
579 argv.emplace_back("--solver-statistics");
580 argc++;
581 }
582
583 _flt.setFlagOutputByDefault(ifMzn2Fzn());
584
585 bool isMznMzn = false;
586
587 if (!flagIsSolns2out) {
588 try {
589 const SolverConfig& sc = _solverConfigs.config(solver);
590 string solverId;
591 if (sc.executable().empty()) {
592 solverId = sc.id();
593 } else if (sc.supportsMzn()) {
594 solverId = "org.minizinc.mzn-mzn";
595 } else if (sc.supportsFzn()) {
596 solverId = "org.minizinc.mzn-fzn";
597 } else if (sc.supportsNL()) {
598 solverId = "org.minizinc.mzn-nl";
599 } else {
600 _log << "Selected solver does not support MiniZinc, FlatZinc or NL input." << endl;
601 return OPTION_ERROR;
602 }
603
604 // Check support of -a and -i
605 for (const auto& flag : sc.stdFlags()) {
606 if (flag == "-a") {
607 _supportsA = true;
608 } else if (flag == "-i") {
609 _supportsI = true;
610 }
611 }
612
613 for (auto* it : get_global_solver_registry()->getSolverFactories()) {
614 if (it->getId() ==
615 solverId) { /// TODO: also check version (currently assumes all ids are unique)
616 _sf = it;
617 delete _siOpt;
618 _siOpt = _sf->createOptions();
619 if (!sc.executable().empty() || solverId == "org.minizinc.mzn-fzn" ||
620 solverId == "org.minizinc.mzn-nl") {
621 std::vector<MZNFZNSolverFlag> acceptedFlags;
622 for (const auto& sf : sc.stdFlags()) {
623 acceptedFlags.push_back(MZNFZNSolverFlag::std(sf));
624 }
625 for (const auto& ef : sc.extraFlags()) {
626 acceptedFlags.push_back(MZNFZNSolverFlag::extra(ef));
627 }
628
629 // Collect arguments required for underlying exe
630 vector<string> fzn_mzn_flags;
631 if (sc.needsStdlibDir()) {
632 fzn_mzn_flags.emplace_back("--stdlib-dir");
633 fzn_mzn_flags.push_back(FileUtils::share_directory());
634 }
635 if (sc.needsMznExecutable()) {
636 fzn_mzn_flags.emplace_back("--minizinc-exe");
637 fzn_mzn_flags.push_back(FileUtils::progpath() + "/" + _executableName);
638 }
639
640 if (sc.supportsMzn()) {
641 isMznMzn = true;
642 MZNSolverFactory::setAcceptedFlags(_siOpt, acceptedFlags);
643 std::vector<std::string> additionalArgs_s;
644 additionalArgs_s.emplace_back("-m");
645 if (!sc.executableResolved().empty()) {
646 additionalArgs_s.push_back(sc.executableResolved());
647 } else {
648 additionalArgs_s.push_back(sc.executable());
649 }
650
651 if (!fzn_mzn_flags.empty()) {
652 add_flags("--mzn-flag", fzn_mzn_flags, additionalArgs_s);
653 }
654
655 // This should maybe be moved to fill in fzn_mzn_flags when
656 // --find-muses is implemented (these arguments will be passed
657 // through to the subsolver of findMUS)
658 if (!sc.mznlib().empty()) {
659 if (sc.mznlib().substr(0, 2) == "-G") {
660 additionalArgs_s.emplace_back("--mzn-flag");
661 additionalArgs_s.push_back(sc.mznlib());
662 } else {
663 additionalArgs_s.emplace_back("--mzn-flag");
664 additionalArgs_s.emplace_back("-I");
665 additionalArgs_s.emplace_back("--mzn-flag");
666 std::string _mznlib;
667 if (!sc.mznlibResolved().empty()) {
668 _mznlib = sc.mznlibResolved();
669 } else {
670 _mznlib = sc.mznlib();
671 }
672 additionalArgs_s.push_back(_mznlib);
673 }
674 }
675
676 for (i = 0; i < additionalArgs_s.size(); ++i) {
677 bool success = _sf->processOption(_siOpt, i, additionalArgs_s);
678 if (!success) {
679 _log << "Solver backend " << solverId << " does not recognise option "
680 << additionalArgs_s[i] << "." << endl;
681 return OPTION_ERROR;
682 }
683 }
684 } else {
685 // supports fzn or nl
686 std::vector<std::string> additionalArgs;
687 if (sc.supportsFzn()) {
688 FZNSolverFactory::setAcceptedFlags(_siOpt, acceptedFlags);
689 additionalArgs.emplace_back("--fzn-cmd");
690 } else {
691 // supports nl
692 additionalArgs.emplace_back("--nl-cmd");
693 }
694 if (!sc.executableResolved().empty()) {
695 additionalArgs.push_back(sc.executableResolved());
696 } else {
697 additionalArgs.push_back(sc.executable());
698 }
699 if (!fzn_mzn_flags.empty()) {
700 if (sc.supportsFzn()) {
701 add_flags("--fzn-flag", fzn_mzn_flags, additionalArgs);
702 } else {
703 add_flags("--nl-flag", fzn_mzn_flags, additionalArgs);
704 }
705 }
706 if (sc.needsPathsFile()) {
707 // Instruct flattener to hold onto paths
708 int i = 0;
709 vector<string> args{"--keep-paths"};
710 _flt.processOption(i, args);
711
712 // Instruct FznSolverInstance to write a path file
713 // and pass it to the executable with --paths arg
714 additionalArgs.emplace_back("--fzn-needs-paths");
715 }
716 if (!sc.needsSolns2Out()) {
717 additionalArgs.emplace_back("--fzn-output-passthrough");
718 }
719 int i = 0;
720 for (i = 0; i < additionalArgs.size(); ++i) {
721 bool success = _sf->processOption(_siOpt, i, additionalArgs);
722 if (!success) {
723 _log << "Solver backend " << solverId << " does not recognise option "
724 << additionalArgs[i] << "." << endl;
725 return OPTION_ERROR;
726 }
727 }
728 }
729 }
730 if (!sc.mznlib().empty()) {
731 if (sc.mznlib().substr(0, 2) == "-G") {
732 std::vector<std::string> additionalArgs({sc.mznlib()});
733 int i = 0;
734 if (!_flt.processOption(i, additionalArgs)) {
735 _log << "Flattener does not recognise option " << sc.mznlib() << endl;
736 return OPTION_ERROR;
737 }
738 } else {
739 std::vector<std::string> additionalArgs(2);
740 additionalArgs[0] = "-I";
741 if (!sc.mznlibResolved().empty()) {
742 additionalArgs[1] = sc.mznlibResolved();
743 } else {
744 additionalArgs[1] = sc.mznlib();
745 }
746 int i = 0;
747 if (!_flt.processOption(i, additionalArgs)) {
748 _log << "Flattener does not recognise option -I." << endl;
749 return OPTION_ERROR;
750 }
751 }
752 }
753 auto reducedDefaultFlags = reducedSolverDefaults.find(sc.id());
754 const auto& defaultFlags = reducedDefaultFlags == reducedSolverDefaults.end()
755 ? sc.defaultFlags()
756 : reducedDefaultFlags->second;
757 if (!defaultFlags.empty()) {
758 std::vector<std::string> addedArgs;
759 addedArgs.push_back(argv[0]); // excutable name
760 for (const auto& df : defaultFlags) {
761 addedArgs.push_back(df);
762 }
763 for (int i = 1; i < argv.size(); i++) {
764 addedArgs.push_back(argv[i]);
765 }
766 argv = addedArgs;
767 argc = addedArgs.size();
768 }
769 break;
770 }
771 }
772
773 } catch (ConfigException& e) {
774 _log << "Config exception: " << e.msg() << endl;
775 return OPTION_ERROR;
776 }
777
778 if (_sf == nullptr) {
779 _log << "Solver " << solver << " not found." << endl;
780 return OPTION_ERROR;
781 }
782
783 CLOParser cop(i, argv); // For special handling of -a, -i and -n-i
784 for (i = 1; i < argc; ++i) {
785 if (argv[i] == "--push-working-directory") {
786 i++;
787 workingDirs.push_back(argv[i]);
788 } else if (argv[i] == "--pop-working-directory") {
789 workingDirs.pop_back();
790 } else if (!ifMzn2Fzn() ? s2out.processOption(i, argv, workingDirs.back())
791 : false) { // NOLINT: Allow repeated empty if
792 // Processed by Solns2Out
793 } else if ((!isMznMzn || _isMzn2fzn) &&
794 _flt.processOption(i, argv,
795 workingDirs.back())) { // NOLINT: Allow repeated empty if
796 // Processed by Flattener
797 } else if ((_supportsA || _supportsI) && cop.get("-a --all --all-solns --all-solutions")) {
798 _flagAllSatisfaction = true;
799 _flagIntermediate = true;
800 } else if ((_supportsA || _supportsI) &&
801 cop.get("-i --intermediate --intermediate-solutions")) {
802 _flagIntermediate = true;
803 } else if (cop.getOption("-n-i --no-intermediate --no-intermediate-solutions")) {
804 _flagIntermediate = false;
805 } else if (_supportsA && cop.get("--all-satisfaction")) {
806 _flagAllSatisfaction = true;
807 } else if (cop.get("--disable-all-satisfaction")) {
808 _flagAllSatisfaction = false;
809 } else if (_sf != nullptr &&
810 _sf->processOption(_siOpt, i, argv)) { // NOLINT: Allow repeated empty if
811 // Processed by Solver Factory
812 } else {
813 std::string executable_name(argv[0]);
814 executable_name = executable_name.substr(executable_name.find_last_of("/\\") + 1);
815 _log << executable_name << ": Unrecognized option or bad format `" << argv[i] << "'"
816 << endl;
817 return OPTION_ERROR;
818 }
819 }
820 return OPTION_OK;
821 }
822 for (i = 1; i < argc; ++i) {
823 if (argv[i] == "--push-working-directory") {
824 i++;
825 workingDirs.push_back(argv[i]);
826 } else if (argv[i] == "--pop-working-directory") {
827 workingDirs.pop_back();
828 } else if (s2out.processOption(i, argv, workingDirs.back())) {
829 // Processed by Solns2Out
830 } else {
831 std::string executable_name(argv[0]);
832 executable_name = executable_name.substr(executable_name.find_last_of("/\\") + 1);
833 _log << executable_name << ": Unrecognized option or bad format `" << argv[i] << "'" << endl;
834 return OPTION_ERROR;
835 }
836 }
837 return OPTION_OK;
838}
839
840void MznSolver::flatten(const std::string& modelString, const std::string& modelName) {
841 _flt.setFlagVerbose(flagCompilerVerbose);
842 _flt.setFlagStatistics(flagCompilerStatistics);
843 _flt.setFlagTimelimit(flagOverallTimeLimit);
844 _flt.flatten(modelString, modelName);
845}
846
847SolverInstance::Status MznSolver::solve() {
848 { // To be able to clean up flatzinc after PrcessFlt()
849 GCLock lock;
850 getSI()->processFlatZinc();
851 }
852 SolverInstance::Status status = getSI()->solve();
853 GCLock lock;
854 if (!getSI()->getSolns2Out()->fStatusPrinted) {
855 getSI()->getSolns2Out()->evalStatus(status);
856 }
857 if (_siOpt->printStatistics) {
858 getSI()->printStatistics();
859 }
860 if (flagStatistics) {
861 getSI()->getSolns2Out()->printStatistics(_os);
862 }
863 return status;
864}
865
866SolverInstance::Status MznSolver::run(const std::vector<std::string>& args0,
867 const std::string& model, const std::string& exeName,
868 const std::string& modelName) {
869 using namespace std::chrono;
870 steady_clock::time_point startTime = steady_clock::now();
871 std::vector<std::string> args = {exeName};
872 for (const auto& a : args0) {
873 args.push_back(a);
874 }
875 switch (processOptions(args)) {
876 case OPTION_FINISH:
877 return SolverInstance::NONE;
878 case OPTION_ERROR:
879 printUsage();
880 _os << "More info with \"" << exeName << " --help\"\n";
881 return SolverInstance::ERROR;
882 case OPTION_OK:
883 break;
884 }
885 if (flagIsSolns2out &&
886 (ifMzn2Fzn() || _sf == nullptr || _sf->getId() != "org.minizinc.mzn-mzn") &&
887 !_flt.hasInputFiles() && model.empty()) {
888 // We are in solns2out mode
889 while (std::cin.good()) {
890 string line;
891 getline(std::cin, line);
892 line += '\n'; // need eols as in t=raw stream
893 s2out.feedRawDataChunk(line.c_str());
894 }
895 return SolverInstance::NONE;
896 }
897
898 if (!ifMzn2Fzn() && _sf->getId() == "org.minizinc.mzn-mzn") {
899 Env env;
900 _si = _sf->createSI(env, _log, _siOpt);
901 _si->setSolns2Out(&s2out);
902 { // To be able to clean up flatzinc after PrcessFlt()
903 GCLock lock;
904 _si->options()->verbose = getFlagVerbose();
905 _si->options()->printStatistics = getFlagStatistics();
906 }
907 _si->solve();
908 return SolverInstance::NONE;
909 }
910
911 try {
912 flatten(model, modelName);
913 } catch (Timeout&) {
914 s2out.evalStatus(SolverInstance::UNKNOWN);
915 return SolverInstance::UNKNOWN;
916 }
917
918 if (!ifMzn2Fzn() && flagOverallTimeLimit != 0) {
919 steady_clock::time_point afterFlattening = steady_clock::now();
920 milliseconds passed = duration_cast<milliseconds>(afterFlattening - startTime);
921 milliseconds time_limit(flagOverallTimeLimit);
922 if (passed > time_limit) {
923 s2out.evalStatus(getFltStatus());
924 return SolverInstance::UNKNOWN;
925 }
926 int time_left = (time_limit - passed).count();
927 std::vector<std::string> timeoutArgs(2);
928 timeoutArgs[0] = "--solver-time-limit";
929 std::ostringstream oss;
930 oss << time_left;
931 timeoutArgs[1] = oss.str();
932 int i = 0;
933 _sf->processOption(_siOpt, i, timeoutArgs);
934 }
935
936 if (SolverInstance::UNKNOWN == getFltStatus()) {
937 if (!ifMzn2Fzn()) { // only then
938 // Special handling of basic stdFlags
939 auto* solve_item = _flt.getEnv()->model()->solveItem();
940 bool is_sat_problem =
941 solve_item != nullptr ? solve_item->st() == SolveI::SolveType::ST_SAT : true;
942 if (is_sat_problem && _flagAllSatisfaction) {
943 if (_supportsA) {
944 std::vector<std::string> a_flag = {"-a"};
945 int i = 0;
946 _sf->processOption(_siOpt, i, a_flag);
947 } else {
948 // Solver does not support -a
949 _log << "WARNING: Solver does not support all solutions for satisfaction problems."
950 << endl;
951 }
952 }
953 if (!is_sat_problem && _flagIntermediate) {
954 std::vector<std::string> i_flag(1);
955 i_flag[0] = _supportsI ? "-i" : "-a"; // Fallback to -a if -i is not supported
956 int i = 0;
957 _sf->processOption(_siOpt, i, i_flag);
958 }
959
960 // GCLock lock; // better locally, to enable cleanup after ProcessFlt()
961 addSolverInterface();
962 return solve();
963 }
964 return SolverInstance::NONE;
965 }
966 if (!ifMzn2Fzn()) {
967 s2out.evalStatus(getFltStatus());
968 }
969 return getFltStatus();
970 // Add evalOutput() here? TODO
971}