this repo has no description
at develop 38 kB view raw
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 ! distributed with this 10 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 11 12/* A basic mzn2fzn wrapper, can be used as a plugin 13 */ 14 15#ifdef _MSC_VER 16#define _CRT_SECURE_NO_WARNINGS 17#endif 18 19#include <minizinc/flattener.hh> 20#include <minizinc/pathfileprinter.hh> 21 22#include <fstream> 23 24#ifdef HAS_GECODE 25#include <minizinc/solvers/gecode_solverinstance.hh> 26 27#include <utility> 28#endif 29 30using namespace std; 31using namespace MiniZinc; 32 33void Flattener::printVersion(ostream& os) { 34 os << "MiniZinc to FlatZinc converter, version " << MZN_VERSION_MAJOR << "." << MZN_VERSION_MINOR 35 << "." << MZN_VERSION_PATCH; 36 if (!std::string(MZN_BUILD_REF).empty()) { 37 os << ", build " << MZN_BUILD_REF; 38 } 39 os << std::endl; 40 os << "Copyright (C) 2014-" << string(__DATE__).substr(7, 4) 41 << " Monash University, NICTA, Data61" << std::endl; 42} 43 44void Flattener::printHelp(ostream& os) const { 45 os << std::endl 46 << "Flattener input options:" << std::endl 47 << " --instance-check-only\n Check the model instance (including data) for errors, but " 48 "do " 49 "not\n convert to FlatZinc." 50 << std::endl 51 << " -e, --model-check-only\n Check the model (without requiring data) for errors, but " 52 "do " 53 "not\n convert to FlatZinc." 54 << std::endl 55 << " --model-interface-only\n Only extract parameters and output variables." << std::endl 56 << " --model-types-only\n Only output variable (enum) type information." << std::endl 57 << " --no-optimize\n Do not optimize the FlatZinc" << std::endl 58 << " --no-chain-compression\n Do not simplify chains of implication constraints." 59 << std::endl 60 << " -m <file>, --model <file>\n File named <file> is the model." << std::endl 61 << " -d <file>, --data <file>\n File named <file> contains data used by the model." 62 << std::endl 63 << " -D <data>, --cmdline-data <data>\n Include the given data assignment in the model." 64 << std::endl 65 << " --stdlib-dir <dir>\n Path to MiniZinc standard library directory" << std::endl 66 << " -G <dir>, --globals-dir <dir>, --mzn-globals-dir <dir>\n Search for included " 67 "globals " 68 "in <stdlib>/<dir>." 69 << std::endl 70 << " -, --input-from-stdin\n Read problem from standard input" << std::endl 71 << " -I <dir>, --search-dir <dir>\n Additionally search for included files in <dir>." 72 << std::endl 73 << " -D \"fMIPdomains=true\"\n Switch on MIPDomain Unification" << std::endl 74 << " --MIPDMaxIntvEE <n>\n MIPD: max integer domain subinterval length to enforce " 75 "equality encoding, default " 76 << _optMIPDmaxIntvEE << std::endl 77 << " --MIPDMaxDensEE <n>\n MIPD: max domain cardinality to N subintervals ratio\n to " 78 "enforce equality encoding, default " 79 << _optMIPDmaxDensEE << ", either condition triggers" << std::endl 80 << " --only-range-domains\n When no MIPdomains: all domains contiguous, holes replaced " 81 "by " 82 "inequalities" 83 << std::endl 84 << " --allow-multiple-assignments\n Allow multiple assignments to the same variable " 85 "(e.g. " 86 "in dzn)" 87 << std::endl 88 << " --no-half-reifications\n Only use fully reified constraints, even when a half " 89 "reified constraint is defined." 90 << std::endl 91 << " --compile-solution-checker <file>.mzc.mzn\n Compile solution checker model" 92 << std::endl 93 << std::endl 94 << "Flattener two-pass options:" << std::endl 95 << " --two-pass\n Flatten twice to make better flattening decisions for the target" 96 << std::endl 97#ifdef HAS_GECODE 98 << " --use-gecode\n Perform root-node-propagation with Gecode (adds --two-pass)" 99 << std::endl 100 << " --shave\n Probe bounds of all variables at the root node (adds --use-gecode)" 101 << std::endl 102 << " --sac\n Probe values of all variables at the root node (adds --use-gecode)" 103 << std::endl 104 << " --pre-passes <n>\n Number of times to apply shave/sac pass (0 = fixed-point, 1 = " 105 "default)" 106 << std::endl 107#endif 108 << " -O<n>\n Two-pass optimisation levels:" << std::endl 109 << " -O0: Disable optimize (--no-optimize) -O1: Single pass (default)" << std::endl 110 << " -O2: Same as: --two-pass" 111#ifdef HAS_GECODE 112 << " -O3: Same as: --use-gecode" << std::endl 113 << " -O4: Same as: --shave -O5: Same as: --sac" << std::endl 114#else 115 << "\n -O3,4,5: Disabled [Requires MiniZinc with built-in Gecode support]" << std::endl 116#endif 117 << " -g\n Debug mode: Forces -O0 and records all domain changes as constraints instead " 118 "of " 119 "applying them" 120 << std::endl 121 << std::endl; 122 os << "Flattener output options:" << std::endl 123 << " --no-output-ozn, -O-\n Do not output ozn file" << std::endl 124 << " --output-base <name>\n Base name for output files" << std::endl 125 << (_fOutputByDefault 126 ? " -o <file>, --fzn <file>, --output-to-file <file>, --output-fzn-to-file <file>\n" 127 : " --fzn <file>, --output-fzn-to-file <file>\n") 128 << " Filename for generated FlatZinc output" << std::endl 129 << " --ozn, --output-ozn-to-file <file>\n Filename for model output specification " 130 "(--ozn- " 131 "for none)" 132 << std::endl 133 << " --keep-paths\n Don't remove path annotations from FlatZinc" << std::endl 134 << " --output-paths\n Output a symbol table (.paths file)" << std::endl 135 << " --output-paths-to-file <file>\n Output a symbol table (.paths file) to <file>" 136 << std::endl 137 << " --output-detailed-timing\n Output detailed profiling information of compilation time" 138 << std::endl 139 << " --output-to-stdout, --output-fzn-to-stdout\n Print generated FlatZinc to standard " 140 "output" 141 << std::endl 142 << " --output-ozn-to-stdout\n Print model output specification to standard output" 143 << std::endl 144 << " --output-paths-to-stdout\n Output symbol table to standard output" << std::endl 145 << " --output-mode <item|dzn|json|checker>\n Create output according to output item " 146 "(default), or output compatible\n with dzn or json format, or for solution checking" 147 << std::endl 148 << " --output-objective\n Print value of objective function in dzn or json output" 149 << std::endl 150 << " --output-output-item\n Print the output item as a string in the dzn or json output" 151 << std::endl 152 << " -Werror\n Turn warnings into errors" << std::endl; 153} 154 155bool Flattener::processOption(int& i, std::vector<std::string>& argv, 156 const std::string& workingDir) { 157 CLOParser cop(i, argv); 158 string buffer; 159 int intBuffer; 160 161 if (cop.getOption("-I --search-dir", &buffer)) { 162 _includePaths.push_back(FileUtils::file_path(buffer + "/", workingDir)); 163 } else if (cop.getOption("--no-typecheck")) { 164 _flags.typecheck = false; 165 } else if (cop.getOption("--instance-check-only")) { 166 _flags.instanceCheckOnly = true; 167 } else if (cop.getOption("-e --model-check-only")) { 168 _flags.modelCheckOnly = true; 169 } else if (cop.getOption("--model-interface-only")) { 170 _flags.modelInterfaceOnly = true; 171 } else if (cop.getOption("--model-types-only")) { 172 _flags.modelTypesOnly = true; 173 } else if (cop.getOption("-v --verbose")) { 174 _flags.verbose = true; 175 } else if (cop.getOption("--newfzn")) { 176 _flags.newfzn = true; 177 } else if (cop.getOption("--no-optimize --no-optimise")) { 178 _flags.optimize = false; 179 } else if (cop.getOption("--no-chain-compression")) { 180 _flags.chainCompression = false; 181 } else if (cop.getOption("--no-output-ozn -O-")) { 182 _flags.noOutputOzn = true; 183 } else if (cop.getOption("--output-base", &_flagOutputBase)) { // NOLINT: Allow repeated empty if 184 // Parsed by reference 185 } else if (cop.getOption(_fOutputByDefault ? "-o --fzn --output-to-file --output-fzn-to-file" 186 : "--fzn --output-fzn-to-file", 187 &buffer)) { 188 _flagOutputFzn = FileUtils::file_path(buffer, workingDir); 189 } else if (cop.getOption("--output-paths")) { 190 _fopts.collectMznPaths = true; 191 } else if (cop.getOption("--output-paths-to-file", &buffer)) { 192 _flagOutputPaths = FileUtils::file_path(buffer, workingDir); 193 _fopts.collectMznPaths = true; 194 } else if (cop.getOption("--output-to-stdout --output-fzn-to-stdout")) { 195 _flags.outputFznStdout = true; 196 } else if (cop.getOption("--output-ozn-to-stdout")) { 197 _flags.outputOznStdout = true; 198 } else if (cop.getOption("--output-paths-to-stdout")) { 199 _fopts.collectMznPaths = true; 200 _flags.outputPathsStdout = true; 201 } else if (cop.getOption("--output-detailed-timing")) { 202 _fopts.detailedTiming = true; 203 } else if (cop.getOption("--output-mode", &buffer)) { 204 if (buffer == "dzn") { 205 _flagOutputMode = FlatteningOptions::OUTPUT_DZN; 206 } else if (buffer == "json") { 207 _flagOutputMode = FlatteningOptions::OUTPUT_JSON; 208 } else if (buffer == "item") { 209 _flagOutputMode = FlatteningOptions::OUTPUT_ITEM; 210 } else if (buffer == "checker") { 211 _flagOutputMode = FlatteningOptions::OUTPUT_CHECKER; 212 } else { 213 return false; 214 } 215 } else if (cop.getOption("--output-objective")) { 216 _flags.outputObjective = true; 217 } else if (cop.getOption("--output-output-item")) { 218 _flags.outputOutputItem = true; 219 } else if (cop.getOption("- --input-from-stdin")) { 220 _flags.stdinInput = true; 221 } else if (cop.getOption("-d --data", &buffer)) { 222 auto last_dot = buffer.find_last_of('.'); 223 if (last_dot == string::npos) { 224 return false; 225 } 226 auto extension = buffer.substr(last_dot, string::npos); 227 if (extension != ".dzn" && extension != ".json") { 228 return false; 229 } 230 _datafiles.push_back(FileUtils::file_path(buffer, workingDir)); 231 } else if (cop.getOption("--stdlib-dir", &buffer)) { 232 _stdLibDir = FileUtils::file_path(buffer, workingDir); 233 } else if (cop.getOption("-G --globals-dir --mzn-globals-dir", 234 &_globalsDir)) { // NOLINT: Allow repeated empty if 235 // Parsed by reference 236 } else if (cop.getOption("-D --cmdline-data", &buffer)) { 237 _datafiles.push_back("cmd:/" + buffer); 238 } else if (cop.getOption("--allow-unbounded-vars")) { 239 _flags.allowUnboundedVars = true; 240 } else if (cop.getOption("--only-range-domains")) { 241 _flags.onlyRangeDomains = true; 242 } else if (cop.getOption("--no-MIPdomains")) { // internal 243 _flags.noMIPdomains = true; 244 } else if (cop.getOption("--MIPDMaxIntvEE", 245 &_optMIPDmaxIntvEE)) { // NOLINT: Allow repeated empty if 246 // Parsed by reference 247 } else if (cop.getOption("--MIPDMaxDensEE", 248 &_optMIPDmaxDensEE)) { // NOLINT: Allow repeated empty if 249 // Parsed by reference 250 } else if (cop.getOption("-Werror")) { 251 _flags.werror = true; 252 } else if (cop.getOption("--use-gecode")) { 253#ifdef HAS_GECODE 254 _flags.twoPass = true; 255 _flags.gecode = true; 256#else 257 _log << "warning: Gecode not available. Ignoring '--use-gecode'\n"; 258#endif 259 } else if (cop.getOption("--sac")) { 260#ifdef HAS_GECODE 261 _flags.twoPass = true; 262 _flags.gecode = true; 263 _flags.sac = true; 264#else 265 _log << "warning: Gecode not available. Ignoring '--sac'\n"; 266#endif 267 268 } else if (cop.getOption("--shave")) { 269#ifdef HAS_GECODE 270 _flags.twoPass = true; 271 _flags.gecode = true; 272 _flags.shave = true; 273#else 274 _log << "warning: Gecode not available. Ignoring '--shave'\n"; 275#endif 276 } else if (cop.getOption("--two-pass")) { 277 _flags.twoPass = true; 278 } else if (cop.getOption("--pre-passes", &intBuffer)) { 279 if (intBuffer >= 0) { 280 _flagPrePasses = static_cast<unsigned int>(intBuffer); 281 } 282 } else if (cop.getOption("-O", &intBuffer)) { 283 switch (intBuffer) { 284 case 0: { 285 _flags.optimize = false; 286 break; 287 } 288 case 1: { 289 // Default settings 290 break; 291 } 292 case 2: { 293 _flags.twoPass = true; 294 break; 295 } 296 case 3: { 297 _flags.twoPass = true; 298 _flags.gecode = true; 299 break; 300 } 301 case 4: { 302 _flags.twoPass = true; 303 _flags.gecode = true; 304 _flags.shave = true; 305 break; 306 } 307 case 5: { 308 _flags.twoPass = true; 309 _flags.gecode = true; 310 _flags.sac = true; 311 break; 312 } 313 default: { 314 _log << "% Error: Unsupported optimisation level, cannot process -O" << intBuffer << "." 315 << std::endl; 316 return false; 317 } 318 } 319 // ozn options must be after the -O<n> optimisation options 320 } else if (cop.getOption("--ozn --output-ozn-to-file", &buffer)) { 321 _flagOutputOzn = FileUtils::file_path(buffer, workingDir); 322 } else if (cop.getOption("-g")) { 323 _flags.optimize = false; 324 _flags.twoPass = false; 325 _flags.gecode = false; 326 _flags.shave = false; 327 _flags.sac = false; 328 _fopts.recordDomainChanges = true; 329 } else if (string(argv[i]) == "--keep-paths") { 330 _flags.keepMznPaths = true; 331 _fopts.collectMznPaths = true; 332 } else if (string(argv[i]) == "--only-toplevel-presolve") { 333 _fopts.onlyToplevelPaths = true; 334 } else if (cop.getOption("--allow-multiple-assignments")) { 335 _flags.allowMultiAssign = true; 336 } else if (cop.getOption("--no-half-reifications")) { 337 _fopts.enableHalfReification = false; 338 } else if (string(argv[i]) == "--input-is-flatzinc") { 339 _isFlatzinc = true; 340 } else if (cop.getOption("--compile-solution-checker", &buffer)) { 341 if (buffer.length() >= 8 && buffer.substr(buffer.length() - 8, string::npos) == ".mzc.mzn") { 342 _flags.compileSolutionCheckModel = true; 343 _flags.modelCheckOnly = true; 344 _filenames.push_back(FileUtils::file_path(buffer, workingDir)); 345 } else { 346 _log << "Error: solution checker model must have extension .mzc.mzn" << std::endl; 347 return false; 348 } 349 } else if (cop.getOption("-m --model", &buffer)) { 350 if (buffer.length() <= 4) { 351 return false; 352 } 353 auto extension = buffer.substr(buffer.length() - 4, string::npos); 354 auto isChecker = 355 buffer.length() > 8 && buffer.substr(buffer.length() - 8, string::npos) == ".mzc.mzn"; 356 if ((extension == ".mzn" && !isChecker) || extension == ".fzn") { 357 if (extension == ".fzn") { 358 _isFlatzinc = true; 359 if (_fOutputByDefault) { // mzn2fzn mode 360 return false; 361 } 362 } 363 _filenames.push_back(FileUtils::file_path(buffer, workingDir)); 364 return true; 365 } 366 _log << "Error: model must have extension .mzn (or .fzn)" << std::endl; 367 return false; 368 } else { 369 std::string input_file(argv[i]); 370 if (input_file.length() <= 4) { 371 return false; 372 } 373 size_t last_dot = input_file.find_last_of('.'); 374 if (last_dot == string::npos) { 375 return false; 376 } 377 std::string extension = input_file.substr(last_dot, string::npos); 378 if (extension == ".mzc" || 379 (input_file.length() >= 8 && 380 input_file.substr(input_file.length() - 8, string::npos) == ".mzc.mzn")) { 381 _flagSolutionCheckModel = input_file; 382 } else if (extension == ".mzn" || extension == ".fzn") { 383 if (extension == ".fzn") { 384 _isFlatzinc = true; 385 if (_fOutputByDefault) { // mzn2fzn mode 386 return false; 387 } 388 } 389 _filenames.push_back(input_file); 390 } else if (extension == ".dzn" || extension == ".json") { 391 _datafiles.push_back(input_file); 392 } else { 393 if (_fOutputByDefault) { 394 _log << "Error: cannot handle file extension " << extension << "." << std::endl; 395 } 396 return false; 397 } 398 } 399 return true; 400} 401 402Flattener::Flattener(std::ostream& os, std::ostream& log, std::string stdlibDir) 403 : _os(os), _log(log), _stdLibDir(std::move(stdlibDir)) {} 404 405Flattener::~Flattener() { 406 if (_pEnv != nullptr) { // ??? TODO 407 if (_isFlatzinc) { 408 _pEnv->swap(); 409 } 410 } 411} 412 413Env* Flattener::multiPassFlatten(const vector<unique_ptr<Pass> >& passes) { 414 Env& e = *getEnv(); 415 416 Env* pre_env = &e; 417 size_t npasses = passes.size(); 418 pre_env->envi().finalPassNumber = static_cast<unsigned int>(npasses); 419 Timer starttime; 420 bool verbose = false; 421 for (unsigned int i = 0; i < passes.size(); i++) { 422 pre_env->envi().currentPassNumber = i; 423 if (verbose) { 424 _log << "Start pass " << i << ":\n"; 425 } 426 427 Env* out_env = passes[i]->run(pre_env, _log); 428 if (out_env == nullptr) { 429 return nullptr; 430 } 431 if (pre_env != &e && pre_env != out_env) { 432 delete pre_env; 433 } 434 pre_env = out_env; 435 436 if (verbose) { 437 _log << "Finish pass " << i << ": " << starttime.stoptime() << "\n"; 438 } 439 } 440 441 return pre_env; 442} 443 444class FlattenTimeout { 445public: 446 FlattenTimeout(unsigned long long int t) { GC::setTimeout(t); } 447 ~FlattenTimeout() { GC::setTimeout(0); } 448}; 449 450void Flattener::flatten(const std::string& modelString, const std::string& modelName) { 451 FlattenTimeout flatten_timeout(_fopts.timeout); 452 Timer flatten_time; 453 _starttime.reset(); 454 455 if (_flags.verbose) { 456 printVersion(_log); 457 } 458 459 if (_filenames.empty() && !_flagSolutionCheckModel.empty()) { 460 // Compile solution check model as if it were a normal model 461 _filenames.push_back(_flagSolutionCheckModel); 462 _flagSolutionCheckModel = ""; 463 } 464 465 if (_filenames.empty() && !_flags.stdinInput && modelString.empty()) { 466 throw Error("Error: no model file given."); 467 } 468 469 if (_stdLibDir.empty()) { 470 throw Error( 471 "Error: unknown minizinc standard library directory.\n" 472 "Specify --stdlib-dir on the command line or set the\n" 473 "MZN_STDLIB_DIR environment variable."); 474 } 475 476 if (!_globalsDir.empty()) { 477 _includePaths.insert(_includePaths.begin(), _stdLibDir + "/" + _globalsDir + "/"); 478 } 479 _includePaths.push_back(_stdLibDir + "/std/"); 480 481 for (auto& includePath : _includePaths) { 482 if (!FileUtils::directory_exists(includePath)) { 483 throw Error("Cannot access include directory " + includePath); 484 } 485 } 486 487 if (_flagOutputBase.empty()) { 488 if (_filenames.empty()) { 489 _flagOutputBase = "mznout"; 490 } else { 491 _flagOutputBase = _filenames[0].substr(0, _filenames[0].length() - 4); 492 } 493 } 494 495 if (_filenames.end() != find(_filenames.begin(), _filenames.end(), _flagOutputFzn) || 496 _datafiles.end() != find(_datafiles.begin(), _datafiles.end(), _flagOutputFzn)) { 497 _log << " WARNING: fzn filename '" << _flagOutputFzn << "' matches an input file, ignoring." 498 << endl; 499 _flagOutputFzn = ""; 500 } 501 if (_filenames.end() != find(_filenames.begin(), _filenames.end(), _flagOutputOzn) || 502 _datafiles.end() != find(_datafiles.begin(), _datafiles.end(), _flagOutputOzn)) { 503 _log << " WARNING: ozn filename '" << _flagOutputOzn << "' matches an input file, ignoring." 504 << endl; 505 _flagOutputOzn = ""; 506 } 507 508 if (_fOutputByDefault) { 509 if (_flagOutputFzn.empty()) { 510 _flagOutputFzn = _flagOutputBase + ".fzn"; 511 } 512 if (_flagOutputPaths.empty() && _fopts.collectMznPaths) { 513 _flagOutputPaths = _flagOutputBase + ".paths"; 514 } 515 if (_flagOutputOzn.empty() && !_flags.noOutputOzn) { 516 _flagOutputOzn = _flagOutputBase + ".ozn"; 517 } 518 } 519 520 { 521 std::stringstream errstream; 522 523 Model* m; 524 _pEnv.reset(new Env(nullptr, _os, _log)); 525 Env* env = getEnv(); 526 527 if (!_flags.compileSolutionCheckModel && !_flagSolutionCheckModel.empty()) { 528 // Extract variables to check from solution check model 529 if (_flags.verbose) { 530 _log << "Parsing solution checker model " << _flagSolutionCheckModel << " ..." << endl; 531 } 532 bool isCompressedChecker = 533 _flagSolutionCheckModel.size() >= 4 && 534 _flagSolutionCheckModel.substr(_flagSolutionCheckModel.size() - 4) == ".mzc"; 535 std::vector<std::string> smm_model({_flagSolutionCheckModel}); 536 Model* smm = parse(*env, smm_model, _datafiles, "", "", _includePaths, _isFlatzinc, false, 537 false, _flags.verbose, errstream); 538 if (_flags.verbose) { 539 _log << " done parsing (" << _starttime.stoptime() << ")" << std::endl; 540 } 541 if (smm != nullptr) { 542 _log << errstream.str(); 543 errstream.str(""); 544 std::ostringstream smm_oss; 545 std::ostringstream smm_stats_oss; 546 Printer p(smm_oss, 0, false); 547 p.print(smm); 548 Env smm_env(smm); 549 GCLock lock; 550 vector<TypeError> typeErrors; 551 try { 552 MiniZinc::typecheck(smm_env, smm, typeErrors, true, false, true); 553 if (!typeErrors.empty()) { 554 if (!isCompressedChecker) { 555 for (auto& typeError : typeErrors) { 556 if (_flags.verbose) { 557 _log << std::endl; 558 } 559 _log << typeError.loc() << ":" << std::endl; 560 _log << typeError.what() << ": " << typeError.msg() << std::endl; 561 } 562 } 563 throw Error("multiple type errors"); 564 } 565 for (auto& i : *smm) { 566 if (auto* vdi = i->dynamicCast<VarDeclI>()) { 567 if (vdi->e()->e() == nullptr) { 568 env->envi().checkVars.emplace_back(vdi->e()); 569 } else if (vdi->e()->ann().contains(constants().ann.rhs_from_assignment)) { 570 smm_stats_oss << *vdi; 571 } 572 } 573 } 574 smm->compact(); 575 std::string smm_compressed = 576 FileUtils::encode_base64(FileUtils::deflate_string(smm_oss.str())); 577 auto* ti = new TypeInst(Location().introduce(), Type::parstring(), nullptr); 578 auto* checkString = 579 new VarDecl(Location().introduce(), ti, ASTString("_mzn_solution_checker"), 580 new StringLit(Location().introduce(), smm_compressed)); 581 auto* checkStringI = new VarDeclI(Location().introduce(), checkString); 582 env->output()->addItem(checkStringI); 583 584 for (FunctionIterator it = smm->functions().begin(); it != smm->functions().end(); ++it) { 585 if (it->id() == "checkStatistics") { 586 smm_stats_oss << *it; 587 smm_stats_oss << "int: mzn_stats_failures;\n"; 588 smm_stats_oss << "int: mzn_stats_solutions;\n"; 589 smm_stats_oss << "int: mzn_stats_nodes;\n"; 590 smm_stats_oss << "int: mzn_stats_time;\n"; 591 smm_stats_oss << "output " 592 "[checkStatistics(mzn_stats_failures,mzn_stats_solutions,mzn_stats_" 593 "nodes,mzn_stats_time)];\n"; 594 std::string smm_stats_compressed = 595 FileUtils::encode_base64(FileUtils::deflate_string(smm_stats_oss.str())); 596 auto* ti = new TypeInst(Location().introduce(), Type::parstring(), nullptr); 597 auto* checkStatsString = 598 new VarDecl(Location().introduce(), ti, ASTString("_mzn_stats_checker"), 599 new StringLit(Location().introduce(), smm_stats_compressed)); 600 auto* checkStatsStringI = new VarDeclI(Location().introduce(), checkStatsString); 601 env->output()->addItem(checkStatsStringI); 602 } 603 } 604 } catch (TypeError& e) { 605 if (isCompressedChecker) { 606 _log << "Warning: type error in solution checker model\n"; 607 } else { 608 throw; 609 } 610 } 611 } else { 612 if (isCompressedChecker) { 613 _log << "Warning: syntax error in solution checker model\n"; 614 } else { 615 _log << errstream.str(); 616 throw Error("parse error"); 617 } 618 } 619 } 620 621 if (_flags.compileSolutionCheckModel) { 622 if (!modelString.empty()) { 623 throw Error("Cannot compile solution checker model with additional model inputs."); 624 } 625 if (_flags.stdinInput) { 626 throw Error( 627 "Cannot compile solution checker model with additional model from standard input."); 628 } 629 if (_filenames.size() != 1) { 630 throw Error("Cannot compile solution checker model with more than one model given."); 631 } 632 } 633 634 if (!_flagSolutionCheckModel.empty() && _filenames.empty()) { 635 throw Error("Cannot run solution checker without model."); 636 } 637 638 std::string modelText = modelString; 639 if (_flags.stdinInput) { 640 std::string input = 641 std::string(istreambuf_iterator<char>(std::cin), istreambuf_iterator<char>()); 642 modelText += input; 643 } 644 645 if (_flags.verbose) { 646 _log << "Parsing file(s) "; 647 for (int i = 0; i < _filenames.size(); ++i) { 648 _log << (i == 0 ? "" : ", '") << _filenames[i] << '\''; 649 } 650 for (const auto& sFln : _datafiles) { 651 _log << ", '" << sFln << '\''; 652 } 653 _log << " ..." << std::endl; 654 } 655 errstream.str(""); 656 m = parse(*env, _filenames, _datafiles, modelText, modelName.empty() ? "stdin" : modelName, 657 _includePaths, _isFlatzinc, false, false, _flags.verbose, errstream); 658 if (!_globalsDir.empty()) { 659 _includePaths.erase(_includePaths.begin()); 660 } 661 if (m == nullptr) { 662 throw Error(errstream.str()); 663 } 664 _log << errstream.str(); 665 env->model(m); 666 if (_flags.typecheck) { 667 if (_flags.verbose) { 668 _log << " done parsing (" << _starttime.stoptime() << ")" << std::endl; 669 } 670 671 if (_flags.instanceCheckOnly || _flags.modelCheckOnly || _flags.modelInterfaceOnly || 672 _flags.modelTypesOnly) { 673 std::ostringstream compiledSolutionCheckModel; 674 if (_flags.compileSolutionCheckModel) { 675 Printer p(compiledSolutionCheckModel, 0); 676 p.print(m); 677 } 678 GCLock lock; 679 vector<TypeError> typeErrors; 680 MiniZinc::typecheck( 681 *env, m, typeErrors, 682 _flags.modelTypesOnly || _flags.modelInterfaceOnly || _flags.modelCheckOnly, 683 _flags.allowMultiAssign); 684 if (!typeErrors.empty()) { 685 for (auto& typeError : typeErrors) { 686 if (_flags.verbose) { 687 _log << std::endl; 688 } 689 _log << typeError.loc() << ":" << std::endl; 690 _log << typeError.what() << ": " << typeError.msg() << std::endl; 691 } 692 throw Error("multiple type errors"); 693 } 694 if (_flags.modelInterfaceOnly) { 695 MiniZinc::output_model_interface(*env, m, _os, _includePaths); 696 } 697 if (_flags.modelTypesOnly) { 698 MiniZinc::output_model_variable_types(*env, m, _os, _includePaths); 699 } 700 if (_flags.compileSolutionCheckModel) { 701 std::string mzc(FileUtils::deflate_string(compiledSolutionCheckModel.str())); 702 mzc = FileUtils::encode_base64(mzc); 703 std::string mzc_filename = _filenames[0].substr(0, _filenames[0].size() - 4); 704 if (_flags.verbose) { 705 _log << "Write solution checker to " << mzc_filename << "\n"; 706 } 707 std::ofstream mzc_f(FILE_PATH(mzc_filename)); 708 mzc_f << mzc; 709 mzc_f.close(); 710 } 711 status = SolverInstance::NONE; 712 } else { 713 if (_isFlatzinc) { 714 GCLock lock; 715 vector<TypeError> typeErrors; 716 MiniZinc::typecheck(*env, m, typeErrors, 717 _flags.modelCheckOnly || _flags.modelInterfaceOnly, 718 _flags.allowMultiAssign, true); 719 if (!typeErrors.empty()) { 720 for (auto& typeError : typeErrors) { 721 if (_flags.verbose) { 722 _log << std::endl; 723 } 724 _log << typeError.loc() << ":" << std::endl; 725 _log << typeError.what() << ": " << typeError.msg() << std::endl; 726 } 727 throw Error("multiple type errors"); 728 } 729 MiniZinc::register_builtins(*env); 730 env->swap(); 731 populate_output(*env); 732 } else { 733 if (_flags.verbose) { 734 _log << "Flattening ..."; 735 } 736 737 _fopts.onlyRangeDomains = _flags.onlyRangeDomains; 738 _fopts.verbose = _flags.verbose; 739 _fopts.outputMode = _flagOutputMode; 740 _fopts.outputObjective = _flags.outputObjective; 741 _fopts.outputOutputItem = _flags.outputOutputItem; 742 _fopts.hasChecker = !_flagSolutionCheckModel.empty(); 743#ifdef HAS_GECODE 744 GecodeOptions gopts; 745 gopts.onlyRangeDomains = _flags.onlyRangeDomains; 746 gopts.sac = _flags.sac; 747 gopts.allowUnboundedVars = _flags.allowUnboundedVars; 748 gopts.shave = _flags.shave; 749 gopts.printStatistics = _flags.statistics; 750 gopts.prePasses = _flagPrePasses; 751#endif 752 FlatteningOptions pass_opts = _fopts; 753 CompilePassFlags cfs; 754 cfs.noMIPdomains = _flags.noMIPdomains; 755 cfs.verbose = _flags.verbose; 756 cfs.statistics = _flags.statistics; 757 cfs.optimize = _flags.optimize; 758 cfs.chainCompression = _flags.chainCompression; 759 cfs.newfzn = _flags.newfzn; 760 cfs.werror = _flags.werror; 761 cfs.modelCheckOnly = _flags.modelCheckOnly; 762 cfs.modelInterfaceOnly = _flags.modelInterfaceOnly; 763 cfs.allowMultiAssign = _flags.allowMultiAssign; 764 765 std::vector<unique_ptr<Pass> > managed_passes; 766 767 if (_flags.twoPass) { 768 std::string library = _stdLibDir + (_flags.gecode ? "/gecode_presolver/" : "/std/"); 769 bool differentLibrary = (library != _stdLibDir + "/" + _globalsDir + "/"); 770 managed_passes.emplace_back(new CompilePass(env, pass_opts, cfs, library, _includePaths, 771 true, differentLibrary)); 772#ifdef HAS_GECODE 773 if (_flags.gecode) { 774 managed_passes.emplace_back(new GecodePass(&gopts)); 775 } 776#endif 777 } 778 managed_passes.emplace_back(new CompilePass(env, _fopts, cfs, 779 _stdLibDir + "/" + _globalsDir + "/", 780 _includePaths, _flags.twoPass, false)); 781 782 Env* out_env = multiPassFlatten(managed_passes); 783 if (out_env == nullptr) { 784 exit(EXIT_FAILURE); 785 } 786 787 if (out_env != env) { 788 _pEnv.reset(out_env); 789 } 790 env = out_env; 791 if (_flags.verbose) { 792 _log << " done (" << _starttime.stoptime() << ")," 793 << " max stack depth " << env->maxCallStack() << std::endl; 794 } 795 } 796 797 if (_flags.statistics) { 798 FlatModelStatistics stats = statistics(*env); 799 _os << "% Generated FlatZinc statistics:\n"; 800 801 _os << "%%%mzn-stat: paths=" << env->envi().getPathMap().size() << endl; 802 803 if (stats.n_bool_vars != 0) { 804 _os << "%%%mzn-stat: flatBoolVars=" << stats.n_bool_vars << endl; 805 } 806 if (stats.n_int_vars != 0) { 807 _os << "%%%mzn-stat: flatIntVars=" << stats.n_int_vars << endl; 808 } 809 if (stats.n_float_vars != 0) { 810 _os << "%%%mzn-stat: flatFloatVars=" << stats.n_float_vars << endl; 811 } 812 if (stats.n_set_vars != 0) { 813 _os << "%%%mzn-stat: flatSetVars=" << stats.n_set_vars << endl; 814 } 815 816 if (stats.n_bool_ct != 0) { 817 _os << "%%%mzn-stat: flatBoolConstraints=" << stats.n_bool_ct << endl; 818 } 819 if (stats.n_int_ct != 0) { 820 _os << "%%%mzn-stat: flatIntConstraints=" << stats.n_int_ct << endl; 821 } 822 if (stats.n_float_ct != 0) { 823 _os << "%%%mzn-stat: flatFloatConstraints=" << stats.n_float_ct << endl; 824 } 825 if (stats.n_set_ct != 0) { 826 _os << "%%%mzn-stat: flatSetConstraints=" << stats.n_set_ct << endl; 827 } 828 829 if (stats.n_reif_ct != 0) { 830 _os << "%%%mzn-stat: evaluatedReifiedConstraints=" << stats.n_reif_ct << endl; 831 } 832 if (stats.n_imp_ct != 0) { 833 _os << "%%%mzn-stat: evaluatedHalfReifiedConstraints=" << stats.n_imp_ct << endl; 834 } 835 836 if (stats.n_imp_del != 0) { 837 _os << "%%%mzn-stat: eliminatedImplications=" << stats.n_imp_del << endl; 838 } 839 if (stats.n_lin_del != 0) { 840 _os << "%%%mzn-stat: eliminatedLinearConstraints=" << stats.n_lin_del << endl; 841 } 842 843 /// Objective / SAT. These messages are used by mzn-test.py. 844 SolveI* solveItem = env->flat()->solveItem(); 845 if (solveItem->st() != SolveI::SolveType::ST_SAT) { 846 if (solveItem->st() == SolveI::SolveType::ST_MAX) { 847 _os << "%%%mzn-stat: method=\"maximize\"" << endl; 848 } else { 849 _os << "%%%mzn-stat: method=\"minimize\"" << endl; 850 } 851 } else { 852 _os << "%%%mzn-stat: method=\"satisfy\"" << endl; 853 } 854 855 _os << "%%%mzn-stat: flatTime=" << flatten_time.s() << endl; 856 _os << "%%%mzn-stat-end" << endl << endl; 857 } 858 859 if (_flags.outputPathsStdout) { 860 if (_flags.verbose) { 861 _log << "Printing Paths to stdout ..." << std::endl; 862 } 863 PathFilePrinter pfp(_os, env->envi()); 864 pfp.print(env->flat()); 865 if (_flags.verbose) { 866 _log << " done (" << _starttime.stoptime() << ")" << std::endl; 867 } 868 } else if (!_flagOutputPaths.empty()) { 869 if (_flags.verbose) { 870 _log << "Printing Paths to '" << _flagOutputPaths << "' ..." << std::flush; 871 } 872 std::ofstream ofs(FILE_PATH(_flagOutputPaths), ios::out); 873 check_io_status(ofs.good(), " I/O error: cannot open fzn output file. "); 874 PathFilePrinter pfp(ofs, env->envi()); 875 pfp.print(env->flat()); 876 check_io_status(ofs.good(), " I/O error: cannot write fzn output file. "); 877 ofs.close(); 878 if (_flags.verbose) { 879 _log << " done (" << _starttime.stoptime() << ")" << std::endl; 880 } 881 } 882 883 if ((_fopts.collectMznPaths || _flags.twoPass) && !_flags.keepMznPaths) { 884 class RemovePathAnnotations : public ItemVisitor { 885 public: 886 static void removePath(Annotation& a) { a.removeCall(constants().ann.mzn_path); } 887 static void vVarDeclI(VarDeclI* vdi) { removePath(vdi->e()->ann()); } 888 static void vConstraintI(ConstraintI* ci) { removePath(ci->e()->ann()); } 889 static void vSolveI(SolveI* si) { 890 removePath(si->ann()); 891 if (Expression* e = si->e()) { 892 removePath(e->ann()); 893 } 894 } 895 } removePaths; 896 iter_items<RemovePathAnnotations>(removePaths, env->flat()); 897 } 898 899 if (_flags.outputFznStdout) { 900 if (_flags.verbose) { 901 _log << "Printing FlatZinc to stdout ..." << std::endl; 902 } 903 Printer p(_os, 0); 904 p.print(env->flat()); 905 if (_flags.verbose) { 906 _log << " done (" << _starttime.stoptime() << ")" << std::endl; 907 } 908 } else if (!_flagOutputFzn.empty()) { 909 if (_flags.verbose) { 910 _log << "Printing FlatZinc to '" << _flagOutputFzn << "' ..." << std::flush; 911 } 912 std::ofstream ofs(FILE_PATH(_flagOutputFzn), ios::out); 913 check_io_status(ofs.good(), " I/O error: cannot open fzn output file. "); 914 Printer p(ofs, 0); 915 p.print(env->flat()); 916 check_io_status(ofs.good(), " I/O error: cannot write fzn output file. "); 917 ofs.close(); 918 if (_flags.verbose) { 919 _log << " done (" << _starttime.stoptime() << ")" << std::endl; 920 } 921 } 922 if (!_flags.noOutputOzn) { 923 if (_flags.outputOznStdout) { 924 if (_flags.verbose) { 925 _log << "Printing .ozn to stdout ..." << std::endl; 926 } 927 Printer p(_os, 0); 928 p.print(env->output()); 929 if (_flags.verbose) { 930 _log << " done (" << _starttime.stoptime() << ")" << std::endl; 931 } 932 } else if (!_flagOutputOzn.empty()) { 933 if (_flags.verbose) { 934 _log << "Printing .ozn to '" << _flagOutputOzn << "' ..." << std::flush; 935 } 936 std::ofstream ofs(FILE_PATH(_flagOutputOzn), std::ios::out); 937 check_io_status(ofs.good(), " I/O error: cannot open ozn output file. "); 938 Printer p(ofs, 0); 939 p.print(env->output()); 940 check_io_status(ofs.good(), " I/O error: cannot write ozn output file. "); 941 ofs.close(); 942 if (_flags.verbose) { 943 _log << " done (" << _starttime.stoptime() << ")" << std::endl; 944 } 945 } 946 } 947 } 948 } else { // !flag_typecheck 949 Printer p(_os); 950 p.print(m); 951 } 952 } 953 954 if (getEnv()->envi().failed()) { 955 status = SolverInstance::UNSAT; 956 } 957 958 if (_flags.verbose) { 959 size_t mem = GC::maxMem(); 960 if (mem < 1024) { 961 _log << "Maximum memory " << mem << " bytes"; 962 } else if (mem < 1024 * 1024) { 963 _log << "Maximum memory " << mem / 1024 << " Kbytes"; 964 } else { 965 _log << "Maximum memory " << mem / (1024 * 1024) << " Mbytes"; 966 } 967 _log << "." << std::endl; 968 } 969} 970 971void Flattener::printStatistics(ostream& /*os*/) {}