/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* * Main authors: * Guido Tack * Gleb Belov */ /* 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/. */ /* This (main) file coordinates flattening and solving. * The corresponding modules are flexibly plugged in * as derived classes, prospectively from DLLs. * A flattening module should provide MinZinc::GetFlattener() * A solving module should provide an object of a class derived from SolverFactory. * Need to get more flexible for multi-pass & multi-solving stuff TODO */ #ifdef _MSC_VER #define _CRT_SECURE_NO_WARNINGS #endif #include #include #include #include #include #include #include #include #include #ifdef HAS_GUROBI #include #endif #ifdef HAS_CPLEX #include #endif #ifdef HAS_OSICBC #include #endif #ifdef HAS_XPRESS #include #endif #ifdef HAS_GECODE #include #endif #ifdef HAS_GEAS #include #endif #ifdef HAS_SCIP #include #endif #include #include #include #include #include #include using namespace std; using namespace MiniZinc; SolverInitialiser::SolverInitialiser() { #ifdef HAS_GUROBI GurobiSolverFactoryInitialiser _gurobi_init; #endif #ifdef HAS_CPLEX static CplexSolverFactoryInitialiser _cplex_init; #endif #ifdef HAS_OSICBC static OSICBCSolverFactoryInitialiser _osicbc_init; #endif #ifdef HAS_XPRESS static XpressSolverFactoryInitialiser _xpress_init; #endif #ifdef HAS_GECODE static GecodeSolverFactoryInitialiser _gecode_init; #endif #ifdef HAS_GEAS static GeasSolverFactoryInitialiser _geas_init; #endif #ifdef HAS_SCIP static SCIPSolverFactoryInitialiser _scip_init; #endif static FZNSolverFactoryInitialiser _fzn_init; static MZNSolverFactoryInitialiser _mzn_init; static NLSolverFactoryInitialiser _nl_init; } MZNFZNSolverFlag MZNFZNSolverFlag::std(const std::string& n0) { const std::string argFlags("-I -n -p -r -n-o"); if (argFlags.find(n0) != std::string::npos) { return MZNFZNSolverFlag(FT_ARG, n0); } return MZNFZNSolverFlag(FT_NOARG, n0); } MZNFZNSolverFlag MZNFZNSolverFlag::extra(const SolverConfig::ExtraFlag& ef) { return MZNFZNSolverFlag( ef.flagType == SolverConfig::ExtraFlag::FlagType::T_BOOL && ef.range.empty() ? FT_NOARG : FT_ARG, ef.flag); } // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SolverRegistry* MiniZinc::get_global_solver_registry() { static SolverRegistry sr; return &sr; } void SolverRegistry::addSolverFactory(SolverFactory* pSF) { assert(pSF); _sfstorage.push_back(pSF); } void SolverRegistry::removeSolverFactory(SolverFactory* pSF) { auto it = find(_sfstorage.begin(), _sfstorage.end(), pSF); assert(pSF); _sfstorage.erase(it); } void SolverRegistry::addFactoryFlag(const std::string& flag, SolverFactory* sf) { assert(sf); _factoryFlagStorage.push_back(std::make_pair(flag, sf)); } void SolverRegistry::removeFactoryFlag(const std::string& flag, SolverFactory* sf) { assert(sf); auto it = find(_factoryFlagStorage.begin(), _factoryFlagStorage.end(), std::make_pair(flag, sf)); _factoryFlagStorage.erase(it); } /// Function createSI also adds each SI to the local storage SolverInstanceBase* SolverFactory::createSI(Env& env, std::ostream& log, SolverInstanceBase::Options* opt) { SolverInstanceBase* pSI = doCreateSI(env, log, opt); if (pSI == nullptr) { throw InternalError("SolverFactory: failed to initialize solver " + getDescription()); } _sistorage.resize(_sistorage.size() + 1); _sistorage.back().reset(pSI); return pSI; } /// also providing a destroy function for a DLL or just special allocator etc. void SolverFactory::destroySI(SolverInstanceBase* pSI) { auto it = _sistorage.begin(); for (; it != _sistorage.end(); ++it) { if (it->get() == pSI) { break; } } if (_sistorage.end() == it) { cerr << " SolverFactory: failed to remove solver at " << pSI << endl; throw InternalError(" SolverFactory: failed to remove solver"); } _sistorage.erase(it); } MznSolver::MznSolver(std::ostream& os0, std::ostream& log0) : _solverConfigs(log0), _flt(os0, log0, _solverConfigs.mznlibDir()), _executableName(""), _os(os0), _log(log0), s2out(os0, log0, _solverConfigs.mznlibDir()) {} MznSolver::~MznSolver() { // if (si) // first the solver // CleanupSolverInterface(si); // TODO cleanup the used solver interfaces _si = nullptr; _siOpt = nullptr; GC::trigger(); } bool MznSolver::ifMzn2Fzn() const { return _isMzn2fzn; } bool MznSolver::ifSolns2out() const { return s2out.opt.flagStandaloneSolns2Out; } void MznSolver::addSolverInterface(SolverFactory* sf) { _si = sf->createSI(*_flt.getEnv(), _log, _siOpt); assert(_si); if (s2out.getEnv() == nullptr) { s2out.initFromEnv(_flt.getEnv()); } _si->setSolns2Out(&s2out); if (flagCompilerVerbose) { _log // << " ---------------------------------------------------------------------------\n" << " % SOLVING PHASE\n" << sf->getDescription(_siOpt) << endl; } } void MznSolver::addSolverInterface() { GCLock lock; if (_sf == nullptr) { if (get_global_solver_registry()->getSolverFactories().empty()) { _log << " MznSolver: NO SOLVER FACTORIES LINKED." << endl; assert(0); } _sf = get_global_solver_registry()->getSolverFactories().back(); } addSolverInterface(_sf); } void MznSolver::printUsage() { _os << _executableName << ": "; if (ifMzn2Fzn()) { _os << "MiniZinc to FlatZinc converter.\n" << "Usage: " << _executableName << " [] [-I ] .mzn [.dzn ...]" << std::endl; } else if (ifSolns2out()) { _os << "Solutions to output translator.\n" << "Usage: " << _executableName << " [] .ozn" << std::endl; } else { _os << "MiniZinc driver.\n" << "Usage: " << _executableName << " [] [-I ] .mzn [.dzn ...] or just .fzn" << std::endl; } } void MznSolver::printHelp(const std::string& selectedSolver) { printUsage(); _os << "General options:" << std::endl << " --help, -h\n Print this help message." << std::endl << " --version\n Print version information." << std::endl << " --solvers\n Print list of available solvers." << std::endl << " --time-limit \n Stop after milliseconds (includes compilation and solving)." << std::endl << " --solver , --solver .msc\n Select solver to use." << std::endl << " --help \n Print help for a particular solver." << std::endl << " -v, -l, --verbose\n Print progress/log statements. Note that some solvers may log " "to " "stdout." << std::endl << " --verbose-compilation\n Print progress/log statements for compilation." << std::endl << " -s, --statistics\n Print statistics." << std::endl << " --compiler-statistics\n Print statistics for compilation." << std::endl << " -c, --compile\n Compile only (do not run solver)." << std::endl << " --config-dirs\n Output configuration directories." << std::endl << " --param-file \n Load parameters from the given JSON file." << std::endl; if (selectedSolver.empty()) { _flt.printHelp(_os); _os << endl; if (!ifMzn2Fzn()) { Solns2Out::printHelp(_os); _os << endl; } _os << "Available solvers (get help using --help ):" << endl; std::vector solvers = _solverConfigs.solvers(); if (solvers.empty()) { cout << " none.\n"; } for (auto& solver : solvers) { cout << " " << solver << endl; } } else { const SolverConfig& sc = _solverConfigs.config(selectedSolver); string solverId = sc.executable().empty() ? sc.id() : (sc.supportsMzn() ? string("org.minizinc.mzn-mzn") : string("org.minizinc.mzn-fzn")); bool found = false; for (auto it = get_global_solver_registry()->getSolverFactories().rbegin(); it != get_global_solver_registry()->getSolverFactories().rend(); ++it) { if ((*it)->getId() == solverId) { _os << endl; (*it)->printHelp(_os); if (!sc.executable().empty() && !sc.extraFlags().empty()) { _os << "Extra solver flags (use with "; _os << (sc.supportsMzn() ? "--mzn-flags" : "--fzn-flags") << ")" << endl; for (const SolverConfig::ExtraFlag& ef : sc.extraFlags()) { _os << " " << ef.flag << endl << " " << ef.description << endl; } } found = true; } } if (!found) { _os << "No help found for solver " << selectedSolver << endl; } } } void add_flags(const std::string& sep, const std::vector& in_args, std::vector& out_args) { for (const std::string& arg : in_args) { out_args.push_back(sep); out_args.push_back(arg); } } MznSolver::OptionStatus MznSolver::processOptions(std::vector& argv) { _executableName = argv[0]; _executableName = _executableName.substr(_executableName.find_last_of("/\\") + 1); size_t lastdot = _executableName.find_last_of('.'); if (lastdot != std::string::npos) { _executableName = _executableName.substr(0, lastdot); } string solver; bool load_params = false; bool mzn2fzn_exe = (_executableName == "mzn2fzn"); if (mzn2fzn_exe) { _isMzn2fzn = true; } else if (_executableName == "solns2out") { s2out.opt.flagStandaloneSolns2Out = true; flagIsSolns2out = true; } bool compileSolutionChecker = false; int i = 1; int j = 1; int argc = static_cast(argv.size()); std::vector workingDirs = {""}; if (argc < 2) { return OPTION_ERROR; } // Add params from a file if necessary std::vector paramFiles; for (i = 1; i < argc; ++i) { string paramFile; bool usedFlag = false; bool pushWorkingDir = true; if (argv[i] == "--param-file") { usedFlag = true; ++i; if (i == argc) { _log << "Argument required for --param-file" << endl; return OPTION_ERROR; } paramFile = argv[i]; } else if (argv[i] == "--param-file-no-push") { usedFlag = true; pushWorkingDir = false; ++i; if (i == argc) { _log << "Argument required for --param-file-no-push" << endl; return OPTION_ERROR; } paramFile = argv[i]; } else if (argv[i] == "--push-working-directory") { ++i; workingDirs.push_back(argv[i]); } else if (argv[i] == "--pop-working-directory") { workingDirs.pop_back(); } else { size_t last_dot = argv[i].find_last_of('.'); if (last_dot != string::npos && argv[i].substr(last_dot, string::npos) == ".mpc") { paramFile = argv[i]; } } if (!paramFile.empty()) { try { auto paramFilePath = FileUtils::file_path(paramFile, workingDirs.back()); if (std::find(paramFiles.begin(), paramFiles.end(), paramFilePath) != paramFiles.end()) { throw ParamException("Cyclic parameter configuration file"); } // add parameter file arguments ParamConfig pc; pc.blacklist( {"--solvers", "--solvers-json", "--solver-json", "--help", "-h", "--config-dirs"}); pc.negatedFlag("-i", "-n-i"); pc.negatedFlag("--intermediate", "--no-intermediate"); pc.negatedFlag("--intermediate-solutions", "--no-intermediate-solutions"); pc.negatedFlag("--all-satisfaction", "--disable-all-satisfaction"); pc.load(paramFilePath); // Insert the new options auto toInsert = pc.argv(); auto remove = argv.begin() + (usedFlag ? i - 1 : i); auto position = argv.erase(remove, argv.begin() + i + 1); if (pushWorkingDir) { position = argv.insert(position, {"--push-working-directory", FileUtils::file_path(FileUtils::dir_name(paramFilePath))}) + 2; } position = argv.insert(position, toInsert.begin(), toInsert.end()) + toInsert.size(); if (pushWorkingDir) { position = argv.insert(position, "--pop-working-directory") + 1; } paramFiles.push_back(paramFilePath); argc = argv.size(); // Have to process the newly added options if (usedFlag) { i -= 2; } else { i--; } } catch (ParamException& e) { _log << "Solver parameter exception: " << e.msg() << endl; return OPTION_ERROR; } } } // Process any registered factory flags const auto& factoryFlags = get_global_solver_registry()->getFactoryFlags(); std::unordered_map> reducedSolverDefaults; if (!factoryFlags.empty()) { // Process solver default factory flags for (const auto& factoryFlag : factoryFlags) { auto factoryId = factoryFlag.second->getId(); if (reducedSolverDefaults.count(factoryId) == 0) { reducedSolverDefaults.insert({factoryId, _solverConfigs.defaultOptions(factoryId)}); } auto& defaultArgs = reducedSolverDefaults.find(factoryId)->second; std::vector keep; for (i = 0; i < defaultArgs.size(); i++) { if (defaultArgs[i] != factoryFlag.first || !factoryFlag.second->processFactoryOption(i, defaultArgs)) { keep.push_back(defaultArgs[i]); } } defaultArgs = keep; } // Process command line factory flags std::vector remaining = {argv[0]}; for (i = 1; i < argc; i++) { bool ok = false; if (argv[i] == "--push-working-directory") { remaining.push_back(argv[i]); i++; workingDirs.push_back(argv[i]); } else if (argv[i] == "--pop-working-directory") { workingDirs.pop_back(); } else { for (const auto& factoryFlag : factoryFlags) { if (argv[i] == factoryFlag.first && factoryFlag.second->processFactoryOption(i, argv, workingDirs.back())) { ok = true; break; } } } if (!ok) { remaining.push_back(argv[i]); } } argv = remaining; argc = remaining.size(); } for (auto* sf : get_global_solver_registry()->getSolverFactories()) { // Notify solver factories that factory flags are done sf->factoryOptionsFinished(); } // After this point all solver configurations must be available _solverConfigs.populate(_log); for (i = 1; i < argc; ++i) { if (argv[i] == "-h" || argv[i] == "--help") { if (argc > i + 1) { printHelp(argv[i + 1]); } else { printHelp(); } return OPTION_FINISH; } if (argv[i] == "--version") { Flattener::printVersion(cout); return OPTION_FINISH; } if (argv[i] == "--solvers") { cout << "MiniZinc driver.\nAvailable solver configurations:\n"; std::vector solvers = _solverConfigs.solvers(); if (solvers.empty()) { cout << " none.\n"; } for (auto& solver : solvers) { cout << " " << solver << endl; } cout << "Search path for solver configurations:\n"; for (const string& p : _solverConfigs.solverConfigsPath()) { cout << " " << p << endl; } return OPTION_FINISH; } if (argv[i] == "--solvers-json") { cout << _solverConfigs.solverConfigsJSON(); return OPTION_FINISH; } if (argv[i] == "--solver-json") { ++i; if (i == argc) { _log << "Argument required for --solver-json" << endl; return OPTION_ERROR; } if (!solver.empty() && solver != argv[i]) { _log << "Only one --solver-json option allowed" << endl; return OPTION_ERROR; } solver = argv[i]; const SolverConfig& sc = _solverConfigs.config(solver); cout << sc.toJSON(_solverConfigs); return OPTION_FINISH; } if (argv[i] == "--config-dirs") { GCLock lock; cout << "{\n"; cout << " \"globalConfigFile\" : \"" << Printer::escapeStringLit(FileUtils::global_config_file()) << "\",\n"; cout << " \"userConfigFile\" : \"" << Printer::escapeStringLit(FileUtils::user_config_file()) << "\",\n"; cout << " \"userSolverConfigDir\" : \"" << Printer::escapeStringLit(FileUtils::user_config_dir()) << "/solvers\",\n"; cout << " \"mznStdlibDir\" : \"" << Printer::escapeStringLit(_solverConfigs.mznlibDir()) << "\"\n"; cout << "}\n"; return OPTION_FINISH; } if (argv[i] == "--time-limit") { ++i; if (i == argc) { _log << "Argument required for --time-limit" << endl; return OPTION_ERROR; } flagOverallTimeLimit = atoi(argv[i].c_str()); } else if (argv[i] == "--solver") { ++i; if (i == argc) { _log << "Argument required for --solver" << endl; return OPTION_ERROR; } if (!solver.empty() && solver != argv[i]) { _log << "Only one --solver option allowed" << endl; return OPTION_ERROR; } solver = argv[i]; } else if (argv[i] == "-c" || argv[i] == "--compile") { _isMzn2fzn = true; } else if (argv[i] == "-v" || argv[i] == "--verbose" || argv[i] == "-l") { flagVerbose = true; flagCompilerVerbose = true; } else if (argv[i] == "--verbose-compilation") { flagCompilerVerbose = true; } else if (argv[i] == "-s" || argv[i] == "--statistics") { flagStatistics = true; flagCompilerStatistics = true; } else if (argv[i] == "--compiler-statistics") { flagCompilerStatistics = true; } else { if ((argv[i] == "--fzn-cmd" || argv[i] == "--flatzinc-cmd") && solver.empty()) { solver = "org.minizinc.mzn-fzn"; } if (argv[i] == "--compile-solution-checker") { compileSolutionChecker = true; } if (argv[i] == "--ozn-file") { flagIsSolns2out = true; } argv[j++] = argv[i]; } } argv.resize(j); argc = j; if ((mzn2fzn_exe || compileSolutionChecker) && solver.empty()) { solver = "org.minizinc.mzn-fzn"; } if (flagVerbose) { argv.emplace_back("--verbose-solving"); argc++; } if (flagStatistics) { argv.emplace_back("--solver-statistics"); argc++; } _flt.setFlagOutputByDefault(ifMzn2Fzn()); bool isMznMzn = false; if (!flagIsSolns2out) { try { const SolverConfig& sc = _solverConfigs.config(solver); string solverId; if (sc.executable().empty()) { solverId = sc.id(); } else if (sc.supportsMzn()) { solverId = "org.minizinc.mzn-mzn"; } else if (sc.supportsFzn()) { solverId = "org.minizinc.mzn-fzn"; } else if (sc.supportsNL()) { solverId = "org.minizinc.mzn-nl"; } else { _log << "Selected solver does not support MiniZinc, FlatZinc or NL input." << endl; return OPTION_ERROR; } // Check support of -a and -i for (const auto& flag : sc.stdFlags()) { if (flag == "-a") { _supportsA = true; } else if (flag == "-i") { _supportsI = true; } } for (auto* it : get_global_solver_registry()->getSolverFactories()) { if (it->getId() == solverId) { /// TODO: also check version (currently assumes all ids are unique) _sf = it; delete _siOpt; _siOpt = _sf->createOptions(); if (!sc.executable().empty() || solverId == "org.minizinc.mzn-fzn" || solverId == "org.minizinc.mzn-nl") { std::vector acceptedFlags; for (const auto& sf : sc.stdFlags()) { acceptedFlags.push_back(MZNFZNSolverFlag::std(sf)); } for (const auto& ef : sc.extraFlags()) { acceptedFlags.push_back(MZNFZNSolverFlag::extra(ef)); } // Collect arguments required for underlying exe vector fzn_mzn_flags; if (sc.needsStdlibDir()) { fzn_mzn_flags.emplace_back("--stdlib-dir"); fzn_mzn_flags.push_back(FileUtils::share_directory()); } if (sc.needsMznExecutable()) { fzn_mzn_flags.emplace_back("--minizinc-exe"); fzn_mzn_flags.push_back(FileUtils::progpath() + "/" + _executableName); } if (sc.supportsMzn()) { isMznMzn = true; MZNSolverFactory::setAcceptedFlags(_siOpt, acceptedFlags); std::vector additionalArgs_s; additionalArgs_s.emplace_back("-m"); if (!sc.executableResolved().empty()) { additionalArgs_s.push_back(sc.executableResolved()); } else { additionalArgs_s.push_back(sc.executable()); } if (!fzn_mzn_flags.empty()) { add_flags("--mzn-flag", fzn_mzn_flags, additionalArgs_s); } // This should maybe be moved to fill in fzn_mzn_flags when // --find-muses is implemented (these arguments will be passed // through to the subsolver of findMUS) if (!sc.mznlib().empty()) { if (sc.mznlib().substr(0, 2) == "-G") { additionalArgs_s.emplace_back("--mzn-flag"); additionalArgs_s.push_back(sc.mznlib()); } else { additionalArgs_s.emplace_back("--mzn-flag"); additionalArgs_s.emplace_back("-I"); additionalArgs_s.emplace_back("--mzn-flag"); std::string _mznlib; if (!sc.mznlibResolved().empty()) { _mznlib = sc.mznlibResolved(); } else { _mznlib = sc.mznlib(); } additionalArgs_s.push_back(_mznlib); } } for (i = 0; i < additionalArgs_s.size(); ++i) { bool success = _sf->processOption(_siOpt, i, additionalArgs_s); if (!success) { _log << "Solver backend " << solverId << " does not recognise option " << additionalArgs_s[i] << "." << endl; return OPTION_ERROR; } } } else { // supports fzn or nl std::vector additionalArgs; if (sc.supportsFzn()) { FZNSolverFactory::setAcceptedFlags(_siOpt, acceptedFlags); additionalArgs.emplace_back("--fzn-cmd"); } else { // supports nl additionalArgs.emplace_back("--nl-cmd"); } if (!sc.executableResolved().empty()) { additionalArgs.push_back(sc.executableResolved()); } else { additionalArgs.push_back(sc.executable()); } if (!fzn_mzn_flags.empty()) { if (sc.supportsFzn()) { add_flags("--fzn-flag", fzn_mzn_flags, additionalArgs); } else { add_flags("--nl-flag", fzn_mzn_flags, additionalArgs); } } if (sc.needsPathsFile()) { // Instruct flattener to hold onto paths int i = 0; vector args{"--keep-paths"}; _flt.processOption(i, args); // Instruct FznSolverInstance to write a path file // and pass it to the executable with --paths arg additionalArgs.emplace_back("--fzn-needs-paths"); } if (!sc.needsSolns2Out()) { additionalArgs.emplace_back("--fzn-output-passthrough"); } int i = 0; for (i = 0; i < additionalArgs.size(); ++i) { bool success = _sf->processOption(_siOpt, i, additionalArgs); if (!success) { _log << "Solver backend " << solverId << " does not recognise option " << additionalArgs[i] << "." << endl; return OPTION_ERROR; } } } } if (!sc.mznlib().empty()) { if (sc.mznlib().substr(0, 2) == "-G") { std::vector additionalArgs({sc.mznlib()}); int i = 0; if (!_flt.processOption(i, additionalArgs)) { _log << "Flattener does not recognise option " << sc.mznlib() << endl; return OPTION_ERROR; } } else { std::vector additionalArgs(2); additionalArgs[0] = "-I"; if (!sc.mznlibResolved().empty()) { additionalArgs[1] = sc.mznlibResolved(); } else { additionalArgs[1] = sc.mznlib(); } int i = 0; if (!_flt.processOption(i, additionalArgs)) { _log << "Flattener does not recognise option -I." << endl; return OPTION_ERROR; } } } auto reducedDefaultFlags = reducedSolverDefaults.find(sc.id()); const auto& defaultFlags = reducedDefaultFlags == reducedSolverDefaults.end() ? sc.defaultFlags() : reducedDefaultFlags->second; if (!defaultFlags.empty()) { std::vector addedArgs; addedArgs.push_back(argv[0]); // excutable name for (const auto& df : defaultFlags) { addedArgs.push_back(df); } for (int i = 1; i < argv.size(); i++) { addedArgs.push_back(argv[i]); } argv = addedArgs; argc = addedArgs.size(); } break; } } } catch (ConfigException& e) { _log << "Config exception: " << e.msg() << endl; return OPTION_ERROR; } if (_sf == nullptr) { _log << "Solver " << solver << " not found." << endl; return OPTION_ERROR; } CLOParser cop(i, argv); // For special handling of -a, -i and -n-i for (i = 1; i < argc; ++i) { if (argv[i] == "--push-working-directory") { i++; workingDirs.push_back(argv[i]); } else if (argv[i] == "--pop-working-directory") { workingDirs.pop_back(); } else if (!ifMzn2Fzn() ? s2out.processOption(i, argv, workingDirs.back()) : false) { // NOLINT: Allow repeated empty if // Processed by Solns2Out } else if ((!isMznMzn || _isMzn2fzn) && _flt.processOption(i, argv, workingDirs.back())) { // NOLINT: Allow repeated empty if // Processed by Flattener } else if ((_supportsA || _supportsI) && cop.get("-a --all --all-solns --all-solutions")) { _flagAllSatisfaction = true; _flagIntermediate = true; } else if ((_supportsA || _supportsI) && cop.get("-i --intermediate --intermediate-solutions")) { _flagIntermediate = true; } else if (cop.getOption("-n-i --no-intermediate --no-intermediate-solutions")) { _flagIntermediate = false; } else if (_supportsA && cop.get("--all-satisfaction")) { _flagAllSatisfaction = true; } else if (cop.get("--disable-all-satisfaction")) { _flagAllSatisfaction = false; } else if (_sf != nullptr && _sf->processOption(_siOpt, i, argv)) { // NOLINT: Allow repeated empty if // Processed by Solver Factory } else { std::string executable_name(argv[0]); executable_name = executable_name.substr(executable_name.find_last_of("/\\") + 1); _log << executable_name << ": Unrecognized option or bad format `" << argv[i] << "'" << endl; return OPTION_ERROR; } } return OPTION_OK; } for (i = 1; i < argc; ++i) { if (argv[i] == "--push-working-directory") { i++; workingDirs.push_back(argv[i]); } else if (argv[i] == "--pop-working-directory") { workingDirs.pop_back(); } else if (s2out.processOption(i, argv, workingDirs.back())) { // Processed by Solns2Out } else { std::string executable_name(argv[0]); executable_name = executable_name.substr(executable_name.find_last_of("/\\") + 1); _log << executable_name << ": Unrecognized option or bad format `" << argv[i] << "'" << endl; return OPTION_ERROR; } } return OPTION_OK; } void MznSolver::flatten(const std::string& modelString, const std::string& modelName) { _flt.setFlagVerbose(flagCompilerVerbose); _flt.setFlagStatistics(flagCompilerStatistics); _flt.setFlagTimelimit(flagOverallTimeLimit); _flt.flatten(modelString, modelName); } SolverInstance::Status MznSolver::solve() { { // To be able to clean up flatzinc after PrcessFlt() GCLock lock; getSI()->processFlatZinc(); } SolverInstance::Status status = getSI()->solve(); GCLock lock; if (!getSI()->getSolns2Out()->fStatusPrinted) { getSI()->getSolns2Out()->evalStatus(status); } if (_siOpt->printStatistics) { getSI()->printStatistics(); } if (flagStatistics) { getSI()->getSolns2Out()->printStatistics(_os); } return status; } SolverInstance::Status MznSolver::run(const std::vector& args0, const std::string& model, const std::string& exeName, const std::string& modelName) { using namespace std::chrono; steady_clock::time_point startTime = steady_clock::now(); std::vector args = {exeName}; for (const auto& a : args0) { args.push_back(a); } switch (processOptions(args)) { case OPTION_FINISH: return SolverInstance::NONE; case OPTION_ERROR: printUsage(); _os << "More info with \"" << exeName << " --help\"\n"; return SolverInstance::ERROR; case OPTION_OK: break; } if (flagIsSolns2out && (ifMzn2Fzn() || _sf == nullptr || _sf->getId() != "org.minizinc.mzn-mzn") && !_flt.hasInputFiles() && model.empty()) { // We are in solns2out mode while (std::cin.good()) { string line; getline(std::cin, line); line += '\n'; // need eols as in t=raw stream s2out.feedRawDataChunk(line.c_str()); } return SolverInstance::NONE; } if (!ifMzn2Fzn() && _sf->getId() == "org.minizinc.mzn-mzn") { Env env; _si = _sf->createSI(env, _log, _siOpt); _si->setSolns2Out(&s2out); { // To be able to clean up flatzinc after PrcessFlt() GCLock lock; _si->options()->verbose = getFlagVerbose(); _si->options()->printStatistics = getFlagStatistics(); } _si->solve(); return SolverInstance::NONE; } try { flatten(model, modelName); } catch (Timeout&) { s2out.evalStatus(SolverInstance::UNKNOWN); return SolverInstance::UNKNOWN; } if (!ifMzn2Fzn() && flagOverallTimeLimit != 0) { steady_clock::time_point afterFlattening = steady_clock::now(); milliseconds passed = duration_cast(afterFlattening - startTime); milliseconds time_limit(flagOverallTimeLimit); if (passed > time_limit) { s2out.evalStatus(getFltStatus()); return SolverInstance::UNKNOWN; } int time_left = (time_limit - passed).count(); std::vector timeoutArgs(2); timeoutArgs[0] = "--solver-time-limit"; std::ostringstream oss; oss << time_left; timeoutArgs[1] = oss.str(); int i = 0; _sf->processOption(_siOpt, i, timeoutArgs); } if (SolverInstance::UNKNOWN == getFltStatus()) { if (!ifMzn2Fzn()) { // only then // Special handling of basic stdFlags auto* solve_item = _flt.getEnv()->model()->solveItem(); bool is_sat_problem = solve_item != nullptr ? solve_item->st() == SolveI::SolveType::ST_SAT : true; if (is_sat_problem && _flagAllSatisfaction) { if (_supportsA) { std::vector a_flag = {"-a"}; int i = 0; _sf->processOption(_siOpt, i, a_flag); } else { // Solver does not support -a _log << "WARNING: Solver does not support all solutions for satisfaction problems." << endl; } } if (!is_sat_problem && _flagIntermediate) { std::vector i_flag(1); i_flag[0] = _supportsI ? "-i" : "-a"; // Fallback to -a if -i is not supported int i = 0; _sf->processOption(_siOpt, i, i_flag); } // GCLock lock; // better locally, to enable cleanup after ProcessFlt() addSolverInterface(); return solve(); } return SolverInstance::NONE; } if (!ifMzn2Fzn()) { s2out.evalStatus(getFltStatus()); } return getFltStatus(); // Add evalOutput() here? TODO }