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/process.hh>
17 #include <minizinc/solvers/mzn_solverinstance.hh>
18 #include <minizinc/timer.hh>
19 
20 #include <cstdio>
21 #include <fstream>
22 
23 using namespace std;
24 
25 namespace MiniZinc {
26 
MZNSolverFactory()27 MZNSolverFactory::MZNSolverFactory() {
28   SolverConfig sc("org.minizinc.mzn-mzn",
29                   MZN_VERSION_MAJOR "." MZN_VERSION_MINOR "." MZN_VERSION_PATCH);
30   sc.name("Generic MiniZinc driver");
31   sc.mznlibVersion(1);
32   sc.description("MiniZinc generic MiniZinc solver plugin");
33   sc.requiredFlags({"-m"});
34   sc.tags({"__internal__"});
35   sc.supportsFzn(false);
36   sc.supportsMzn(true);
37   sc.needsSolns2Out(false);
38   SolverConfigs::registerBuiltinSolver(sc);
39 }
40 
getDescription(SolverInstanceBase::Options *)41 string MZNSolverFactory::getDescription(SolverInstanceBase::Options* /*opt*/) {
42   string v = "MZN solver plugin, compiled  " __DATE__ "  " __TIME__;
43   return v;
44 }
45 
getVersion(SolverInstanceBase::Options *)46 string MZNSolverFactory::getVersion(SolverInstanceBase::Options* /*opt*/) {
47   return MZN_VERSION_MAJOR;
48 }
49 
getId()50 string MZNSolverFactory::getId() { return "org.minizinc.mzn-mzn"; }
51 
printHelp(ostream & os)52 void MZNSolverFactory::printHelp(ostream& os) {
53   os << "MZN-MZN plugin options:" << std::endl
54      << "  -m, --minizinc-cmd <exe>\n     the backend solver filename.\n"
55      << "  --mzn-flags <options>, --minizinc-flags <options>, --backend-flags <options>\n"
56         "     Specify option to be passed to the MiniZinc interpreter.\n"
57      << "  --mzn-flag <option>, --minizinc-flag <option>, --backend-flag <option>\n"
58         "     As above, but for a single option string that need to be quoted in a shell.\n"
59      << "  -t <ms>, --solver-time-limit <ms>, --mzn-time-limit <ms>\n"
60         "     Set time limit for solving.\n"
61      << "  --mzn-sigint\n     Send SIGINT instead of SIGTERM.\n";
62 }
63 
createOptions()64 SolverInstanceBase::Options* MZNSolverFactory::createOptions() { return new MZNSolverOptions; }
65 
doCreateSI(Env & env,std::ostream & log,SolverInstanceBase::Options * opt)66 SolverInstanceBase* MZNSolverFactory::doCreateSI(Env& env, std::ostream& log,
67                                                  SolverInstanceBase::Options* opt) {
68   return new MZNSolverInstance(env, log, opt);
69 }
70 
setAcceptedFlags(SolverInstanceBase::Options * opt,const std::vector<MZNFZNSolverFlag> & flags)71 void MZNSolverFactory::setAcceptedFlags(SolverInstanceBase::Options* opt,
72                                         const std::vector<MZNFZNSolverFlag>& flags) {
73   auto& _opt = static_cast<MZNSolverOptions&>(*opt);
74   _opt.mznSolverFlags.clear();
75   for (const auto& f : flags) {
76     if (f.n == "-t") {
77       _opt.supportsT = true;
78     } else {
79       _opt.mznSolverFlags.push_back(f);
80     }
81   }
82 }
83 
processOption(SolverInstanceBase::Options * opt,int & i,std::vector<std::string> & argv,const std::string & workingDir)84 bool MZNSolverFactory::processOption(SolverInstanceBase::Options* opt, int& i,
85                                      std::vector<std::string>& argv,
86                                      const std::string& workingDir) {
87   auto& _opt = static_cast<MZNSolverOptions&>(*opt);
88   CLOParser cop(i, argv);
89   string buffer;
90   int nn = -1;
91 
92   if (cop.getOption("-m --minizinc-cmd", &buffer)) {
93     _opt.mznSolver = buffer;
94   } else if (cop.getOption("--mzn-flags --minizinc-flags --backend-flags", &buffer)) {
95     std::vector<std::string> cmdLine = FileUtils::parse_cmd_line(buffer);
96     for (auto& s : cmdLine) {
97       _opt.mznFlags.push_back(s);
98     }
99   } else if (cop.getOption("-t --solver-time-limit --mzn-time-limit", &nn)) {
100     _opt.mznTimeLimitMilliseconds = nn;
101     if (_opt.supportsT) {
102       _opt.solverTimeLimitMilliseconds = nn;
103       _opt.mznTimeLimitMilliseconds += 1000;  // kill 1 second after solver should have stopped
104     }
105   } else if (cop.getOption("--mzn-sigint")) {
106     _opt.mznSigint = true;
107   } else if (cop.getOption("--mzn-flag --minizinc-flag --backend-flag", &buffer)) {
108     _opt.mznFlags.push_back(buffer);
109   } else if (cop.getOption("--solver-statistics")) {
110     _opt.printStatistics = true;
111   } else if (cop.getOption("--verbose-solving")) {
112     _opt.verbose = true;
113   } else {
114     for (auto& mznf : _opt.mznSolverFlags) {
115       if (mznf.t == MZNFZNSolverFlag::FT_ARG && cop.getOption(mznf.n.c_str(), &buffer)) {
116         _opt.mznFlags.push_back(mznf.n);
117         _opt.mznFlags.push_back(buffer);
118         return true;
119       }
120       if (mznf.t == MZNFZNSolverFlag::FT_NOARG && cop.getOption(mznf.n.c_str())) {
121         _opt.mznFlags.push_back(mznf.n);
122         return true;
123       }
124     }
125     std::string input_file(argv[i]);
126     if (input_file.length() <= 4) {
127       return false;
128     }
129     size_t last_dot = input_file.find_last_of('.');
130     if (last_dot == string::npos) {
131       return false;
132     }
133     std::string extension = input_file.substr(last_dot, string::npos);
134     if (extension == ".mzn" || extension == ".mzc" || extension == ".fzn" || extension == ".dzn" ||
135         extension == ".json") {
136       _opt.mznFlags.push_back(input_file);
137     } else {
138       return false;
139     }
140   }
141   return true;
142 }
143 
MZNSolverInstance(Env & env,std::ostream & log,SolverInstanceBase::Options * options)144 MZNSolverInstance::MZNSolverInstance(Env& env, std::ostream& log,
145                                      SolverInstanceBase::Options* options)
146     : SolverInstanceBase(env, log, options) {}
147 
~MZNSolverInstance()148 MZNSolverInstance::~MZNSolverInstance() {}
149 
solve()150 SolverInstance::Status MZNSolverInstance::solve() {
151   auto& opt = static_cast<MZNSolverOptions&>(*_options);
152   if (opt.mznSolver.empty()) {
153     throw InternalError("No MiniZinc solver specified");
154   }
155   /// Passing options to solver
156   vector<string> cmd_line;
157   cmd_line.push_back(opt.mznSolver);
158   for (auto& f : opt.mznFlags) {
159     cmd_line.push_back(f);
160   }
161   if (opt.printStatistics) {
162     cmd_line.emplace_back("-s");
163   }
164   if (opt.verbose) {
165     cmd_line.emplace_back("-v");
166     _log << "Using MZN solver " << cmd_line[0] << " for solving, parameters: ";
167     for (int i = 1; i < cmd_line.size(); ++i) {
168       _log << "" << cmd_line[i] << " ";
169     }
170     _log << std::endl;
171   }
172   if (opt.solverTimeLimitMilliseconds != 0) {
173     cmd_line.emplace_back("-t");
174     std::ostringstream oss;
175     oss << opt.solverTimeLimitMilliseconds;
176     cmd_line.push_back(oss.str());
177   }
178   int timelimit = opt.mznTimeLimitMilliseconds;
179   bool sigint = opt.mznSigint;
180   Solns2Log s2l(getSolns2Out()->getOutput(), _log);
181   Process<Solns2Log> proc(cmd_line, &s2l, timelimit, sigint);
182   int exitCode = proc.run();
183 
184   return exitCode == 0 ? SolverInstance::UNKNOWN : SolverInstance::ERROR;
185 }
186 
processFlatZinc()187 void MZNSolverInstance::processFlatZinc() {}
188 
resetSolver()189 void MZNSolverInstance::resetSolver() {}
190 
191 }  // namespace MiniZinc
192