1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
|
/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <minizinc/file_utils.hh>
#include <minizinc/process.hh>
#include <minizinc/solvers/nl/nl_file.hh>
#include <minizinc/solvers/nl/nl_solreader.hh>
#include <minizinc/solvers/nl/nl_solverinstance.hh>
#include <cstdio>
#include <cstring>
#include <fstream>
using namespace std;
namespace MiniZinc {
/** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
* **/
// Solver Factory
NLSolverFactory::NLSolverFactory() {
SolverConfig sc("org.minizinc.mzn-nl",
MZN_VERSION_MAJOR "." MZN_VERSION_MINOR "." MZN_VERSION_PATCH);
sc.name("Generic Non Linear driver");
sc.mznlibVersion(1);
sc.description("MiniZinc generic Non Linear solver plugin");
sc.inputType(SolverConfig::O_NL);
sc.requiredFlags({"--nl-cmd"});
sc.tags({"__internal__"});
SolverConfigs::registerBuiltinSolver(sc);
}
string NLSolverFactory::getDescription(SolverInstanceBase::Options* /*opt*/) {
string v = "NL solver plugin";
return v;
}
string NLSolverFactory::getVersion(SolverInstanceBase::Options* /*opt*/) {
return MZN_VERSION_MAJOR;
}
string NLSolverFactory::getId() { return "org.minizinc.mzn-nl"; }
void NLSolverFactory::printHelp(ostream& os) {
os << "MZN-NL plugin options" << std::endl
<< " --nl-cmd , --nonlinear-cmd <exe>\n The backend solver filename.\n"
<< " --nl-flags <options>, --backend-flags <options>\n"
" Specify option to be passed to the NL solver.\n"
<< " --nl-flag <option>, --backend-flag <option>\n"
" As above, but for a single option string that needs to be quoted in a shell.\n"
<< " --hexafloat\n Use hexadecimal format when communicating floating points with the "
"solver.\n"
<< " --keepfile\n Write the nl and sol files next to the input file and don't remove "
"them.\n"
// << " --nl-sigint\n Send SIGINT instead of SIGTERM.\n"
// << " -t <ms>, --solver-time-limit <ms>, --fzn-time-limit <ms>\n Set time limit (in
// milliseconds) for solving.\n"
// << " -p <n>, --parallel <n>\n Use <n> threads during search. The default is
// solver-dependent.\n"
// << " -r <n>, --seed <n>, --random-seed <n>\n For compatibility only: use solver flags
// instead.\n"
;
}
SolverInstanceBase::Options* NLSolverFactory::createOptions() { return new NLSolverOptions; }
SolverInstanceBase* NLSolverFactory::doCreateSI(Env& env, std::ostream& log,
SolverInstanceBase::Options* opt) {
return new NLSolverInstance(env, log, opt);
}
bool NLSolverFactory::processOption(SolverInstanceBase::Options* opt, int& i,
std::vector<std::string>& argv, const std::string& workingDir) {
auto& _opt = static_cast<NLSolverOptions&>(*opt);
CLOParser cop(i, argv);
string buffer;
int nn = -1;
if (cop.getOption("--nl-cmd --nonlinear-cmd", &buffer)) {
_opt.nlSolver = buffer;
} else if (cop.getOption("--hexafloat")) {
_opt.doHexafloat = true;
} else if (cop.getOption("--nl-flags --backend-flags", &buffer)) {
auto args = FileUtils::parse_cmd_line(buffer);
for (const auto& arg : args) {
_opt.nlFlags.push_back(arg);
}
} else if (cop.getOption("--nl-flag --backend-flag", &buffer)) {
_opt.nlFlags.push_back(buffer);
} else if (cop.getOption("--keepfile")) {
_opt.doKeepfile = true;
} else if (cop.getOption("-s --solver-statistics")) {
// ignore statistics flags for now
} else if (cop.getOption("-v --verbose-solving")) {
_opt.verbose = true;
} else {
for (auto& fznf : _opt.nlSolverFlags) {
if (fznf.t == MZNFZNSolverFlag::FT_ARG && cop.getOption(fznf.n.c_str(), &buffer)) {
_opt.nlFlags.push_back(fznf.n);
_opt.nlFlags.push_back(buffer);
return true;
}
if (fznf.t == MZNFZNSolverFlag::FT_NOARG && cop.getOption(fznf.n.c_str())) {
_opt.nlFlags.push_back(fznf.n);
return true;
}
}
return false;
}
return true;
}
/** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
* **/
// Solver Instance
NLSolverInstance::NLSolverInstance(Env& env, std::ostream& log,
SolverInstanceBase::Options* options)
: SolverInstanceBase(env, log, options), _fzn(env.flat()), _ozn(env.output()) {}
NLSolverInstance::~NLSolverInstance() {}
void NLSolverInstance::processFlatZinc() {}
void NLSolverInstance::resetSolver() {}
SolverInstance::Status NLSolverInstance::solve() {
// Get the options
auto& opt = static_cast<NLSolverOptions&>(*_options);
// --- --- --- Prepare the files
string file_nl; // Output for the NL, will be the input for the solver
string file_sol; // Ouput of the solver
std::unique_ptr<FileUtils::TmpDir> tmpdir;
if (opt.doKeepfile) {
// Keep file: output next to original file
ASTString file_mzn = _env.envi().originalModel != nullptr
? _env.envi().originalModel->filepath()
: _env.envi().model->filepath();
string file_base = std::string(file_mzn.substr(0, file_mzn.findLastOf('.')));
file_nl = file_base + ".nl";
file_sol = file_base + ".sol";
} else {
// Don't keep file: create a temp directory
tmpdir = std::unique_ptr<FileUtils::TmpDir>(new FileUtils::TmpDir());
file_nl = tmpdir->name() + "/model.nl";
file_sol = tmpdir->name() + "/model.sol";
}
std::ofstream outfile(FILE_PATH(file_nl));
// Configure floating point output
if (opt.doHexafloat) {
outfile << hexfloat;
} else {
outfile.precision(numeric_limits<double>::digits10 + 2);
}
// --- --- --- Result of the try/catch block
// Use to talk back to minizinc
auto* out = getSolns2Out();
// Manage status
int exitStatus = -1;
// --- --- --- All the NL operations in one try/catch
try {
// --- --- --- Create the NL data
// Analyse the variable declarations
for (VarDeclIterator it = _fzn->vardecls().begin(); it != _fzn->vardecls().end(); ++it) {
if (!it->removed()) {
Item& item = *it;
analyse(&item);
}
}
// Analyse the contraints
for (ConstraintIterator it = _fzn->constraints().begin(); it != _fzn->constraints().end();
++it) {
if (!it->removed()) {
Item& item = *it;
analyse(&item);
}
}
// Analyse the goal
analyse(_fzn->solveItem());
// Phase 2
_nlFile.phase2();
// Print to the files
_nlFile.printToStream(outfile);
// --- --- --- Call the solver
NLSolns2Out s2o = NLSolns2Out(out, _nlFile, opt.verbose);
vector<string> cmd_line;
if (opt.nlSolver.empty()) {
outfile.close();
throw Error("No NL solver specified");
}
cmd_line.push_back(opt.nlSolver);
cmd_line.push_back(file_nl);
cmd_line.emplace_back("-AMPL");
for (const auto& arg : opt.nlFlags) {
cmd_line.push_back(arg);
}
Process<NLSolns2Out> proc(cmd_line, &s2o, 0, true);
exitStatus = proc.run();
if (exitStatus == 0) {
// Parse the result
s2o.parseSolution(file_sol);
}
} catch (const NLException& e) {
out->getLog() << e.what();
exitStatus = -2;
}
// --- --- --- Cleanup and exit
outfile.close();
return exitStatus == 0 ? out->status : Status::ERROR;
}
// Unused
Expression* NLSolverInstance::getSolutionValue(Id* id) {
assert(false);
return nullptr;
}
/** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
* **/
/** Analyse an item
* An item is a "top node" in the ast.
* In flatzinc, we can only have the cases 'variable declaration' and 'constraint'.
*/
void NLSolverInstance::analyse(const Item* i) {
// Guard
if (i == nullptr) {
return;
}
// Switch on the id of item
switch (i->iid()) {
case Item::II_INC: {
should_not_happen("include \"" << i->cast<IncludeI>()->f() << "\")");
} break;
// Case of the variable declaration.
// Because it is a variable declaration, the expression associated to the item is necessary a
// VarDecl. From the VarDecl, we can obtain the type and the RHS expression. Use this to analyse
// further.
case Item::II_VD: {
DEBUG_MSG("II_VD: Variable Declaration.");
const VarDecl* vd = i->cast<VarDeclI>()->e();
const auto* ti = Expression::cast<TypeInst>(vd->ti());
const Expression* rhs = vd->e();
_nlFile.addVarDecl(vd, ti, rhs);
} break;
case Item::II_ASN: {
should_not_happen("item II_ASN should not be present in NL's input.");
} break;
// Case of the constraint.
// Constraint are expressed through builtin calls.
// Hence, the expression associated to the item must be a E_CALL.
case Item::II_CON: {
DEBUG_MSG("II_CON: Constraint.");
Expression* e = i->cast<ConstraintI>()->e();
if (Expression::eid(e) == Expression::E_CALL) {
const auto* c = Expression::cast<Call>(e);
DEBUG_MSG(" " << c->id() << " ");
_nlFile.analyseConstraint(c);
} else {
DEBUG_MSG(" Contraint is not a builtin call.");
assert(false);
}
} break;
// Case of the 'solve' directive
case Item::II_SOL: {
const auto* si = i->cast<SolveI>();
_nlFile.addSolve(si->st(), si->e());
} break;
case Item::II_OUT: {
should_not_happen("Item II_OUT should not be present in NL's input.");
} break;
case Item::II_FUN: {
should_not_happen("Item II_FUN should not be present in NL's input.");
} break;
} // END OF SWITCH
}
} // namespace MiniZinc
|