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