this repo has no description
at develop 58 kB view raw
1// * -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ 2 3/* 4 * Main authors: 5 * Gleb Belov <gleb.belov@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/config.hh> 17#include <minizinc/file_utils.hh> 18#include <minizinc/utils.hh> 19 20#include <cmath> 21#include <cstring> 22#include <fstream> 23#include <iomanip> 24#include <iostream> 25#include <sstream> 26#include <stdexcept> 27#include <string> 28 29#ifdef CPLEX_PLUGIN 30#ifdef HAS_DLFCN_H 31#include <dlfcn.h> 32#elif defined HAS_WINDOWS_H 33#define NOMINMAX // Ensure the words min/max remain available 34#include <Windows.h> 35#undef ERROR 36#endif 37#endif 38 39#include <minizinc/solvers/MIP/MIP_cplex_wrap.hh> 40 41using namespace std; 42 43#ifdef CPLEX_PLUGIN 44 45namespace { 46void* dll_open(const std::string& file) { 47#ifdef HAS_DLFCN_H 48 if (MiniZinc::FileUtils::is_absolute(file)) { 49 return dlopen(file.c_str(), RTLD_NOW); 50 } 51 if (void* so = dlopen(("lib" + file + ".so").c_str(), RTLD_NOW)) { 52 return so; 53 } 54 return dlopen(("lib" + file + ".jnilib").c_str(), RTLD_NOW); 55#else 56 if (MiniZinc::FileUtils::is_absolute(file)) { 57 return LoadLibrary(file.c_str()); 58 } 59 return LoadLibrary((file + ".dll").c_str()); 60#endif 61} 62void* dll_sym(void* dll, const char* sym) { 63#ifdef HAS_DLFCN_H 64 void* ret = dlsym(dll, sym); 65#else 66 void* ret = GetProcAddress((HMODULE)dll, sym); 67#endif 68 if (ret == nullptr) { 69 throw MiniZinc::InternalError("cannot load symbol " + string(sym) + " from CPLEX dll"); 70 } 71 return ret; 72} 73void dll_close(void* dll) { 74#ifdef HAS_DLFCN_H 75 dlclose(dll); 76#else 77 FreeLibrary((HMODULE)dll); 78#endif 79} 80} // namespace 81 82#endif 83 84const vector<string>& cplex_dlls() { 85 static const vector<string> sCPLEXDLLs = {"cplex12100", "cplex1290", "cplex1280", "cplex1270"}; 86 return sCPLEXDLLs; 87} 88 89void MIPCplexWrapper::checkDLL() { 90#ifdef CPLEX_PLUGIN 91 _cplexDll = nullptr; 92 if (!_factoryOptions.cplexDll.empty()) { 93 _cplexDll = dll_open(_factoryOptions.cplexDll); 94 } else { 95 for (const auto& s : cplex_dlls()) { 96 _cplexDll = dll_open(s); 97 if (nullptr != _cplexDll) { 98 break; 99 } 100 } 101 } 102 103 if (_cplexDll == nullptr) { 104 if (_factoryOptions.cplexDll.empty()) { 105 throw MiniZinc::InternalError("cannot load cplex dll, specify --cplex-dll"); 106 } 107 throw MiniZinc::InternalError("cannot load cplex dll `" + _factoryOptions.cplexDll + "'"); 108 } 109 110 *(void**)(&dll_CPXaddfuncdest) = dll_sym(_cplexDll, "CPXaddfuncdest"); 111 *(void**)(&dll_CPXaddindconstr) = dll_sym(_cplexDll, "CPXaddindconstr"); 112 *(void**)(&dll_CPXaddlazyconstraints) = dll_sym(_cplexDll, "CPXaddlazyconstraints"); 113 *(void**)(&dll_CPXaddmipstarts) = dll_sym(_cplexDll, "CPXaddmipstarts"); 114 *(void**)(&dll_CPXaddrows) = dll_sym(_cplexDll, "CPXaddrows"); 115 *(void**)(&dll_CPXaddusercuts) = dll_sym(_cplexDll, "CPXaddusercuts"); 116 *(void**)(&dll_CPXchgbds) = dll_sym(_cplexDll, "CPXchgbds"); 117 *(void**)(&dll_CPXchgmipstarts) = dll_sym(_cplexDll, "CPXchgmipstarts"); 118 *(void**)(&dll_CPXchgobjsen) = dll_sym(_cplexDll, "CPXchgobjsen"); 119 *(void**)(&dll_CPXcloseCPLEX) = dll_sym(_cplexDll, "CPXcloseCPLEX"); 120 *(void**)(&dll_CPXcreateprob) = dll_sym(_cplexDll, "CPXcreateprob"); 121 *(void**)(&dll_CPXcutcallbackadd) = dll_sym(_cplexDll, "CPXcutcallbackadd"); 122 *(void**)(&dll_CPXfreeprob) = dll_sym(_cplexDll, "CPXfreeprob"); 123 *(void**)(&dll_CPXgetbestobjval) = dll_sym(_cplexDll, "CPXgetbestobjval"); 124 *(void**)(&dll_CPXgetcallbackincumbent) = dll_sym(_cplexDll, "CPXgetcallbackincumbent"); 125 *(void**)(&dll_CPXgetcallbackinfo) = dll_sym(_cplexDll, "CPXgetcallbackinfo"); 126 *(void**)(&dll_CPXgetcallbacknodeinfo) = dll_sym(_cplexDll, "CPXgetcallbacknodeinfo"); 127 *(void**)(&dll_CPXgetcallbacknodex) = dll_sym(_cplexDll, "CPXgetcallbacknodex"); 128 *(void**)(&dll_CPXgetchannels) = dll_sym(_cplexDll, "CPXgetchannels"); 129 *(void**)(&dll_CPXgetdettime) = dll_sym(_cplexDll, "CPXgetdettime"); 130 *(void**)(&dll_CPXgeterrorstring) = dll_sym(_cplexDll, "CPXgeterrorstring"); 131 *(void**)(&dll_CPXgetmipstartindex) = dll_sym(_cplexDll, "CPXgetmipstartindex"); 132 *(void**)(&dll_CPXgetnodecnt) = dll_sym(_cplexDll, "CPXgetnodecnt"); 133 *(void**)(&dll_CPXgetnodeleftcnt) = dll_sym(_cplexDll, "CPXgetnodeleftcnt"); 134 *(void**)(&dll_CPXgetnumcols) = dll_sym(_cplexDll, "CPXgetnumcols"); 135 *(void**)(&dll_CPXgetnumrows) = dll_sym(_cplexDll, "CPXgetnumrows"); 136 *(void**)(&dll_CPXgetobjsen) = dll_sym(_cplexDll, "CPXgetobjsen"); 137 *(void**)(&dll_CPXgetobjval) = dll_sym(_cplexDll, "CPXgetobjval"); 138 *(void**)(&dll_CPXgetsolnpoolnumsolns) = dll_sym(_cplexDll, "CPXgetsolnpoolnumsolns"); 139 *(void**)(&dll_CPXgetstat) = dll_sym(_cplexDll, "CPXgetstat"); 140 *(void**)(&dll_CPXgetstatstring) = dll_sym(_cplexDll, "CPXgetstatstring"); 141 *(void**)(&dll_CPXgettime) = dll_sym(_cplexDll, "CPXgettime"); 142 *(void**)(&dll_CPXgetx) = dll_sym(_cplexDll, "CPXgetx"); 143 *(void**)(&dll_CPXmipopt) = dll_sym(_cplexDll, "CPXmipopt"); 144 *(void**)(&dll_CPXnewcols) = dll_sym(_cplexDll, "CPXnewcols"); 145 *(void**)(&dll_CPXopenCPLEX) = dll_sym(_cplexDll, "CPXopenCPLEX"); 146 *(void**)(&dll_CPXreadcopyparam) = dll_sym(_cplexDll, "CPXreadcopyparam"); 147 *(void**)(&dll_CPXsetdblparam) = dll_sym(_cplexDll, "CPXsetdblparam"); 148 *(void**)(&dll_CPXsetinfocallbackfunc) = dll_sym(_cplexDll, "CPXsetinfocallbackfunc"); 149 *(void**)(&dll_CPXsetintparam) = dll_sym(_cplexDll, "CPXsetintparam"); 150 *(void**)(&dll_CPXsetstrparam) = dll_sym(_cplexDll, "CPXsetstrparam"); 151 *(void**)(&dll_CPXsetlazyconstraintcallbackfunc) = 152 dll_sym(_cplexDll, "CPXsetlazyconstraintcallbackfunc"); 153 *(void**)(&dll_CPXsetusercutcallbackfunc) = dll_sym(_cplexDll, "CPXsetusercutcallbackfunc"); 154 *(void**)(&dll_CPXversion) = dll_sym(_cplexDll, "CPXversion"); 155 *(void**)(&dll_CPXwriteparam) = dll_sym(_cplexDll, "CPXwriteparam"); 156 *(void**)(&dll_CPXwriteprob) = dll_sym(_cplexDll, "CPXwriteprob"); 157 *(void**)(&dll_CPXgetparamname) = dll_sym(_cplexDll, "CPXgetparamname"); 158 *(void**)(&dll_CPXgetparamnum) = dll_sym(_cplexDll, "CPXgetparamnum"); 159 *(void**)(&dll_CPXgetparamtype) = dll_sym(_cplexDll, "CPXgetparamtype"); 160 *(void**)(&dll_CPXinfointparam) = dll_sym(_cplexDll, "CPXinfointparam"); 161 *(void**)(&dll_CPXinfolongparam) = dll_sym(_cplexDll, "CPXinfolongparam"); 162 *(void**)(&dll_CPXinfodblparam) = dll_sym(_cplexDll, "CPXinfodblparam"); 163 *(void**)(&dll_CPXinfostrparam) = dll_sym(_cplexDll, "CPXinfostrparam"); 164 *(void**)(&dll_CPXsetlongparam) = dll_sym(_cplexDll, "CPXsetlongparam"); 165 166#else 167 168 dll_CPXaddfuncdest = CPXaddfuncdest; 169 dll_CPXaddindconstr = CPXaddindconstr; 170 dll_CPXaddlazyconstraints = CPXaddlazyconstraints; 171 dll_CPXaddmipstarts = CPXaddmipstarts; 172 dll_CPXaddrows = CPXaddrows; 173 dll_CPXaddusercuts = CPXaddusercuts; 174 dll_CPXchgbds = CPXchgbds; 175 dll_CPXchgmipstarts = CPXchgmipstarts; 176 dll_CPXchgobjsen = CPXchgobjsen; 177 dll_CPXcloseCPLEX = CPXcloseCPLEX; 178 dll_CPXcreateprob = CPXcreateprob; 179 dll_CPXcutcallbackadd = CPXcutcallbackadd; 180 dll_CPXfreeprob = CPXfreeprob; 181 dll_CPXgetbestobjval = CPXgetbestobjval; 182 dll_CPXgetcallbackincumbent = CPXgetcallbackincumbent; 183 dll_CPXgetcallbackinfo = CPXgetcallbackinfo; 184 dll_CPXgetcallbacknodeinfo = CPXgetcallbacknodeinfo; 185 dll_CPXgetcallbacknodex = CPXgetcallbacknodex; 186 dll_CPXgetchannels = CPXgetchannels; 187 dll_CPXgetdettime = CPXgetdettime; 188 dll_CPXgeterrorstring = CPXgeterrorstring; 189 dll_CPXgetmipstartindex = CPXgetmipstartindex; 190 dll_CPXgetnodecnt = CPXgetnodecnt; 191 dll_CPXgetnodeleftcnt = CPXgetnodeleftcnt; 192 dll_CPXgetnumcols = CPXgetnumcols; 193 dll_CPXgetnumrows = CPXgetnumrows; 194 dll_CPXgetobjsen = CPXgetobjsen; 195 dll_CPXgetobjval = CPXgetobjval; 196 dll_CPXgetsolnpoolnumsolns = CPXgetsolnpoolnumsolns; 197 dll_CPXgetstat = CPXgetstat; 198 dll_CPXgetstatstring = CPXgetstatstring; 199 dll_CPXgettime = CPXgettime; 200 dll_CPXgetx = CPXgetx; 201 dll_CPXmipopt = CPXmipopt; 202 dll_CPXnewcols = CPXnewcols; 203 dll_CPXopenCPLEX = CPXopenCPLEX; 204 dll_CPXreadcopyparam = CPXreadcopyparam; 205 dll_CPXsetdblparam = CPXsetdblparam; 206 dll_CPXsetinfocallbackfunc = CPXsetinfocallbackfunc; 207 dll_CPXsetintparam = CPXsetintparam; 208 dll_CPXsetstrparam = CPXsetstrparam; 209 dll_CPXsetlazyconstraintcallbackfunc = CPXsetlazyconstraintcallbackfunc; 210 dll_CPXsetusercutcallbackfunc = CPXsetusercutcallbackfunc; 211 dll_CPXversion = CPXversion; 212 dll_CPXwriteparam = CPXwriteparam; 213 dll_CPXwriteprob = CPXwriteprob; 214 dll_CPXgetparamname = CPXgetparamname; 215 dll_CPXgetparamnum = CPXgetparamnum; 216 dll_CPXgetparamtype = CPXgetparamtype; 217 dll_CPXinfointparam = CPXinfointparam; 218 dll_CPXinfolongparam = CPXinfolongparam; 219 dll_CPXinfodblparam = CPXinfodblparam; 220 dll_CPXinfostrparam = CPXinfostrparam; 221 dll_CPXsetlongparam = CPXsetlongparam; 222 223#endif 224} 225 226string MIPCplexWrapper::getDescription(FactoryOptions& factoryOpt, 227 MiniZinc::SolverInstanceBase::Options* opt) { 228 string v = "MIP wrapper for IBM ILOG CPLEX "; 229 int status; 230 Options def_options; 231 Options* options = opt != nullptr ? static_cast<Options*>(opt) : &def_options; 232 try { 233 MIPCplexWrapper mcw(factoryOpt, options); 234 CPXENVptr env = mcw.dll_CPXopenCPLEX(&status); 235 if (env != nullptr) { 236 v += mcw.dll_CPXversion(env); 237 status = mcw.dll_CPXcloseCPLEX(&env); 238 } else { 239 v += "[?? ...cannot open CPLEX env to query version]"; 240 } 241 } catch (MiniZinc::InternalError&) { 242 v += "[?? ...cannot open CPLEX env to query version]"; 243 } 244 v += " Compiled " __DATE__ " " __TIME__; 245 return v; 246} 247 248string MIPCplexWrapper::getVersion(FactoryOptions& factoryOpt, 249 MiniZinc::SolverInstanceBase::Options* opt) { 250 string v; 251 int status; 252 Options def_options; 253 Options* options = opt != nullptr ? static_cast<Options*>(opt) : &def_options; 254 try { 255 MIPCplexWrapper mcw(factoryOpt, options); 256 CPXENVptr env = mcw.dll_CPXopenCPLEX(&status); 257 if (env != nullptr) { 258 v += mcw.dll_CPXversion(env); 259 status = mcw.dll_CPXcloseCPLEX(&env); 260 } else { 261 v += "<unknown version>"; 262 } 263 } catch (MiniZinc::InternalError&) { 264 v += "<unknown version>"; 265 } 266 return v; 267} 268 269vector<string> MIPCplexWrapper::getRequiredFlags(FactoryOptions& f) { 270 int status; 271 FactoryOptions factoryOptions; 272 Options options; 273 try { 274 MIPCplexWrapper mcw(factoryOptions, &options); 275 CPXENVptr env = mcw.dll_CPXopenCPLEX(&status); 276 if (env != nullptr) { 277 return {}; 278 } 279 } catch (MiniZinc::InternalError&) { 280 } 281 return {"--cplex-dll"}; 282} 283 284vector<string> MIPCplexWrapper::getFactoryFlags() { return {"--cplex-dll"}; } 285 286string MIPCplexWrapper::getId() { return "cplex"; } 287 288string MIPCplexWrapper::getName() { return "CPLEX"; } 289 290vector<string> MIPCplexWrapper::getTags() { return {"mip", "float", "api"}; } 291 292vector<string> MIPCplexWrapper::getStdFlags() { return {"-i", "-p", "-s", "-v"}; } 293 294vector<MiniZinc::SolverConfig::ExtraFlag> MIPCplexWrapper::getExtraFlags( 295 FactoryOptions& factoryOpt) { 296 // CPLEX doesn't seem to have a way to enumerate all parameters 297 static std::vector<std::string> all_params = { 298 "CPXPARAM_Advance", "CPXPARAM_Barrier_Algorithm", "CPXPARAM_Barrier_ColNonzeros", 299 "CPXPARAM_Barrier_ConvergeTol", "CPXPARAM_Barrier_Crossover", "CPXPARAM_Barrier_Display", 300 "CPXPARAM_Barrier_Limits_Corrections", "CPXPARAM_Barrier_Limits_Growth", 301 "CPXPARAM_Barrier_Limits_Iteration", "CPXPARAM_Barrier_Limits_ObjRange", 302 "CPXPARAM_Barrier_Ordering", "CPXPARAM_Barrier_QCPConvergeTol", "CPXPARAM_Barrier_StartAlg", 303 "CPXPARAM_Benders_Strategy", "CPXPARAM_Benders_Tolerances_feasibilitycut", 304 "CPXPARAM_Benders_Tolerances_optimalitycut", "CPXPARAM_Benders_WorkerAlgorithm", 305 "CPXPARAM_ClockType", "CPXPARAM_Conflict_Algorithm", "CPXPARAM_Conflict_Display", 306 "CPXPARAM_CPUmask", "CPXPARAM_DetTimeLimit", "CPXPARAM_DistMIP_Rampup_DetTimeLimit", 307 "CPXPARAM_DistMIP_Rampup_Duration", "CPXPARAM_DistMIP_Rampup_TimeLimit", 308 "CPXPARAM_Emphasis_Memory", 309 // "CPXPARAM_Emphasis_MIP", 310 "CPXPARAM_Emphasis_Numerical", "CPXPARAM_Feasopt_Mode", "CPXPARAM_Feasopt_Tolerance", 311 "CPXPARAM_LPMethod", "CPXPARAM_MIP_Cuts_BQP", "CPXPARAM_MIP_Cuts_Cliques", 312 "CPXPARAM_MIP_Cuts_Covers", "CPXPARAM_MIP_Cuts_Disjunctive", "CPXPARAM_MIP_Cuts_FlowCovers", 313 "CPXPARAM_MIP_Cuts_Gomory", "CPXPARAM_MIP_Cuts_GUBCovers", "CPXPARAM_MIP_Cuts_Implied", 314 "CPXPARAM_MIP_Cuts_LiftProj", "CPXPARAM_MIP_Cuts_LocalImplied", "CPXPARAM_MIP_Cuts_MCFCut", 315 "CPXPARAM_MIP_Cuts_MIRCut", "CPXPARAM_MIP_Cuts_PathCut", "CPXPARAM_MIP_Cuts_RLT", 316 "CPXPARAM_MIP_Cuts_ZeroHalfCut", 317 // "CPXPARAM_MIP_Display", 318 "CPXPARAM_MIP_Interval", "CPXPARAM_MIP_Limits_AggForCut", 319 "CPXPARAM_MIP_Limits_AuxRootThreads", "CPXPARAM_MIP_Limits_CutPasses", 320 "CPXPARAM_MIP_Limits_CutsFactor", "CPXPARAM_MIP_Limits_EachCutLimit", 321 "CPXPARAM_MIP_Limits_GomoryCand", "CPXPARAM_MIP_Limits_GomoryPass", 322 "CPXPARAM_MIP_Limits_Nodes", "CPXPARAM_MIP_Limits_PolishTime", "CPXPARAM_MIP_Limits_Populate", 323 "CPXPARAM_MIP_Limits_ProbeDetTime", "CPXPARAM_MIP_Limits_ProbeTime", 324 "CPXPARAM_MIP_Limits_RepairTries", 325 // "CPXPARAM_MIP_Limits_Solutions", 326 "CPXPARAM_MIP_Limits_StrongCand", "CPXPARAM_MIP_Limits_StrongIt", 327 "CPXPARAM_MIP_Limits_TreeMemory", "CPXPARAM_MIP_OrderType", 328 "CPXPARAM_MIP_PolishAfter_AbsMIPGap", "CPXPARAM_MIP_PolishAfter_DetTime", 329 "CPXPARAM_MIP_PolishAfter_MIPGap", "CPXPARAM_MIP_PolishAfter_Nodes", 330 "CPXPARAM_MIP_PolishAfter_Solutions", "CPXPARAM_MIP_PolishAfter_Time", 331 "CPXPARAM_MIP_Pool_AbsGap", "CPXPARAM_MIP_Pool_Capacity", "CPXPARAM_MIP_Pool_Intensity", 332 "CPXPARAM_MIP_Pool_RelGap", "CPXPARAM_MIP_Pool_Replace", "CPXPARAM_MIP_Strategy_Backtrack", 333 "CPXPARAM_MIP_Strategy_BBInterval", "CPXPARAM_MIP_Strategy_Branch", 334 // "CPXPARAM_MIP_Strategy_CallbackReducedLP", 335 "CPXPARAM_MIP_Strategy_Dive", 336 // "CPXPARAM_MIP_Strategy_File", 337 "CPXPARAM_MIP_Strategy_FPHeur", "CPXPARAM_MIP_Strategy_HeuristicEffort", 338 "CPXPARAM_MIP_Strategy_HeuristicFreq", "CPXPARAM_MIP_Strategy_KappaStats", 339 "CPXPARAM_MIP_Strategy_LBHeur", "CPXPARAM_MIP_Strategy_MIQCPStrat", 340 "CPXPARAM_MIP_Strategy_NodeSelect", "CPXPARAM_MIP_Strategy_Order", 341 "CPXPARAM_MIP_Strategy_PresolveNode", "CPXPARAM_MIP_Strategy_Probe", 342 "CPXPARAM_MIP_Strategy_RINSHeur", 343 // "CPXPARAM_MIP_Strategy_Search", 344 "CPXPARAM_MIP_Strategy_StartAlgorithm", "CPXPARAM_MIP_Strategy_SubAlgorithm", 345 "CPXPARAM_MIP_Strategy_VariableSelect", "CPXPARAM_MIP_SubMIP_StartAlg", 346 "CPXPARAM_MIP_SubMIP_SubAlg", "CPXPARAM_MIP_SubMIP_NodeLimit", "CPXPARAM_MIP_SubMIP_Scale", 347 // "CPXPARAM_MIP_Tolerances_AbsMIPGap", 348 "CPXPARAM_MIP_Tolerances_Linearization", 349 // "CPXPARAM_MIP_Tolerances_Integrality", 350 "CPXPARAM_MIP_Tolerances_LowerCutoff", 351 // "CPXPARAM_MIP_Tolerances_MIPGap", 352 "CPXPARAM_MIP_Tolerances_ObjDifference", "CPXPARAM_MIP_Tolerances_RelObjDifference", 353 "CPXPARAM_MIP_Tolerances_UpperCutoff", "CPXPARAM_MultiObjective_Display", 354 "CPXPARAM_Network_Display", "CPXPARAM_Network_Iterations", "CPXPARAM_Network_NetFind", 355 "CPXPARAM_Network_Pricing", "CPXPARAM_Network_Tolerances_Feasibility", 356 "CPXPARAM_Network_Tolerances_Optimality", "CPXPARAM_OptimalityTarget", 357 "CPXPARAM_Output_CloneLog", "CPXPARAM_Output_IntSolFilePrefix", "CPXPARAM_Output_MPSLong", 358 "CPXPARAM_Output_WriteLevel", "CPXPARAM_Parallel", "CPXPARAM_ParamDisplay", 359 "CPXPARAM_Preprocessing_Aggregator", "CPXPARAM_Preprocessing_BoundStrength", 360 "CPXPARAM_Preprocessing_CoeffReduce", "CPXPARAM_Preprocessing_Dependency", 361 "CPXPARAM_Preprocessing_Dual", "CPXPARAM_Preprocessing_Fill", 362 "CPXPARAM_Preprocessing_Folding", 363 // "CPXPARAM_Preprocessing_Linear", 364 "CPXPARAM_Preprocessing_NumPass", "CPXPARAM_Preprocessing_Presolve", 365 "CPXPARAM_Preprocessing_QCPDuals", "CPXPARAM_Preprocessing_QPMakePSD", 366 "CPXPARAM_Preprocessing_QToLin", "CPXPARAM_Preprocessing_Reduce", 367 "CPXPARAM_Preprocessing_Relax", "CPXPARAM_Preprocessing_RepeatPresolve", 368 "CPXPARAM_Preprocessing_Symmetry", "CPXPARAM_QPMethod", 369 // "CPXPARAM_RandomSeed", 370 "CPXPARAM_Read_APIEncoding", "CPXPARAM_Read_Constraints", "CPXPARAM_Read_DataCheck", 371 "CPXPARAM_Read_FileEncoding", "CPXPARAM_Read_Nonzeros", "CPXPARAM_Read_QPNonzeros", 372 "CPXPARAM_Read_Scale", "CPXPARAM_Read_Variables", "CPXPARAM_Read_WarningLimit", 373 "CPXPARAM_Record", "CPXPARAM_ScreenOutput", "CPXPARAM_Sifting_Algorithm", 374 "CPXPARAM_Sifting_Simplex", "CPXPARAM_Sifting_Display", "CPXPARAM_Sifting_Iterations", 375 "CPXPARAM_Simplex_Crash", "CPXPARAM_Simplex_DGradient", "CPXPARAM_Simplex_Display", 376 "CPXPARAM_Simplex_DynamicRows", "CPXPARAM_Simplex_Limits_Iterations", 377 "CPXPARAM_Simplex_Limits_LowerObj", "CPXPARAM_Simplex_Limits_Perturbation", 378 "CPXPARAM_Simplex_Limits_Singularity", "CPXPARAM_Simplex_Limits_UpperObj", 379 "CPXPARAM_Simplex_Perturbation_Constant", "CPXPARAM_Simplex_Perturbation_Indicator", 380 "CPXPARAM_Simplex_PGradient", "CPXPARAM_Simplex_Pricing", "CPXPARAM_Simplex_Refactor", 381 "CPXPARAM_Simplex_Tolerances_Feasibility", "CPXPARAM_Simplex_Tolerances_Markowitz", 382 "CPXPARAM_Simplex_Tolerances_Optimality", "CPXPARAM_SolutionType", 383 // "CPXPARAM_Threads", 384 // "CPXPARAM_TimeLimit", 385 "CPXPARAM_Tune_DetTimeLimit", "CPXPARAM_Tune_Display", "CPXPARAM_Tune_Measure", 386 "CPXPARAM_Tune_Repeat", "CPXPARAM_Tune_TimeLimit", 387 // "CPXPARAM_WorkDir", 388 // "CPXPARAM_WorkMem", 389 "CPX_PARAM_ADVIND", "CPX_PARAM_AGGFILL", "CPX_PARAM_AGGIND", "CPX_PARAM_CLOCKTYPE", 390 "CPX_PARAM_CRAIND", "CPX_PARAM_DEPIND", "CPX_PARAM_DPRIIND", "CPX_PARAM_PRICELIM", 391 "CPX_PARAM_EPMRK", "CPX_PARAM_EPOPT", "CPX_PARAM_EPPER", "CPX_PARAM_EPRHS", 392 "CPX_PARAM_SIMDISPLAY", "CPX_PARAM_ITLIM", "CPX_PARAM_ROWREADLIM", "CPX_PARAM_NETFIND", 393 "CPX_PARAM_COLREADLIM", "CPX_PARAM_NZREADLIM", "CPX_PARAM_OBJLLIM", "CPX_PARAM_OBJULIM", 394 "CPX_PARAM_PERIND", "CPX_PARAM_PERLIM", "CPX_PARAM_PPRIIND", "CPX_PARAM_PREIND", 395 "CPX_PARAM_REINV", "CPX_PARAM_SCAIND", "CPX_PARAM_SCRIND", "CPX_PARAM_SINGLIM", 396 "CPX_PARAM_TILIM", "CPX_PARAM_PREDUAL", "CPX_PARAM_PREPASS", "CPX_PARAM_DATACHECK", 397 "CPX_PARAM_REDUCE", "CPX_PARAM_PRELINEAR", "CPX_PARAM_LPMETHOD", "CPX_PARAM_QPMETHOD", 398 "CPX_PARAM_WORKDIR", "CPX_PARAM_WORKMEM", "CPX_PARAM_THREADS", "CPX_PARAM_CONFLICTALG", 399 "CPX_PARAM_CONFLICTDISPLAY", "CPX_PARAM_SIFTDISPLAY", "CPX_PARAM_SIFTALG", 400 "CPX_PARAM_SIFTITLIM", "CPX_PARAM_MPSLONGNUM", "CPX_PARAM_MEMORYEMPHASIS", 401 "CPX_PARAM_NUMERICALEMPHASIS", "CPX_PARAM_FEASOPTMODE", "CPX_PARAM_PARALLELMODE", 402 "CPX_PARAM_TUNINGMEASURE", "CPX_PARAM_TUNINGREPEAT", "CPX_PARAM_TUNINGTILIM", 403 "CPX_PARAM_TUNINGDISPLAY", "CPX_PARAM_WRITELEVEL", "CPX_PARAM_RANDOMSEED", 404 "CPX_PARAM_DETTILIM", "CPX_PARAM_FILEENCODING", "CPX_PARAM_APIENCODING", 405 "CPX_PARAM_OPTIMALITYTARGET", "CPX_PARAM_CLONELOG", "CPX_PARAM_TUNINGDETTILIM", 406 "CPX_PARAM_CPUMASK", "CPX_PARAM_SOLUTIONTYPE", "CPX_PARAM_WARNLIM", "CPX_PARAM_SIFTSIM", 407 "CPX_PARAM_DYNAMICROWS", "CPX_PARAM_RECORD", "CPX_PARAM_PARAMDISPLAY", "CPX_PARAM_FOLDING", 408 "CPX_PARAM_WORKERALG", "CPX_PARAM_BENDERSSTRATEGY", "CPX_PARAM_BENDERSFEASCUTTOL", 409 "CPX_PARAM_BENDERSOPTCUTTOL", "CPX_PARAM_MULTIOBJDISPLAY", "CPX_PARAM_BRDIR", 410 "CPX_PARAM_BTTOL", "CPX_PARAM_CLIQUES", "CPX_PARAM_COEREDIND", "CPX_PARAM_COVERS", 411 "CPX_PARAM_CUTLO", "CPX_PARAM_CUTUP", "CPX_PARAM_EPAGAP", "CPX_PARAM_EPGAP", 412 "CPX_PARAM_EPINT", "CPX_PARAM_MIPDISPLAY", "CPX_PARAM_MIPINTERVAL", "CPX_PARAM_INTSOLLIM", 413 "CPX_PARAM_NODEFILEIND", "CPX_PARAM_NODELIM", "CPX_PARAM_NODESEL", "CPX_PARAM_OBJDIF", 414 "CPX_PARAM_MIPORDIND", "CPX_PARAM_RELOBJDIF", "CPX_PARAM_STARTALG", "CPX_PARAM_SUBALG", 415 "CPX_PARAM_TRELIM", "CPX_PARAM_VARSEL", "CPX_PARAM_BNDSTRENIND", "CPX_PARAM_HEURFREQ", 416 "CPX_PARAM_MIPORDTYPE", "CPX_PARAM_CUTSFACTOR", "CPX_PARAM_RELAXPREIND", "CPX_PARAM_PRESLVND", 417 "CPX_PARAM_BBINTERVAL", "CPX_PARAM_FLOWCOVERS", "CPX_PARAM_IMPLBD", "CPX_PARAM_PROBE", 418 "CPX_PARAM_GUBCOVERS", "CPX_PARAM_STRONGCANDLIM", "CPX_PARAM_STRONGITLIM", 419 "CPX_PARAM_FRACCAND", "CPX_PARAM_FRACCUTS", "CPX_PARAM_FRACPASS", "CPX_PARAM_FLOWPATHS", 420 "CPX_PARAM_MIRCUTS", "CPX_PARAM_DISJCUTS", "CPX_PARAM_AGGCUTLIM", 421 // "CPX_PARAM_MIPCBREDLP", 422 "CPX_PARAM_CUTPASS", "CPX_PARAM_MIPEMPHASIS", "CPX_PARAM_SYMMETRY", "CPX_PARAM_DIVETYPE", 423 "CPX_PARAM_RINSHEUR", "CPX_PARAM_LBHEUR", "CPX_PARAM_REPEATPRESOLVE", "CPX_PARAM_PROBETIME", 424 "CPX_PARAM_POLISHTIME", "CPX_PARAM_REPAIRTRIES", "CPX_PARAM_EPLIN", "CPX_PARAM_EPRELAX", 425 "CPX_PARAM_FPHEUR", "CPX_PARAM_EACHCUTLIM", "CPX_PARAM_SOLNPOOLCAPACITY", 426 "CPX_PARAM_SOLNPOOLREPLACE", "CPX_PARAM_SOLNPOOLGAP", "CPX_PARAM_SOLNPOOLAGAP", 427 "CPX_PARAM_SOLNPOOLINTENSITY", "CPX_PARAM_POPULATELIM", "CPX_PARAM_MIPSEARCH", 428 "CPX_PARAM_MIQCPSTRAT", "CPX_PARAM_ZEROHALFCUTS", "CPX_PARAM_HEUREFFORT", 429 "CPX_PARAM_POLISHAFTEREPAGAP", "CPX_PARAM_POLISHAFTEREPGAP", "CPX_PARAM_POLISHAFTERNODE", 430 "CPX_PARAM_POLISHAFTERINTSOL", "CPX_PARAM_POLISHAFTERTIME", "CPX_PARAM_MCFCUTS", 431 "CPX_PARAM_MIPKAPPASTATS", "CPX_PARAM_AUXROOTTHREADS", "CPX_PARAM_INTSOLFILEPREFIX", 432 "CPX_PARAM_PROBEDETTIME", "CPX_PARAM_POLISHAFTERDETTIME", "CPX_PARAM_LANDPCUTS", 433 "CPX_PARAM_RAMPUPDURATION", "CPX_PARAM_RAMPUPDETTILIM", "CPX_PARAM_RAMPUPTILIM", 434 "CPX_PARAM_LOCALIMPLBD", "CPX_PARAM_BQPCUTS", "CPX_PARAM_RLTCUTS", "CPX_PARAM_SUBMIPSTARTALG", 435 "CPX_PARAM_SUBMIPSUBALG", "CPX_PARAM_SUBMIPSCAIND", "CPX_PARAM_SUBMIPNODELIMIT", 436 "CPX_PARAM_BAREPCOMP", "CPX_PARAM_BARGROWTH", "CPX_PARAM_BAROBJRNG", "CPX_PARAM_BARALG", 437 "CPX_PARAM_BARCOLNZ", "CPX_PARAM_BARDISPLAY", "CPX_PARAM_BARITLIM", "CPX_PARAM_BARMAXCOR", 438 "CPX_PARAM_BARORDER", "CPX_PARAM_BARSTARTALG", "CPX_PARAM_BARCROSSALG", 439 "CPX_PARAM_BARQCPEPCOMP", "CPX_PARAM_QPNZREADLIM", "CPX_PARAM_CALCQCPDUALS", 440 "CPX_PARAM_QPMAKEPSDIND", "CPX_PARAM_QTOLININD", "CPX_PARAM_NETITLIM", "CPX_PARAM_NETEPOPT", 441 "CPX_PARAM_NETEPRHS", "CPX_PARAM_NETPPRIIND", "CPX_PARAM_NETDISPLAY"}; 442 int status; 443 Options def_options; 444 try { 445 MIPCplexWrapper mcw(factoryOpt, &def_options); 446 CPXENVptr env = mcw.dll_CPXopenCPLEX(&status); 447 if (env == nullptr) { 448 return {}; 449 } 450 std::vector<MiniZinc::SolverConfig::ExtraFlag> res; 451 for (auto& param : all_params) { 452 int num; 453 int type; 454 if (mcw.dll_CPXgetparamnum(env, param.c_str(), &num) != 0) { 455 // Param not available 456 continue; 457 } 458 mcw.dll_CPXgetparamtype(env, num, &type); 459 MiniZinc::SolverConfig::ExtraFlag::FlagType param_type; 460 std::vector<std::string> param_range; 461 std::string param_default; 462 switch (type) { 463 case CPX_PARAMTYPE_INT: { 464 CPXINT def; 465 CPXINT min; 466 CPXINT max; 467 mcw.dll_CPXinfointparam(env, num, &def, &min, &max); 468 param_type = MiniZinc::SolverConfig::ExtraFlag::FlagType::T_INT; 469 param_range.push_back(std::to_string(min)); 470 param_range.push_back(std::to_string(max)); 471 param_default = std::to_string(def); 472 break; 473 } 474 case CPX_PARAMTYPE_LONG: { 475 CPXLONG def; 476 CPXLONG min; 477 CPXLONG max; 478 mcw.dll_CPXinfolongparam(env, num, &def, &min, &max); 479 param_type = MiniZinc::SolverConfig::ExtraFlag::FlagType::T_INT; 480 param_range.push_back(std::to_string(min)); 481 param_range.push_back(std::to_string(max)); 482 param_default = std::to_string(def); 483 break; 484 } 485 case CPX_PARAMTYPE_DOUBLE: { 486 double def; 487 double min; 488 double max; 489 mcw.dll_CPXinfodblparam(env, num, &def, &min, &max); 490 param_type = MiniZinc::SolverConfig::ExtraFlag::FlagType::T_FLOAT; 491 param_range.push_back(std::to_string(min)); 492 param_range.push_back(std::to_string(max)); 493 param_default = std::to_string(def); 494 break; 495 } 496 case CPX_PARAMTYPE_STRING: { 497 char def[CPX_STR_PARAM_MAX]; 498 mcw.dll_CPXinfostrparam(env, num, def); 499 param_type = MiniZinc::SolverConfig::ExtraFlag::FlagType::T_STRING; 500 param_default = std::string(def); 501 break; 502 } 503 default: 504 continue; 505 } 506 res.emplace_back("--cplex-" + param, param, param_type, param_range, param_default); 507 } 508 return res; 509 } catch (MiniZinc::InternalError&) { 510 return {}; 511 } 512 return {}; 513} 514 515void MIPCplexWrapper::Options::printHelp(ostream& os) { 516 os << "IBM ILOG CPLEX MIP wrapper options:" 517 << std::endl 518 // -s print statistics 519 // << " --readParam <file> read CPLEX parameters from file 520 // << "--writeParam <file> write CPLEX parameters to file 521 // << "--tuneParam instruct CPLEX to tune parameters instead of solving 522 << " --mipfocus <n>\n 1: feasibility, 2: optimality, 3: move bound (default is 0, " 523 "balanced)" 524 << std::endl 525 << " -i\n print intermediate solutions for optimization problems" << std::endl 526 << " -p <N>, --parallel <N>\n use N threads, default: 1" 527 << std::endl 528 // << " --nomippresolve disable MIP presolving NOT IMPL" << std::endl 529 << " --solver-time-limit <N>\n stop search after N milliseconds wall time" << std::endl 530 << " -n <N>, --num-solutions <N>\n" 531 " stop search after N solutions" 532 << std::endl 533 << " -r <N>, --random-seed <N>\n" 534 " random seed, integer" 535 << std::endl 536 << " --workmem <N>, --nodefilestart <N>\n" 537 " maximal RAM for working memory used before writing to node file, GB, default: 0.5" 538 << std::endl 539 << " --nodefiledir <path>\n" 540 " nodefile directory" 541 << std::endl 542 << " --writeModel <file>\n write model to <file> (.lp, .mps, .sav, ...)" << std::endl 543 << " --readParam <file>\n read CPLEX parameters from file" << std::endl 544 << " --writeParam <file>\n write CPLEX parameters to file" 545 << std::endl 546 // << " --tuneParam instruct CPLEX to tune parameters instead of solving NOT IMPL" 547 548 << " --absGap <n>\n absolute gap |primal-dual| to stop" << std::endl 549 << " --relGap <n>\n relative gap |primal-dual|/<solver-dep> to stop. Default 1e-8, set <0 " 550 "to use backend's default" 551 << std::endl 552 << " --intTol <n>\n integrality tolerance for a variable. Default 1e-8" << std::endl 553 << "\n --cplex-dll <file> or <basename>\n CPLEX DLL, or base name, such as cplex1280, " 554 "when using plugin. Default range tried: " 555 << cplex_dlls().front() << " .. " << cplex_dlls().back() 556 << std::endl 557 // << " --objDiff <n> objective function discretization. Default 1.0" << std::endl 558 559 << std::endl; 560} 561 562bool MIPCplexWrapper::FactoryOptions::processOption(int& i, std::vector<std::string>& argv, 563 const std::string& workingDir) { 564 MiniZinc::CLOParser cop(i, argv); 565 return cop.get("--cplex-dll", &cplexDll); 566} 567 568bool MIPCplexWrapper::Options::processOption(int& i, std::vector<std::string>& argv, 569 const std::string& workingDir) { 570 MiniZinc::CLOParser cop(i, argv); 571 std::string buffer; 572 if (cop.get("-i")) { 573 flagIntermediate = true; 574 } else if (string(argv[i]) == "-f") { // NOLINT: Allow repeated empty if 575 // std::cerr << " Flag -f: ignoring fixed strategy anyway." << std::endl; 576 } else if (cop.get("--mipfocus --mipFocus --MIPFocus --MIPfocus", 577 &nMIPFocus)) { // NOLINT: Allow repeated empty if 578 } else if (cop.get("--writeModel", &buffer)) { 579 sExportModel = MiniZinc::FileUtils::file_path(buffer, workingDir); 580 } else if (cop.get("-p --parallel", &nThreads)) { // NOLINT: Allow repeated empty if 581 } else if (cop.get("--solver-time-limit", &nTimeout)) { // NOLINT: Allow repeated empty if 582 } else if (cop.get("-n --num-solutions", &nSolLimit)) { // NOLINT: Allow repeated empty if 583 } else if (cop.get("-r --random-seed", &nSeed)) { // NOLINT: Allow repeated empty if 584 } else if (cop.get("--workmem --nodefilestart", 585 &nWorkMemLimit)) { // NOLINT: Allow repeated empty if 586 } else if (cop.get("--nodefiledir --NodefileDir", 587 &sNodefileDir)) { // NOLINT: Allow repeated empty if 588 } else if (cop.get("--readParam", &buffer)) { 589 sReadParams = MiniZinc::FileUtils::file_path(buffer, workingDir); 590 } else if (cop.get("--writeParam", &buffer)) { 591 sWriteParams = MiniZinc::FileUtils::file_path(buffer, workingDir); 592 } else if (cop.get("--absGap", &absGap)) { // NOLINT: Allow repeated empty if 593 } else if (cop.get("--relGap", &relGap)) { // NOLINT: Allow repeated empty if 594 } else if (cop.get("--intTol", &intTol)) { // NOLINT: Allow repeated empty if 595 // } else if ( cop.get( "--objDiff", &objDiff ) ) { 596 } else { 597 return false; 598 } 599 return true; 600} 601 602void MIPCplexWrapper::wrapAssert(bool cond, const string& msg, bool fTerm) { 603 if (!cond) { 604 strcpy(_cplexBuffer, "[NO ERROR STRING GIVEN]"); 605 dll_CPXgeterrorstring(_env, _status, _cplexBuffer); 606 string msgAll = (" MIPCplexWrapper runtime error: " + msg + " " + _cplexBuffer); 607 cerr << msgAll << endl; 608 if (fTerm) { 609 cerr << "TERMINATING." << endl; 610 throw runtime_error(msgAll); 611 } 612 } 613} 614 615void MIPCplexWrapper::openCPLEX() { 616 checkDLL(); 617 cbui.wrapper = this; 618 /// Cleanup first. 619 // cleanup(); 620 /* Initialize the CPLEX environment */ 621 _env = dll_CPXopenCPLEX(&_status); 622 /* If an error occurs, the status value indicates the reason for 623 failure. A call to CPXgeterrorstring will produce the text of 624 the error message. Note that CPXopenCPLEX produces no output, 625 so the only way to see the cause of the error is to use 626 CPXgeterrorstring. For other CPLEX routines, the errors will 627 be seen if the CPXPARAM_ScreenOutput indicator is set to CPX_ON. */ 628 wrapAssert(_env != nullptr, "Could not open CPLEX environment."); 629 /* Create the problem. */ 630 _lp = dll_CPXcreateprob(_env, &_status, "MIPCplexWrapper"); 631 /* A returned pointer of NULL may mean that not enough memory 632 was available or there was some other problem. In the case of 633 failure, an error message will have been written to the error 634 channel from inside CPLEX. In this example, the setting of 635 the parameter CPXPARAM_ScreenOutput causes the error message to 636 appear on stdout. */ 637 wrapAssert(_lp != nullptr, "Failed to create LP."); 638} 639 640void MIPCplexWrapper::closeCPLEX() { 641 /// Freeing the problem can be slow both in C and C++, see IBM forums. Skipping. 642 /* Free up the problem as allocated by CPXcreateprob, if necessary */ 643 // if ( lp != NULL ) { 644 // status = CPXfreeprob (env, &lp); 645 // cplex_wrapAssert ( !status, "CPXfreeprob failed." ); 646 // } 647 _lp = nullptr; 648 /* Free up the CPLEX environment, if necessary */ 649 if (_env != nullptr) { 650 _status = dll_CPXcloseCPLEX(&_env); 651 assert(_status == 0); // Assume CPLEX environment is closed correctly 652 } 653 /// and at last: 654// MIPWrapper::cleanup(); 655#ifdef CPLEX_PLUGIN 656// dll_close(cplex_dll); 657#endif 658} 659 660void MIPCplexWrapper::doAddVars(size_t n, double* obj, double* lb, double* ub, 661 MIPWrapper::VarType* vt, string* names) { 662 /// Convert var types: 663 vector<char> ctype(n); 664 vector<char*> pcNames(n); 665 for (size_t i = 0; i < n; ++i) { 666 pcNames[i] = (char*)names[i].c_str(); 667 switch (vt[i]) { 668 case REAL: 669 ctype[i] = CPX_CONTINUOUS; 670 break; 671 case INT: 672 ctype[i] = CPX_INTEGER; 673 break; 674 case BINARY: 675 ctype[i] = CPX_BINARY; 676 break; 677 default: 678 throw runtime_error(" MIPWrapper: unknown variable type"); 679 } 680 } 681 _status = dll_CPXnewcols(_env, _lp, n, obj, lb, ub, &ctype[0], &pcNames[0]); 682 wrapAssert(_status == 0, "Failed to declare variables."); 683} 684 685static char get_cplex_constr_cense(MIPWrapper::LinConType sense) { 686 switch (sense) { 687 case MIPWrapper::LQ: 688 return 'L'; 689 case MIPWrapper::EQ: 690 return 'E'; 691 case MIPWrapper::GQ: 692 return 'G'; 693 default: 694 throw runtime_error(" MIPCplexWrapper: unknown constraint type"); 695 } 696} 697 698void MIPCplexWrapper::addRow(int nnz, int* rmatind, double* rmatval, MIPWrapper::LinConType sense, 699 double rhs, int mask, const string& rowName) { 700 /// Convert var types: 701 char ssense = get_cplex_constr_cense(sense); 702 const int ccnt = 0; 703 const int rcnt = 1; 704 const int rmatbeg[] = {0}; 705 char* pRName = (char*)rowName.c_str(); 706 if ((MaskConsType_Normal & mask) != 0) { 707 _status = dll_CPXaddrows(_env, _lp, ccnt, rcnt, nnz, &rhs, &ssense, rmatbeg, rmatind, rmatval, 708 nullptr, &pRName); 709 wrapAssert(_status == 0, "Failed to add constraint."); 710 } 711 if ((MaskConsType_Usercut & mask) != 0) { 712 _status = 713 dll_CPXaddusercuts(_env, _lp, rcnt, nnz, &rhs, &ssense, rmatbeg, rmatind, rmatval, &pRName); 714 wrapAssert(_status == 0, "Failed to add usercut."); 715 } 716 if ((MaskConsType_Lazy & mask) != 0) { 717 _status = dll_CPXaddlazyconstraints(_env, _lp, rcnt, nnz, &rhs, &ssense, rmatbeg, rmatind, 718 rmatval, &pRName); 719 wrapAssert(_status == 0, "Failed to add lazy constraint."); 720 } 721} 722 723void MIPCplexWrapper::addIndicatorConstraint(int iBVar, int bVal, int nnz, int* rmatind, 724 double* rmatval, MIPWrapper::LinConType sense, 725 double rhs, const string& rowName) { 726 wrapAssert(0 <= bVal && 1 >= bVal, "mzn-cplex: addIndicatorConstraint: bVal not 0/1"); 727 char ssense = get_cplex_constr_cense(sense); 728 _status = dll_CPXaddindconstr(_env, _lp, iBVar, 1 - bVal, nnz, rhs, ssense, rmatind, rmatval, 729 rowName.c_str()); 730 wrapAssert(_status == 0, "Failed to add indicator constraint."); 731} 732 733bool MIPCplexWrapper::addWarmStart(const std::vector<VarId>& vars, 734 const std::vector<double>& vals) { 735 assert(vars.size() == vals.size()); 736 const char* sMSName = "MZNMS"; 737 int msindex = -1; 738 const int beg = 0; 739 /// Check if we already added a start 740 _status = dll_CPXgetmipstartindex(_env, _lp, sMSName, &msindex); 741 if (_status != 0) { // not existent 742 // status = dll_CPXaddmipstarts (env, lp, mcnt, nzcnt, beg, varindices, 743 // values, effortlevel, mipstartname); 744 _status = dll_CPXaddmipstarts(_env, _lp, 1, vars.size(), &beg, vars.data(), vals.data(), 745 nullptr, (char**)&sMSName); 746 wrapAssert(_status == 0, "Failed to add warm start."); 747 } else { 748 // status = dll_CPXchgmipstarts (env, lp, mcnt, mipstartindices, nzcnt, beg, varindices, values, 749 // effortlevel); 750 _status = dll_CPXchgmipstarts(_env, _lp, 1, &msindex, vars.size(), &beg, vars.data(), 751 vals.data(), nullptr); 752 wrapAssert(_status == 0, "Failed to extend warm start."); 753 } 754 return true; 755} 756 757void MIPCplexWrapper::setVarBounds(int iVar, double lb, double ub) { 758 wrapAssert(lb <= ub, "mzn-cplex: setVarBounds: lb>ub"); 759 char cl = 'L'; 760 char cu = 'U'; 761 _status = dll_CPXchgbds(_env, _lp, 1, &iVar, &cl, &lb); 762 wrapAssert(_status == 0, "Failed to set lower bound."); 763 _status = dll_CPXchgbds(_env, _lp, 1, &iVar, &cu, &ub); 764 wrapAssert(_status == 0, "Failed to set upper bound."); 765} 766 767void MIPCplexWrapper::setVarLB(int iVar, double lb) { 768 char cl = 'L'; 769 _status = dll_CPXchgbds(_env, _lp, 1, &iVar, &cl, &lb); 770 wrapAssert(_status == 0, "Failed to set lower bound."); 771} 772 773void MIPCplexWrapper::setVarUB(int iVar, double ub) { 774 char cu = 'U'; 775 _status = dll_CPXchgbds(_env, _lp, 1, &iVar, &cu, &ub); 776 wrapAssert(_status == 0, "Failed to set upper bound."); 777} 778 779/// SolutionCallback ------------------------------------------------------------------------ 780/// CPLEX ensures thread-safety 781static int CPXPUBLIC solcallback(CPXCENVptr env, void* cbdata, int wherefrom, void* cbhandle) { 782 int status = 0; 783 784 auto* info = (MIPWrapper::CBUserInfo*)cbhandle; 785 auto* cw = static_cast<MIPCplexWrapper*>(info->wrapper); 786 int hasincumbent = 0; 787 int newincumbent = 0; 788 double objVal; 789 790 status = cw->dll_CPXgetcallbackinfo(env, cbdata, wherefrom, CPX_CALLBACK_INFO_NODE_COUNT, 791 &info->pOutput->nNodes); 792 if (status != 0) { 793 goto TERMINATE; 794 } 795 796 status = cw->dll_CPXgetcallbackinfo(env, cbdata, wherefrom, CPX_CALLBACK_INFO_NODES_LEFT, 797 &info->pOutput->nOpenNodes); 798 if (status != 0) { 799 goto TERMINATE; 800 } 801 802 status = 803 cw->dll_CPXgetcallbackinfo(env, cbdata, wherefrom, CPX_CALLBACK_INFO_MIP_FEAS, &hasincumbent); 804 if (status != 0) { 805 goto TERMINATE; 806 } 807 808 if (hasincumbent != 0) { 809 status = 810 cw->dll_CPXgetcallbackinfo(env, cbdata, wherefrom, CPX_CALLBACK_INFO_BEST_INTEGER, &objVal); 811 if (status != 0) { 812 goto TERMINATE; 813 } 814 815 if (fabs(info->pOutput->objVal - objVal) > 1e-12 * (1.0 + fabs(objVal))) { 816 newincumbent = 1; 817 info->pOutput->objVal = objVal; 818 info->pOutput->status = MIPWrapper::SAT; 819 info->pOutput->statusName = "feasible from a callback"; 820 } 821 } 822 823 // if ( nodecnt >= info->lastlog + 100 || newincumbent ) { 824 // double walltime; 825 // double dettime; 826 827 status = cw->dll_CPXgetcallbackinfo(env, cbdata, wherefrom, CPX_CALLBACK_INFO_BEST_REMAINING, 828 &info->pOutput->bestBound); 829 // if ( status ) goto TERMINATE; 830 831 // status = dll_CPXgettime (env, &walltime); 832 // if ( status ) goto TERMINATE; 833 // 834 // status = dll_CPXgetdettime (env, &dettime); 835 // if ( status ) goto TERMINATE; 836 // 837 // } 838 839 if (newincumbent != 0) { 840 assert(info->pOutput->x); 841 status = cw->dll_CPXgetcallbackincumbent(env, cbdata, wherefrom, (double*)info->pOutput->x, 0, 842 info->pOutput->nCols - 1); 843 if (status != 0) { 844 goto TERMINATE; 845 } 846 847 info->pOutput->dWallTime = 848 std::chrono::duration<double>(std::chrono::steady_clock::now() - info->pOutput->dWallTime0) 849 .count(); 850 info->pOutput->dCPUTime = double(std::clock() - info->pOutput->cCPUTime0) / CLOCKS_PER_SEC; 851 852 /// Call the user function: 853 if (info->solcbfn != nullptr) { 854 (*info->solcbfn)(*info->pOutput, info->psi); 855 } 856 info->printed = true; 857 } 858 859TERMINATE: 860 return (status); 861 862} /* END logcallback */ 863// end SolutionCallback --------------------------------------------------------------------- 864 865/// Cut callbacks, mostly copied from admipex5.c, CPLEX 12.6.3 866/* The following macro defines the smallest improvement 867 on the value of the objective function that is required 868 when adding user cuts from within a callback. 869 If the improvement on the value of the ojective function 870 is not large enough, the callback will abort the cut loop. */ 871 872#define EPSOBJ 0.1 873 874/* The following structure will hold the information we need to 875 pass to the cut callback function */ 876 877struct CutInfo { 878 CPXLPptr lp; 879 int numcols; 880 int num; 881 double* x; 882 int* beg; 883 int* ind; 884 double* val; 885 double* rhs; 886 int nodeid; 887 double nodeobjval; 888 int objsen; 889 MIPWrapper::CBUserInfo* info = nullptr; 890}; 891typedef struct CutInfo CUTINFO, *CUTINFOptr; 892 893/* Init information on the node objval for the user cut callback */ 894 895static void initnodeobjvalinfo(MIPCplexWrapper* cw, CPXENVptr env, CPXLPptr lp, 896 CUTINFOptr cutinfo) { 897 cutinfo->nodeid = -1; 898 cutinfo->nodeobjval = 0.0; 899 cutinfo->objsen = cw->dll_CPXgetobjsen(env, lp); 900 if (cutinfo->objsen == CPX_MIN) { 901 cutinfo->objsen = 1; 902 } else { 903 cutinfo->objsen = -1; 904 } 905} /* END initnodeobjvalinfo */ 906 907static int CPXPUBLIC myusercutcallback(CPXCENVptr env, void* cbdata, int wherefrom, void* cbhandle, 908 int* useraction_p) { 909 int status = 0; 910 911 auto* cutinfo = (CUTINFOptr)cbhandle; 912 913 // int numcols = cutinfo->numcols; 914 // int numcuts = cutinfo->num; 915 // double *x = cutinfo->x; 916 // int *beg = cutinfo->beg; 917 // int *ind = cutinfo->ind; 918 // double *val = cutinfo->val; 919 // double *rhs = cutinfo->rhs; 920 // int *cutind = NULL; 921 // double *cutval = NULL; 922 // double cutvio; 923 int addedcuts = 0; 924 // int i, j, k; //, cutnz; 925 MIPWrapper::CBUserInfo* info = cutinfo->info; 926 auto* cw = static_cast<MIPCplexWrapper*>(info->wrapper); 927 // double *x = info->pCutOutput->x; 928 929 *useraction_p = CPX_CALLBACK_DEFAULT; 930 931 /* If we are called as a user cut callback, decide 932 first if we want to add cuts or abort the cut loop. 933 When adding user cuts with purgeable flag set to 934 CPX_USECUT_PURGE or CPX_USECUT_FILTER, it is important 935 to avoid the possibility of an infinite cut loop, where 936 the same cuts are added to the LP and then immediately 937 purged at every cut pass. Such a situation can be avoided, 938 for instance, by applying a tailing off criterion and aborting 939 the cut loop where no progress in the objval is observed. 940 Note, however, that the same approach must not be applied 941 with lazy constraints. In this case, if lazy constraints are 942 added with purgeable flag set to CPX_USECUT_PURGE, adding 943 the same lazy constraint more than once could be required 944 to ensure the correctness of the final result. */ 945 946 bool fMIPSol = true; 947 if (wherefrom == CPX_CALLBACK_MIP_CUT_LOOP || wherefrom == CPX_CALLBACK_MIP_CUT_LAST) { 948 int oldnodeid = cutinfo->nodeid; 949 double oldnodeobjval = cutinfo->nodeobjval; 950 951 fMIPSol = false; 952 953 /* Retrieve nodeid and node objval of the current node */ 954 955 status = cw->dll_CPXgetcallbacknodeinfo(env, cbdata, wherefrom, 0, 956 CPX_CALLBACK_INFO_NODE_SEQNUM, &cutinfo->nodeid); 957 if (status != 0) { 958 fprintf(stderr, "Failed to get node id.\n"); 959 goto TERMINATE; 960 } 961 962 status = cw->dll_CPXgetcallbacknodeinfo(env, cbdata, wherefrom, 0, 963 CPX_CALLBACK_INFO_NODE_OBJVAL, &cutinfo->nodeobjval); 964 if (status != 0) { 965 fprintf(stderr, "Failed to get node objval.\n"); 966 goto TERMINATE; 967 } 968 969 /* Abort the cut loop if we are stuck at the same node 970 as before and there is no progress in the node objval */ 971 972 if (oldnodeid == cutinfo->nodeid) { 973 double objchg = (cutinfo->nodeobjval - oldnodeobjval); 974 /* Multiply objchg by objsen to normalize 975 the change in the objective function to 976 the case of a minimization problem */ 977 objchg *= cutinfo->objsen; 978 if (objchg <= EPSOBJ) { 979 *useraction_p = CPX_CALLBACK_ABORT_CUT_LOOP; 980 goto TERMINATE; 981 } 982 } 983 } 984 985 /* If we reached this point, we are 986 .. in a lazyconstraint callback, or 987 .. in a user cut callback, and cuts seem to help 988 improving the node objval. 989 In both cases, we retrieve the x solution and 990 look for violated cuts. */ 991 992 if (info->cutcbfn != nullptr) { // if cut handler given 993 MIPWrapper::Output outpRlx; 994 outpRlx.x = info->pOutput->x; // using the sol output storage TODO? 995 outpRlx.nCols = info->pOutput->nCols; 996 assert(outpRlx.x && outpRlx.nCols); 997 status = cw->dll_CPXgetcallbacknodex(env, cbdata, wherefrom, (double*)outpRlx.x, 0, 998 outpRlx.nCols - 1); 999 if (status != 0) { 1000 fprintf(stderr, "Cut callback: failed to get node solution.\n"); 1001 goto TERMINATE; 1002 } 1003 MIPWrapper::CutInput cutInput; 1004 info->cutcbfn(outpRlx, cutInput, info->psi, fMIPSol); 1005 static int nCuts = 0; 1006 nCuts += cutInput.size(); 1007 // if ( cutInput.size() ) 1008 // cerr << "\n N CUTS: " << nCuts << endl; 1009 for (auto& cd : cutInput) { 1010 if ((cd.mask & (MIPWrapper::MaskConsType_Usercut | MIPWrapper::MaskConsType_Lazy)) == 0) { 1011 throw runtime_error("Cut callback: should be user/lazy"); 1012 } 1013 /* Use a cut violation tolerance of 0.01 */ 1014 if (true) { // NOLINT: cutvio > 0.01 ) { 1015 status = cw->dll_CPXcutcallbackadd(env, cbdata, wherefrom, cd.rmatind.size(), cd.rhs, 1016 get_cplex_constr_cense(cd.sense), cd.rmatind.data(), 1017 cd.rmatval.data(), 1018 CPX_USECUT_FORCE); // PURGE? 1019 if (status != 0) { 1020 fprintf(stderr, "CPLEX callback: failed to add cut.\n"); 1021 goto TERMINATE; 1022 } 1023 addedcuts++; 1024 } 1025 } 1026 } 1027 1028 /* Tell CPLEX that cuts have been created */ 1029 if (addedcuts > 0) { 1030 *useraction_p = CPX_CALLBACK_SET; 1031 } 1032 1033TERMINATE: 1034 1035 return (status); 1036 1037} /* END myusercutcallback */ 1038 1039// ----------------- END Cut callbacks ------------------ 1040 1041MIPCplexWrapper::Status MIPCplexWrapper::convertStatus(int cplexStatus) { 1042 Status s = Status::UNKNOWN; 1043 /* Converting the status. */ 1044 switch (cplexStatus) { 1045 case CPXMIP_OPTIMAL: 1046 s = Status::OPT; 1047 wrapAssert(dll_CPXgetsolnpoolnumsolns(_env, _lp) != 0, "Optimality reported but pool empty?", 1048 false); 1049 break; 1050 case CPXMIP_INFEASIBLE: 1051 s = Status::UNSAT; 1052 break; 1053 // case CPXMIP_OPTIMAL_INFEAS: 1054 case CPXMIP_INForUNBD: 1055 s = Status::UNSATorUNBND; 1056 break; 1057 case CPXMIP_SOL_LIM: 1058 case CPXMIP_NODE_LIM_FEAS: 1059 case CPXMIP_TIME_LIM_FEAS: 1060 case CPXMIP_FAIL_FEAS: 1061 case CPXMIP_MEM_LIM_FEAS: 1062 case CPXMIP_ABORT_FEAS: 1063 case CPXMIP_FAIL_FEAS_NO_TREE: 1064 s = Status::SAT; 1065 wrapAssert(dll_CPXgetsolnpoolnumsolns(_env, _lp) != 0, "Feasibility reported but pool empty?", 1066 false); 1067 break; 1068 case CPXMIP_UNBOUNDED: 1069 s = Status::UNBND; 1070 break; 1071 // case CPXMIP_ABORT_INFEAS: 1072 case CPXMIP_FAIL_INFEAS: 1073 s = Status::__ERROR; 1074 break; 1075 default: 1076 // case CPXMIP_OPTIMAL_TOL: 1077 // case CPXMIP_ABORT_RELAXATION_UNBOUNDED: 1078 if (dll_CPXgetsolnpoolnumsolns(_env, _lp) != 0) { 1079 s = Status::SAT; 1080 } else { 1081 s = Status::UNKNOWN; 1082 } 1083 } 1084 return s; 1085} 1086 1087void msgfunction(void* handle, const char* msg_string) { cerr << msg_string << flush; } 1088 1089void MIPCplexWrapper::solve() { // Move into ancestor? 1090 1091 /////////////// Last-minute solver options ////////////////// 1092 // Before all manual params ??? 1093 if (!_options->sReadParams.empty()) { 1094 _status = dll_CPXreadcopyparam(_env, _options->sReadParams.c_str()); 1095 wrapAssert(_status == 0, "Failed to read CPLEX parameters.", false); 1096 } 1097 1098 /* Turn on output to the screen */ 1099 if (fVerbose) { 1100 CPXCHANNELptr chnl[4]; 1101 dll_CPXgetchannels(_env, &chnl[0], &chnl[1], &chnl[2], &chnl[3]); 1102 for (int i = 0; i < 3; ++i) { 1103 _status = dll_CPXaddfuncdest(_env, chnl[i], nullptr, msgfunction); 1104 } 1105 // status = dll_CPXsetintparam(env, CPXPARAM_ScreenOutput, 1106 // fVerbose ? CPX_ON : CPX_OFF); // also when flagIntermediate? TODO 1107 // wrapAssert(!status, " CPLEX Warning: Failure to switch screen indicator.", false); 1108 } 1109 _status = dll_CPXsetintparam(_env, CPXPARAM_MIP_Display, 1110 fVerbose ? 2 : 0); // also when flagIntermediate? TODO 1111 wrapAssert(_status == 0, " CPLEX Warning: Failure to switch logging.", false); 1112 /// Make it wall time by default, 12.8 1113 // status = dll_CPXsetintparam (env, CPXPARAM_ClockType, 1); // CPU time 1114 // wrapAssert(!status, " CPLEX Warning: Failure to measure CPU time.", false); 1115 _status = dll_CPXsetintparam(_env, CPX_PARAM_MIPCBREDLP, CPX_OFF); // Access original model 1116 wrapAssert(_status == 0, " CPLEX Warning: Failure to set access original model in callbacks.", 1117 false); 1118 if (!_options->sExportModel.empty()) { 1119 _status = dll_CPXwriteprob(_env, _lp, _options->sExportModel.c_str(), nullptr); 1120 wrapAssert(_status == 0, "Failed to write LP to disk.", false); 1121 } 1122 1123 /// TODO 1124 // if(all_solutions && obj.getImpl()) { 1125 // IloNum lastObjVal = (obj.getSense() == IloObjective::Minimize ) ? 1126 // _ilocplex->use(SolutionCallback(_iloenv, lastObjVal, *this)); 1127 // Turn off CPLEX logging 1128 1129 if (_options->nThreads > 0) { 1130 _status = dll_CPXsetintparam(_env, CPXPARAM_Threads, _options->nThreads); 1131 wrapAssert(_status == 0, "Failed to set CPXPARAM_Threads.", false); 1132 } 1133 1134 if (_options->nTimeout > 0) { 1135 _status = dll_CPXsetdblparam(_env, CPXPARAM_TimeLimit, 1136 static_cast<double>(_options->nTimeout) / 1000.0); 1137 wrapAssert(_status == 0, "Failed to set CPXPARAM_TimeLimit.", false); 1138 } 1139 if (_options->nSolLimit > 0) { 1140 _status = dll_CPXsetintparam(_env, CPXPARAM_MIP_Limits_Solutions, _options->nSolLimit); 1141 wrapAssert(_status == 0, "Failed to set CPXPARAM_MIP_Limits_Solutions.", false); 1142 } 1143 if (_options->nSeed >= 0) { 1144 _status = dll_CPXsetintparam(_env, CPXPARAM_RandomSeed, _options->nSeed); 1145 wrapAssert(_status == 0, "Failed to set CPXPARAM_RandomSeed.", false); 1146 } 1147 if (_options->nMIPFocus > 0) { 1148 _status = dll_CPXsetintparam(_env, CPXPARAM_Emphasis_MIP, _options->nMIPFocus); 1149 wrapAssert(_status == 0, "Failed to set CPXPARAM_Emphasis_MIP.", false); 1150 } 1151 1152 if (_options->nWorkMemLimit > 0) { 1153 _status = dll_CPXsetintparam(_env, CPXPARAM_MIP_Strategy_File, 3); 1154 wrapAssert(_status == 0, "Failed to set CPXPARAM_MIP_Strategy_File.", false); 1155 _status = dll_CPXsetdblparam(_env, CPXPARAM_WorkMem, 1156 1024.0 * _options->nWorkMemLimit); // MB in CPLEX 1157 wrapAssert(_status == 0, "Failed to set CPXPARAM_WorkMem.", false); 1158 } 1159 1160 if (!_options->sNodefileDir.empty()) { 1161 _status = dll_CPXsetstrparam(_env, CPXPARAM_WorkDir, _options->sNodefileDir.c_str()); 1162 wrapAssert(_status == 0, "Failed to set CPXPARAM_WorkDir.", false); 1163 } 1164 1165 if (_options->absGap >= 0.0) { 1166 _status = dll_CPXsetdblparam(_env, CPXPARAM_MIP_Tolerances_AbsMIPGap, _options->absGap); 1167 wrapAssert(_status == 0, "Failed to set CPXPARAM_MIP_Tolerances_AbsMIPGap.", false); 1168 } 1169 if (_options->relGap >= 0.0) { 1170 _status = dll_CPXsetdblparam(_env, CPXPARAM_MIP_Tolerances_MIPGap, _options->relGap); 1171 wrapAssert(_status == 0, "Failed to set CPXPARAM_MIP_Tolerances_MIPGap.", false); 1172 } 1173 if (_options->intTol >= 0.0) { 1174 _status = dll_CPXsetdblparam(_env, CPXPARAM_MIP_Tolerances_Integrality, _options->intTol); 1175 wrapAssert(_status == 0, "Failed to set CPXPARAM_MIP_Tolerances_Integrality.", false); 1176 } 1177 1178 // status = dll_CPXsetdblparam (_env, CPXPARAM_MIP_Tolerances_ObjDifference, objDiff); 1179 // wrapAssert(!status, "Failed to set CPXPARAM_MIP_Tolerances_ObjDifference.", false); 1180 1181 /// Solution callback 1182 output.nCols = colObj.size(); 1183 _x.resize(output.nCols); 1184 output.x = &_x[0]; 1185 if (_options->flagIntermediate && (cbui.solcbfn != nullptr)) { 1186 _status = dll_CPXsetinfocallbackfunc(_env, solcallback, &cbui); 1187 wrapAssert(_status == 0, "Failed to set solution callback", false); 1188 } 1189 if (cbui.cutcbfn != nullptr) { 1190 assert(cbui.cutMask & (MaskConsType_Usercut | MaskConsType_Lazy)); 1191 if ((cbui.cutMask & MaskConsType_Usercut) != 0) { 1192 // For user cuts, needs to keep some info after presolve 1193 if (fVerbose) { 1194 cerr << " MIPCplexWrapper: user cut callback enabled, setting params" << endl; 1195 } 1196 CUTINFO usercutinfo; // THREADS? TODO 1197 usercutinfo.info = &cbui; 1198 /* Init information on the node objval for the user cut callback */ 1199 initnodeobjvalinfo(this, _env, _lp, &usercutinfo); 1200 /* Assure linear mappings between the presolved and original 1201 models */ 1202 _status = dll_CPXsetintparam(_env, CPXPARAM_Preprocessing_Linear, 0); 1203 wrapAssert(_status == 0, "CPLEX: setting prepro_linear"); 1204 /* Turn on traditional search for use with control callbacks */ 1205 _status = dll_CPXsetintparam(_env, CPXPARAM_MIP_Strategy_Search, CPX_MIPSEARCH_TRADITIONAL); 1206 wrapAssert(_status == 0, "CPLEX: setting traditional search"); 1207 /* Let MIP callbacks work on the original model */ 1208 _status = dll_CPXsetintparam(_env, CPXPARAM_MIP_Strategy_CallbackReducedLP, CPX_OFF); 1209 wrapAssert(_status == 0, "CPLEX: setting callbacks to work on orig model"); 1210 /// And 1211 /* Set up to use MIP usercut callback */ 1212 1213 _status = dll_CPXsetusercutcallbackfunc(_env, myusercutcallback, &usercutinfo); 1214 wrapAssert(_status == 0, "CPLEX: setting user cut callback"); 1215 } 1216 if ((cbui.cutMask & MaskConsType_Lazy) != 0) { 1217 if (fVerbose) { 1218 cerr << " MIPCplexWrapper: lazy cut callback enabled, setting params" << endl; 1219 } 1220 CUTINFO lazyconinfo; 1221 lazyconinfo.info = &cbui; 1222 /* Init information on the node objval for the user cut callback. 1223 No need to initialize the information on the node objval, 1224 for the lazy constraint callback, because those information are 1225 used only in the user cut callback. */ 1226 initnodeobjvalinfo(this, _env, _lp, &lazyconinfo); 1227 /* Assure linear mappings between the presolved and original 1228 models */ 1229 _status = dll_CPXsetintparam(_env, CPXPARAM_Preprocessing_Linear, 0); 1230 wrapAssert(_status == 0, "CPLEX: setting prepro_linear"); 1231 /* Turn on traditional search for use with control callbacks */ 1232 // _status = dll_CPXsetintparam (_env, CPXPARAM_MIP_Strategy_Search, 1233 // CPX_MIPSEARCH_TRADITIONAL); 1234 // wrapAssert ( !_status, "CPLEX: setting traditional search" ); 1235 /* Let MIP callbacks work on the original model */ 1236 _status = dll_CPXsetintparam(_env, CPXPARAM_MIP_Strategy_CallbackReducedLP, CPX_OFF); 1237 wrapAssert(_status == 0, "CPLEX: setting callbacks to work on orig model"); 1238 /* Set up to use MIP lazyconstraint callback. The callback funtion 1239 * registered is the same, but the data will be different. */ 1240 1241 _status = dll_CPXsetlazyconstraintcallbackfunc(_env, myusercutcallback, &lazyconinfo); 1242 wrapAssert(_status == 0, "CPLEX: setting lazy cut callback"); 1243 } 1244 } 1245 1246 // Process extra flags 1247 for (auto& it : _options->extraParams) { 1248 int param; 1249 int param_type; 1250 auto name = it.first.substr(8); 1251 _status = dll_CPXgetparamnum(_env, name.c_str(), &param); 1252 wrapAssert(_status == 0, "CPLEX: could not find parameter " + name); 1253 _status = dll_CPXgetparamtype(_env, param, &param_type); 1254 wrapAssert(_status == 0, "CPLEX: could not get type for parameter " + name); 1255 switch (param_type) { 1256 case CPX_PARAMTYPE_INT: 1257 _status = dll_CPXsetintparam(_env, param, stoi(it.second)); 1258 wrapAssert(_status == 0, "CPLEX: failed to set parameter " + name); 1259 break; 1260 case CPX_PARAMTYPE_LONG: 1261 _status = dll_CPXsetlongparam(_env, param, stoll(it.second)); 1262 wrapAssert(_status == 0, "CPLEX: failed to set parameter " + name); 1263 break; 1264 case CPX_PARAMTYPE_DOUBLE: 1265 _status = dll_CPXsetdblparam(_env, param, stod(it.second)); 1266 wrapAssert(_status == 0, "CPLEX: failed to set parameter " + name); 1267 break; 1268 case CPX_PARAMTYPE_STRING: 1269 _status = dll_CPXsetstrparam(_env, param, it.second.c_str()); 1270 wrapAssert(_status == 0, "CPLEX: failed to set parameter " + name); 1271 break; 1272 default: 1273 wrapAssert(false, "CPLEX: unknown type for parameter " + name); 1274 break; 1275 } 1276 } 1277 1278 /// after all modifs 1279 if (!_options->sWriteParams.empty()) { 1280 _status = dll_CPXwriteparam(_env, _options->sWriteParams.c_str()); 1281 wrapAssert(_status == 0, "Failed to write CPLEX parameters.", false); 1282 } 1283 1284 // _status = dll_CPXgettime (_env, &output.dCPUTime); 1285 // wrapAssert(!_status, "Failed to get time stamp.", false); 1286 cbui.pOutput->dWallTime0 = output.dWallTime0 = std::chrono::steady_clock::now(); 1287 cbui.pOutput->cCPUTime0 = std::clock(); 1288 1289 /* Optimize the problem and obtain solution. */ 1290 _status = dll_CPXmipopt(_env, _lp); 1291 wrapAssert(_status == 0, "Failed to optimize MIP."); 1292 1293 output.dWallTime = 1294 std::chrono::duration<double>(std::chrono::steady_clock::now() - output.dWallTime0).count(); 1295 double tmNow = std::clock(); 1296 // _status = dll_CPXgettime (_env, &tmNow); Buggy in 12.7.1.0 1297 wrapAssert(_status == 0, "Failed to get time stamp.", false); 1298 output.dCPUTime = (tmNow - cbui.pOutput->cCPUTime0) / CLOCKS_PER_SEC; 1299 1300 int solstat = dll_CPXgetstat(_env, _lp); 1301 output.status = convertStatus(solstat); 1302 output.statusName = dll_CPXgetstatstring(_env, solstat, _cplexStatusBuffer); 1303 1304 /// Continuing to fill the output object: 1305 if (Status::OPT == output.status || Status::SAT == output.status) { 1306 _status = dll_CPXgetobjval(_env, _lp, &output.objVal); 1307 wrapAssert(_status == 0, "No MIP objective value available."); 1308 1309 /* The size of the problem should be obtained by asking CPLEX what 1310 the actual size is, rather than using what was passed to CPXcopylp. 1311 cur_numrows and cur_numcols store the current number of rows and 1312 columns, respectively. */ // ?????????????? TODO 1313 1314 // int cur_numrows = dll_CPXgetnumrows (_env, lp); 1315 int cur_numcols = dll_CPXgetnumcols(_env, _lp); 1316 assert(cur_numcols == colObj.size()); 1317 1318 _x.resize(cur_numcols); 1319 output.x = &_x[0]; 1320 _status = dll_CPXgetx(_env, _lp, &_x[0], 0, cur_numcols - 1); 1321 wrapAssert(_status == 0, "Failed to get variable values."); 1322 if (cbui.solcbfn != nullptr /*&& (!_options->flagIntermediate || !cbui.printed)*/) { 1323 cbui.solcbfn(output, cbui.psi); 1324 } 1325 } 1326 output.bestBound = 1e308; 1327 _status = dll_CPXgetbestobjval(_env, _lp, &output.bestBound); 1328 wrapAssert(_status == 0, "Failed to get the best bound.", false); 1329 output.nNodes = dll_CPXgetnodecnt(_env, _lp); 1330 output.nOpenNodes = dll_CPXgetnodeleftcnt(_env, _lp); 1331} 1332 1333void MIPCplexWrapper::setObjSense(int s) { 1334 _status = dll_CPXchgobjsen(_env, _lp, -s); // +1 for min in CPLEX 1335 wrapAssert(_status == 0, "Failed to set obj sense."); 1336}