1 /* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
2 
3 /*
4  *  Main authors:
5  *     Guido Tack <guido.tack@monash.edu>
6  */
7 
8 /* This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
11 
12 #ifdef _MSC_VER
13 #define _CRT_SECURE_NO_WARNINGS
14 #endif
15 
16 #include <minizinc/builtins.hh>
17 #include <minizinc/eval_par.hh>
18 #include <minizinc/parser.hh>
19 #include <minizinc/pathfileprinter.hh>
20 #include <minizinc/prettyprinter.hh>
21 #include <minizinc/process.hh>
22 #include <minizinc/solvers/fzn_solverinstance.hh>
23 #include <minizinc/timer.hh>
24 #include <minizinc/typecheck.hh>
25 
26 #include <cstdio>
27 #include <fstream>
28 
29 using namespace std;
30 
31 namespace MiniZinc {
32 
FZNSolverFactory()33 FZNSolverFactory::FZNSolverFactory() {
34   SolverConfig sc("org.minizinc.mzn-fzn",
35                   MZN_VERSION_MAJOR "." MZN_VERSION_MINOR "." MZN_VERSION_PATCH);
36   sc.name("Generic FlatZinc driver");
37   sc.mznlibVersion(1);
38   sc.description("MiniZinc generic FlatZinc solver plugin");
39   sc.requiredFlags({"--fzn-cmd"});
40   sc.stdFlags({"-a", "-n", "-f", "-p", "-s", "-r", "-v"});
41   sc.tags({"__internal__"});
42   SolverConfigs::registerBuiltinSolver(sc);
43 }
44 
getDescription(SolverInstanceBase::Options *)45 string FZNSolverFactory::getDescription(SolverInstanceBase::Options* /*opt*/) {
46   string v = "FZN solver plugin, compiled  " __DATE__ "  " __TIME__;
47   return v;
48 }
49 
getVersion(SolverInstanceBase::Options *)50 string FZNSolverFactory::getVersion(SolverInstanceBase::Options* /*opt*/) {
51   return MZN_VERSION_MAJOR;
52 }
53 
getId()54 string FZNSolverFactory::getId() { return "org.minizinc.mzn-fzn"; }
55 
printHelp(ostream & os)56 void FZNSolverFactory::printHelp(ostream& os) {
57   os << "MZN-FZN plugin options:" << std::endl
58      << "  --fzn-cmd , --flatzinc-cmd <exe>\n     the backend solver filename.\n"
59      << "  -b, --backend, --solver-backend <be>\n     the backend codename. Currently passed to "
60         "the solver.\n"
61      << "  --fzn-flags <options>, --flatzinc-flags <options> --backend-flags <options>\n"
62         "     Specify option to be passed to the FlatZinc interpreter.\n"
63      << "  --fzn-flag <option>, --flatzinc-flag <option>, --backend-flag\n"
64         "     As above, but for a single option string that need to be quoted in a shell.\n"
65      << "  -t <ms>, --solver-time-limit <ms>, --fzn-time-limit <ms>\n     Set time limit (in "
66         "milliseconds) for solving.\n"
67      << "  --fzn-sigint\n     Send SIGINT instead of SIGTERM.\n"
68      << "  -n <n>, --num-solutions <n>\n"
69      << "    An upper bound on the number of solutions to output for satisfaction problems. The "
70         "default should be 1.\n"
71      << "  -a, --all, --all-solns, --all-solutions\n     Print all solutions for satisfaction "
72         "problems and intermediate solutions for optimization problems.\n"
73      << "  -i, --intermediate --intermediate-solutions\n    Print intermediate solutions for "
74         "optimisation problems.\n"
75      << "  -n-i, --no-intermediate --no-intermediate-solutions\n    Don't print intermediate "
76         "solutions for optimisation problems.\n"
77      << "  --all-satisfaction\n    Print all solutions for satisfaction problems.\n"
78      << "  --disable-all-satisfaction\n    Don't print all solutions for satisfaction problems.\n"
79      << "  -n-o <n>, --num-opt-solutions <n>\n"
80      << "    An upper bound on the number of optimal solutions to output for optimisation "
81         "problems. The default should be 1.\n"
82      << "  -a-o, --all-opt, --all-optimal\n     Print all optimal solutions for optimisation "
83         "problems.\n"
84      << "  -p <n>, --parallel <n>\n     Use <n> threads during search. The default is "
85         "solver-dependent.\n"
86      << "  -k, --keep-files\n     For compatibility only: to produce .ozn and .fzn, use mzn2fzn\n"
87         "     or <this_exe> --fzn ..., --ozn ...\n"
88      << "  -r <n>, --seed <n>, --random-seed <n>\n     For compatibility only: use solver flags "
89         "instead.\n"
90      << "  --cp-profiler <id>,<port>\n    Send search to cp-profiler with given execution ID and "
91         "port.\n";
92 }
93 
createOptions()94 SolverInstanceBase::Options* FZNSolverFactory::createOptions() { return new FZNSolverOptions; }
95 
doCreateSI(Env & env,std::ostream & log,SolverInstanceBase::Options * opt)96 SolverInstanceBase* FZNSolverFactory::doCreateSI(Env& env, std::ostream& log,
97                                                  SolverInstanceBase::Options* opt) {
98   return new FZNSolverInstance(env, log, opt);
99 }
100 
processOption(SolverInstanceBase::Options * opt,int & i,std::vector<std::string> & argv,const std::string & workingDir)101 bool FZNSolverFactory::processOption(SolverInstanceBase::Options* opt, int& i,
102                                      std::vector<std::string>& argv,
103                                      const std::string& workingDir) {
104   auto& _opt = static_cast<FZNSolverOptions&>(*opt);
105   CLOParser cop(i, argv);
106   string buffer;
107   int nn = -1;
108 
109   if (cop.getOption("--fzn-cmd --flatzinc-cmd", &buffer)) {
110     _opt.fznSolver = buffer;
111   } else if (cop.getOption("-b --backend --solver-backend", &buffer)) {
112     _opt.backend = buffer;
113   } else if (cop.getOption("--fzn-flags --flatzinc-flags --backend-flags", &buffer)) {
114     std::vector<std::string> cmdLine = FileUtils::parse_cmd_line(buffer);
115     for (auto& s : cmdLine) {
116       _opt.fznFlags.push_back(s);
117     }
118   } else if (cop.getOption("-t --solver-time-limit --fzn-time-limit", &nn)) {
119     _opt.fznTimeLimitMilliseconds = nn;
120     if (_opt.supportsT) {
121       _opt.solverTimeLimitMilliseconds = nn;
122       _opt.fznTimeLimitMilliseconds += 1000;  // kill 1 second after solver should have stopped
123     }
124   } else if (cop.getOption("--fzn-sigint")) {
125     _opt.fznSigint = true;
126   } else if (cop.getOption("--fzn-needs-paths")) {
127     _opt.fznNeedsPaths = true;
128   } else if (cop.getOption("--fzn-output-passthrough")) {
129     _opt.fznOutputPassthrough = true;
130   } else if (cop.getOption("--fzn-flag --flatzinc-flag --backend-flag", &buffer)) {
131     _opt.fznFlags.push_back(buffer);
132   } else if (_opt.supportsN && cop.getOption("-n --num-solutions", &nn)) {
133     _opt.numSols = nn;
134   } else if (cop.getOption("-a")) {
135     _opt.fznFlags.emplace_back("-a");
136   } else if (cop.getOption("-i")) {
137     _opt.fznFlags.emplace_back("-i");
138   } else if (_opt.supportsNO && cop.getOption("-n-o --num-optimal", &nn)) {
139     _opt.numOptimal = (nn != 0);
140   } else if (_opt.supportsAO && cop.getOption("-a-o --all-opt --all-optimal")) {
141     _opt.allOptimal = true;
142   } else if (cop.getOption("-p --parallel", &nn)) {
143     if (_opt.supportsP) {
144       _opt.parallel = to_string(nn);
145     }
146   } else if (cop.getOption("-k --keep-files")) {
147     // Deprecated option! Does nothing.
148   } else if (cop.getOption("-r --seed --random-seed", &buffer)) {
149     if (_opt.supportsR) {
150       _opt.fznFlags.emplace_back("-r");
151       _opt.fznFlags.push_back(buffer);
152     }
153   } else if (cop.getOption("-s --solver-statistics")) {
154     if (_opt.supportsS) {
155       _opt.printStatistics = true;
156     }
157   } else if (cop.getOption("-v --verbose-solving")) {
158     _opt.verbose = true;
159   } else if (cop.getOption("-f --free-search")) {
160     if (_opt.supportsF) {
161       _opt.fznFlags.emplace_back("-f");
162     }
163   } else if (_opt.supportsCpprofiler && cop.getOption("--cp-profiler", &buffer)) {
164     _opt.fznFlags.emplace_back("--cp-profiler");
165     _opt.fznFlags.push_back(buffer);
166   } else {
167     for (auto& fznf : _opt.fznSolverFlags) {
168       if (fznf.t == MZNFZNSolverFlag::FT_ARG && cop.getOption(fznf.n.c_str(), &buffer)) {
169         _opt.fznFlags.push_back(fznf.n);
170         _opt.fznFlags.push_back(buffer);
171         return true;
172       }
173       if (fznf.t == MZNFZNSolverFlag::FT_NOARG && cop.getOption(fznf.n.c_str())) {
174         _opt.fznFlags.push_back(fznf.n);
175         return true;
176       }
177     }
178 
179     return false;
180   }
181   return true;
182 }
183 
setAcceptedFlags(SolverInstanceBase::Options * opt,const std::vector<MZNFZNSolverFlag> & flags)184 void FZNSolverFactory::setAcceptedFlags(SolverInstanceBase::Options* opt,
185                                         const std::vector<MZNFZNSolverFlag>& flags) {
186   auto& _opt = static_cast<FZNSolverOptions&>(*opt);
187   _opt.fznSolverFlags.clear();
188   for (const auto& f : flags) {
189     if (f.n == "-a") {
190       _opt.supportsA = true;
191     } else if (f.n == "-n") {
192       _opt.supportsN = true;
193     } else if (f.n == "-f") {
194       _opt.supportsF = true;
195     } else if (f.n == "-p") {
196       _opt.supportsP = true;
197     } else if (f.n == "-s") {
198       _opt.supportsS = true;
199     } else if (f.n == "-r") {
200       _opt.supportsR = true;
201     } else if (f.n == "-v") {
202       _opt.supportsV = true;
203     } else if (f.n == "-t") {
204       _opt.supportsT = true;
205     } else if (f.n == "-i") {
206       _opt.supportsI = true;
207     } else if (f.n == "-n-o") {
208       _opt.supportsNO = true;
209     } else if (f.n == "-a-o") {
210       _opt.supportsAO = true;
211     } else if (f.n == "--cp-profiler") {
212       _opt.supportsCpprofiler = true;
213     } else {
214       _opt.fznSolverFlags.push_back(f);
215     }
216   }
217 }
218 
FZNSolverInstance(Env & env,std::ostream & log,SolverInstanceBase::Options * options)219 FZNSolverInstance::FZNSolverInstance(Env& env, std::ostream& log,
220                                      SolverInstanceBase::Options* options)
221     : SolverInstanceBase(env, log, options), _fzn(env.flat()), _ozn(env.output()) {}
222 
~FZNSolverInstance()223 FZNSolverInstance::~FZNSolverInstance() {}
224 
solve()225 SolverInstance::Status FZNSolverInstance::solve() {
226   auto& opt = static_cast<FZNSolverOptions&>(*_options);
227   if (opt.fznSolver.empty()) {
228     throw InternalError("No FlatZinc solver specified");
229   }
230   /// Passing options to solver
231   vector<string> cmd_line;
232   cmd_line.push_back(opt.fznSolver);
233   string sBE = opt.backend;
234   bool is_sat = _fzn->solveItem()->st() == SolveI::SolveType::ST_SAT;
235   if (!sBE.empty()) {
236     cmd_line.emplace_back("-b");
237     cmd_line.push_back(sBE);
238   }
239   for (auto& f : opt.fznFlags) {
240     cmd_line.push_back(f);
241   }
242   if (opt.allOptimal && !is_sat) {
243     cmd_line.emplace_back("-a-o");
244   }
245   if (static_cast<int>(opt.numOptimal) != 1 && !is_sat) {
246     cmd_line.emplace_back("-n-o");
247     ostringstream oss;
248     oss << opt.numOptimal;
249     cmd_line.push_back(oss.str());
250   }
251   if (opt.numSols != 1 && is_sat) {
252     cmd_line.emplace_back("-n");
253     ostringstream oss;
254     oss << opt.numSols;
255     cmd_line.push_back(oss.str());
256   }
257   if (!opt.parallel.empty()) {
258     cmd_line.emplace_back("-p");
259     ostringstream oss;
260     oss << opt.parallel;
261     cmd_line.push_back(oss.str());
262   }
263   if (opt.printStatistics) {
264     cmd_line.emplace_back("-s");
265   }
266   if (opt.solverTimeLimitMilliseconds != 0) {
267     cmd_line.emplace_back("-t");
268     std::ostringstream oss;
269     oss << opt.solverTimeLimitMilliseconds;
270     cmd_line.push_back(oss.str());
271   }
272   if (opt.verbose) {
273     if (opt.supportsV) {
274       cmd_line.emplace_back("-v");
275     }
276     std::cerr << "Using FZN solver " << cmd_line[0] << " for solving, parameters: ";
277     for (int i = 1; i < cmd_line.size(); ++i) {
278       cerr << "" << cmd_line[i] << " ";
279     }
280     cerr << std::endl;
281   }
282   int timelimit = opt.fznTimeLimitMilliseconds;
283   bool sigint = opt.fznSigint;
284 
285   FileUtils::TmpFile fznFile(".fzn");
286   std::ofstream os(FILE_PATH(fznFile.name()));
287   Printer p(os, 0, true);
288   for (FunctionIterator it = _fzn->functions().begin(); it != _fzn->functions().end(); ++it) {
289     if (!it->removed()) {
290       Item& item = *it;
291       p.print(&item);
292     }
293   }
294   for (VarDeclIterator it = _fzn->vardecls().begin(); it != _fzn->vardecls().end(); ++it) {
295     if (!it->removed()) {
296       Item& item = *it;
297       p.print(&item);
298     }
299   }
300   for (ConstraintIterator it = _fzn->constraints().begin(); it != _fzn->constraints().end(); ++it) {
301     if (!it->removed()) {
302       Item& item = *it;
303       p.print(&item);
304     }
305   }
306   p.print(_fzn->solveItem());
307   cmd_line.push_back(fznFile.name());
308 
309   FileUtils::TmpFile* pathsFile = nullptr;
310   if (opt.fznNeedsPaths) {
311     pathsFile = new FileUtils::TmpFile(".paths");
312     std::ofstream ofs(FILE_PATH(pathsFile->name()));
313     PathFilePrinter pfp(ofs, _env.envi());
314     pfp.print(_fzn);
315 
316     cmd_line.emplace_back("--paths");
317     cmd_line.push_back(pathsFile->name());
318   }
319 
320   if (!opt.fznOutputPassthrough) {
321     Process<Solns2Out> proc(cmd_line, getSolns2Out(), timelimit, sigint);
322     int exitStatus = proc.run();
323     delete pathsFile;
324     return exitStatus == 0 ? getSolns2Out()->status : SolverInstance::ERROR;
325   }
326   Solns2Log s2l(getSolns2Out()->getOutput(), _log);
327   Process<Solns2Log> proc(cmd_line, &s2l, timelimit, sigint);
328   int exitStatus = proc.run();
329   delete pathsFile;
330   return exitStatus == 0 ? SolverInstance::NONE : SolverInstance::ERROR;
331 }
332 
processFlatZinc()333 void FZNSolverInstance::processFlatZinc() {}
334 
resetSolver()335 void FZNSolverInstance::resetSolver() {}
336 
getSolutionValue(Id * id)337 Expression* FZNSolverInstance::getSolutionValue(Id* id) {
338   assert(false);
339   return nullptr;
340 }
341 }  // namespace MiniZinc
342