diff --git a/solvers/CMakeLists.txt b/solvers/CMakeLists.txt index 59f135c42..82dbdb2c7 100644 --- a/solvers/CMakeLists.txt +++ b/solvers/CMakeLists.txt @@ -268,7 +268,7 @@ add_ampl_backend(highsmp DLL_RUNTIME SHARED_LIB MODULE HIGHS add_ampl_backend(ortoolsmp DLL_RUNTIME SHARED_LIB MODULE ortoolsmp LIBRARIES ${ortoolsmp_LIBS} ${CMAKE_DL_LIBS}) -add_ampl_backend(cplexmp DLL_RUNTIME SHARED_LIB MODULE CPLEX +add_ampl_backend(cplex DLL_RUNTIME SHARED_LIB MODULE CPLEX LIBRARIES cplex-library ${CMAKE_DL_LIBS}) add_ampl_backend(xpress DLL_RUNTIME SHARED_LIB MODULE XPRESS @@ -359,10 +359,10 @@ endif() -set(CPLEXSRC cplexmpbackend.cc cplexmpbackend.h - cplexmpcommon.cc cplexmpcommon.h - cplexmpmodelapi.cc cplexmpmodelapi.h - cplexmp-modelapi-connect.cc +set(CPLEXSRC cplexbackend.cc cplexbackend.h + cplexcommon.cc cplexcommon.h + cplexmodelapi.cc cplexmodelapi.h + cplex-modelapi-connect.cc model-mgr-with-std-pb.cc) add_prefix(DIRCPLEXSRC "../cplexmp/" ${CPLEXSRC}) set(CPLEXODHSRC cplexodhbackend.h cplexodhbackend.cc) diff --git a/solvers/cplexmp/CHANGES.cplexmp.md b/solvers/cplex/CHANGES.cplex.md similarity index 93% rename from solvers/cplexmp/CHANGES.cplexmp.md rename to solvers/cplex/CHANGES.cplex.md index ab9951f4a..18252f4a1 100644 --- a/solvers/cplexmp/CHANGES.cplexmp.md +++ b/solvers/cplex/CHANGES.cplex.md @@ -1,8 +1,10 @@ -Summary of recent updates to CPLEX MP for AMPL +Summary of recent updates to CPLEX for AMPL ============================================== -## TBD -- Added multiple options +## 20240823 +- CPLEX MP driver is now the default. To use the previous ASL-based driver set: + `option solver cplexasl;` +- Added many options available in the ASL driver - Notable changes with ASL driver: - Keyword 'basis_cond' is now 'kappa' and follows the standard MP implementation - Multiobjective optimization follows the MP standard implementation @@ -14,6 +16,7 @@ Summary of recent updates to CPLEX MP for AMPL `tech:numcores`. - Use MP feature `tech:writemodelonly` instead of `writeprob` + `nosolve`. Note that `nosolve` still applies when specifying `writemipstart` +- Conversion of basis status for constraints depending on sense ## 20240801 - Fix a problem that occured when reporting IIS diff --git a/solvers/cplex/README.cplex.txt b/solvers/cplex/README.cplex.txt new file mode 100644 index 000000000..d83a3a23a --- /dev/null +++ b/solvers/cplex/README.cplex.txt @@ -0,0 +1,62 @@ +The solver "cplex" uses CPLEX (a trademark of IBM, +Inc.; see https://www.ibm.com/products/ilog-cplex-optimization-studio) +to solve integer, mixed-integer, and linear programming problems; +it is an alternative to the ASL-based driver implemented using the mp +library (https://github.com/ampl/mp) for communicating with AMPL and +for reformlation of certain types of problems. +Normally gurobi is invoked by AMPL's solve command, which gives the +invocation + + cplex stub -AMPL + +in which stub.nl is an AMPL generic output file (possibly written +by "ampl -obstub" or "ampl -ogstub"). After solving the problem, +cplex writes a stub.sol file for use by ampl's solve and solution +commands. When you run ampl, this all happens automatically if you +give the AMPL commands + + option solver cplex; + solve; + +You can control cplex by setting the environment variable cplex_options +appropriately (either by using ampl's option command, or by using the +shell's set and export commands before you invoke ampl). You can put +one or more (white-space separated) phrases in $cplex_options. To see +the possibilities, invoke + + cplex -= + +---------- +INSTALLING +========== + +On Linux systems, libcplex*.so (where the value of "*" depends +on the current version of CPLEX) and the libcplex.so.* to which +it points need to appear in the current directory when CPLEX +itself appears there, or in one of the standard places (specified by +/etc/ld.so.conf on some systems), or in a directory named in +$LD_LIBRARY_PATH. An alternative is to add a short shell script, +such as + + #!/bin/sh + LD_LIBRARY_PATH=/usr/local/lib + export LD_LIBRARY_PATH + exec /usr/local/bin/cplexx "$@" + +to a directory in your usual $PATH (and mark the script executable +with, e.g., "chmod +x cplex"). The above script assumes that the +true "cplex" binary has been moved to /usr/local/bin/cplexx and that +the libcplex* files have been moved to /usr/local/lib. + +MacOSX systems are similar to Linux systems, but with DYLD_LIBRARY_PATH +in place of LD_LIBRARY_PATH. + +On MS Windows systems, cplex.exe and the relevant cplex*.dll must +appear somewhere in your usual search $PATH (or in the current +directory). + +If you have questions about or find bugs with this stuff, +please contact: + + AMPL Support + support@ampl.com diff --git a/solvers/cplexmp/cplexmp-ampls-c-api.h b/solvers/cplex/cplex-ampls-c-api.h similarity index 74% rename from solvers/cplexmp/cplexmp-ampls-c-api.h rename to solvers/cplex/cplex-ampls-c-api.h index 8e8075eb2..21be4fa0c 100644 --- a/solvers/cplexmp/cplexmp-ampls-c-api.h +++ b/solvers/cplex/cplex-ampls-c-api.h @@ -13,8 +13,8 @@ * Below are CPLEX-specific AMPLS API functions. * They complement the 'public' AMPLS API defined in ampls-c-api.h. */ -DECLARE_SOLVER_API_FUNCTIONS(cplexmp) +DECLARE_SOLVER_API_FUNCTIONS(cplex) -AMPLS_C_EXPORT void* AMPLSGetEnv_cplexmp(AMPLS_MP_Solver* slv); +AMPLS_C_EXPORT void* AMPLSGetEnv_cplex(AMPLS_MP_Solver* slv); #endif // CPLEXAMPLSCAPI_H diff --git a/solvers/cplex/cplex-lib.c b/solvers/cplex/cplex-lib.c new file mode 100644 index 000000000..057de77ec --- /dev/null +++ b/solvers/cplex/cplex-lib.c @@ -0,0 +1,7 @@ +#include "cplex/cplex-ampls-c-api.h" + +AMPLS_C_EXPORT AMPLS_MP_Solver* AMPLSOpen_cplex(int argc, char** argv) +{ + CCallbacks cb = { NULL }; + return Open_cplex(cb); +} \ No newline at end of file diff --git a/solvers/cplexmp/cplexmp-modelapi-connect.cc b/solvers/cplex/cplex-modelapi-connect.cc similarity index 93% rename from solvers/cplexmp/cplexmp-modelapi-connect.cc rename to solvers/cplex/cplex-modelapi-connect.cc index d3a79f186..4b7cb1997 100644 --- a/solvers/cplexmp/cplexmp-modelapi-connect.cc +++ b/solvers/cplex/cplex-modelapi-connect.cc @@ -1,7 +1,7 @@ #include "mp/flat/redef/MIP/converter_mip.h" #include "mp/flat/model_api_connect.h" -#include "cplexmpmodelapi.h" +#include "cplexmodelapi.h" namespace mp { diff --git a/solvers/cplexmp/cplexmpbackend.cc b/solvers/cplex/cplexbackend.cc similarity index 98% rename from solvers/cplexmp/cplexmpbackend.cc rename to solvers/cplex/cplexbackend.cc index 8e2a03590..c6ed16fc6 100644 --- a/solvers/cplexmp/cplexmpbackend.cc +++ b/solvers/cplex/cplexbackend.cc @@ -6,10 +6,10 @@ #include "mp/env.h" #include "mp/flat/model_api_base.h" -#include "cplexmpbackend.h" +#include "cplexbackend.h" extern "C" { -#include "cplexmp-ampls-c-api.h" // CPLEX AMPLS C API +#include "cplex-ampls-c-api.h" // CPLEX AMPLS C API } #include "mp/ampls-cpp-api.h" @@ -134,7 +134,7 @@ namespace mp { bool CplexBackend::IsMIP() const { auto type = CPXgetprobtype(env(), lp()); - return (!(type == CPXPROB_LP) || (type == CPXPROB_QP) + return !((type == CPXPROB_LP) || (type == CPXPROB_QP) || (type == CPXPROB_QCP)); } bool CplexBackend::IsQCP() const { @@ -145,7 +145,7 @@ namespace mp { bool CplexBackend::HasSolution() { if (hasSolution_ == -1){ auto status = GetSolveResult().first; - hasSolution_ = (status == sol::LIMIT_FEAS) || (status == sol::SOLVED); + hasSolution_ = (status == sol::LIMIT_FEAS) || (status == sol::SOLVED) || (status == sol::UNCERTAIN); } return hasSolution_; } @@ -214,19 +214,27 @@ namespace mp { std::vector cons(NumLinCons()); int status = CPXgetbase(env(), lp(), nullptr, cons.data()); if (status) return cons; - for (auto& s : cons) { - switch (s) { + std::vector sense(NumLinCons()); + CPLEX_CALL(CPXgetsense(env(), lp(), sense.data(), 0, NumLinCons()-1)); + + for (auto i = cons.size(); i--; ) { + switch (cons[i]) { case CPX_BASIC: - s = (int)BasicStatus::bas; + cons[i] = (int)BasicStatus::bas; break; case CPX_AT_LOWER: - s = (int)BasicStatus::low; + if (sense[i] == 'L') + cons[i] = (int)BasicStatus::upp; + else if (sense[i] == 'E') + cons[i] = (int)BasicStatus::equ; + else + cons[i] = (int)BasicStatus::low; break; case CPX_AT_UPPER: // just for range constraints - s = (int)BasicStatus::upp; + cons[i] = (int)BasicStatus::upp; break; default: - MP_RAISE(fmt::format("Unknown CPLEX rstat value: {}", s)); + MP_RAISE(fmt::format("Unknown CPLEX rstat value: {}", cons[i])); } } return cons; @@ -364,7 +372,7 @@ pre::ValueMapDbl CplexBackend::DualSolution() { } ArrayRef CplexBackend::DualSolution_LP() { - if (IsMIP() && need_fixed_MIP() && HasSolution()) + if (HasSolution() && ((!IsMIP()) || need_fixed_MIP())) { int num_cons = NumLinCons(); std::vector pi(num_cons); @@ -1079,7 +1087,7 @@ void CplexBackend::DoCplexFeasRelax() { std::vector ubpen = feasrelax().ubpen(); if (ubpen.size() && ubpen.size() < (size_t)NumVars()) ubpen.resize(NumVars()); - CPLEX_CALL(CPXfeasopt(env(), lp(), + CPLEX_CALL(CPXfeasopt(env(), lp(), (double*)data_or_null(rhspen), (double*)data_or_null(rhspen), (double*)data_or_null(lbpen), (double*)data_or_null(ubpen))); } @@ -2302,23 +2310,23 @@ void CplexBackend::CplexPlayObjNParams() { } // namespace mp -AMPLS_MP_Solver* Open_cplexmp(CCallbacks cb = {}) { +AMPLS_MP_Solver* Open_cplex(CCallbacks cb = {}) { AMPLS_MP_Solver* slv = AMPLS__internal__Open(std::unique_ptr{new mp::CplexBackend()}, cb); return slv; } -void AMPLSClose_cplexmp(AMPLS_MP_Solver* slv) { +void AMPLSClose_cplex(AMPLS_MP_Solver* slv) { AMPLS__internal__Close(slv); } -void* AMPLSGetModel_cplexmp(AMPLS_MP_Solver* slv) { +void* AMPLSGetModel_cplex(AMPLS_MP_Solver* slv) { return dynamic_cast(AMPLSGetBackend(slv))->lp(); } -void* AMPLSGetEnv_cplexmp(AMPLS_MP_Solver* slv) { +void* AMPLSGetEnv_cplex(AMPLS_MP_Solver* slv) { return dynamic_cast(AMPLSGetBackend(slv))->env(); } diff --git a/solvers/cplexmp/cplexmpbackend.h b/solvers/cplex/cplexbackend.h similarity index 99% rename from solvers/cplexmp/cplexmpbackend.h rename to solvers/cplex/cplexbackend.h index 3182ea6d0..678d1db8a 100644 --- a/solvers/cplexmp/cplexmpbackend.h +++ b/solvers/cplex/cplexbackend.h @@ -5,7 +5,7 @@ #include "mp/backend-mip.h" #include "mp/flat/backend_flat.h" -#include "cplexmpcommon.h" +#include "cplexcommon.h" #include namespace mp { diff --git a/solvers/cplexmp/cplexmpcommon.cc b/solvers/cplex/cplexcommon.cc similarity index 98% rename from solvers/cplexmp/cplexmpcommon.cc rename to solvers/cplex/cplexcommon.cc index f2933dec3..adae2986b 100644 --- a/solvers/cplexmp/cplexmpcommon.cc +++ b/solvers/cplex/cplexcommon.cc @@ -1,5 +1,5 @@ #include "mp/format.h" -#include "cplexmpcommon.h" +#include "cplexcommon.h" namespace mp { diff --git a/solvers/cplexmp/cplexmpcommon.h b/solvers/cplex/cplexcommon.h similarity index 100% rename from solvers/cplexmp/cplexmpcommon.h rename to solvers/cplex/cplexcommon.h diff --git a/solvers/cplexmp/cplexmpmodelapi.cc b/solvers/cplex/cplexmodelapi.cc similarity index 99% rename from solvers/cplexmp/cplexmpmodelapi.cc rename to solvers/cplex/cplexmodelapi.cc index 5286ca1eb..95c6fd439 100644 --- a/solvers/cplexmp/cplexmpmodelapi.cc +++ b/solvers/cplex/cplexmodelapi.cc @@ -1,4 +1,4 @@ -#include "cplexmpmodelapi.h" +#include "cplexmodelapi.h" namespace mp { diff --git a/solvers/cplexmp/cplexmpmodelapi.h b/solvers/cplex/cplexmodelapi.h similarity index 99% rename from solvers/cplexmp/cplexmpmodelapi.h rename to solvers/cplex/cplexmodelapi.h index 87a7ccbc1..c26afe9d8 100644 --- a/solvers/cplexmp/cplexmpmodelapi.h +++ b/solvers/cplex/cplexmodelapi.h @@ -4,7 +4,7 @@ #include #include "mp/env.h" -#include "cplexmpcommon.h" +#include "cplexcommon.h" #include "mp/flat/model_api_base.h" #include "mp/flat/constr_std.h" diff --git a/solvers/cplexmp/main.cc b/solvers/cplex/main.cc similarity index 100% rename from solvers/cplexmp/main.cc rename to solvers/cplex/main.cc diff --git a/solvers/cplexmp/model-mgr-with-std-pb.cc b/solvers/cplex/model-mgr-with-std-pb.cc similarity index 100% rename from solvers/cplexmp/model-mgr-with-std-pb.cc rename to solvers/cplex/model-mgr-with-std-pb.cc diff --git a/solvers/cplexmp/README.cplexmp.txt b/solvers/cplexmp/README.cplexmp.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/solvers/cplexmp/cplexmp-lib.c b/solvers/cplexmp/cplexmp-lib.c deleted file mode 100644 index ce4ccb06f..000000000 --- a/solvers/cplexmp/cplexmp-lib.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "cplexmp/cplexmp-ampls-c-api.h" - -AMPLS_C_EXPORT AMPLS_MP_Solver* AMPLSOpen_cplexmp(int argc, char** argv) -{ - CCallbacks cb = { NULL }; - return Open_cplexmp(cb); -} \ No newline at end of file diff --git a/solvers/cplex/CMakeLists.txt b/solvers/cplexsocp/CMakeLists.txt similarity index 100% rename from solvers/cplex/CMakeLists.txt rename to solvers/cplexsocp/CMakeLists.txt diff --git a/solvers/cplex/cplex.c b/solvers/cplexsocp/cplex.c similarity index 100% rename from solvers/cplex/cplex.c rename to solvers/cplexsocp/cplex.c diff --git a/solvers/cplex/socp.cc b/solvers/cplexsocp/socp.cc similarity index 100% rename from solvers/cplex/socp.cc rename to solvers/cplexsocp/socp.cc diff --git a/solvers/xpress/CHANGES.xpress.md b/solvers/xpress/CHANGES.xpress.md index cb680a09f..43e92a902 100644 --- a/solvers/xpress/CHANGES.xpress.md +++ b/solvers/xpress/CHANGES.xpress.md @@ -2,6 +2,13 @@ Summary of recent updates to Xpress for AMPL ============================================ +## 20240823 +- Updated to Xpress 43.01.03 +- Added hybrid gradient algorithm (set *bar:alg* to 4) +- Option *bar:cpuplatform* defaults to -2. To replicate the previous + versions behaviour, set to -1. + + ## 20240724 - Option *acc:_all* - Useful to disable all reformulations (acc:_all=2), diff --git a/solvers/xpress/xpressbackend.cc b/solvers/xpress/xpressbackend.cc index 0ab13943c..297480b91 100644 --- a/solvers/xpress/xpressbackend.cc +++ b/solvers/xpress/xpressbackend.cc @@ -586,7 +586,8 @@ std::string XpressmpBackend::DoXpressFixedModel() { "-1", "Automatic choice (default)", -1}, { "1", "Infeasible-start barrier algorithm", 1}, { "2", "Homogeneous self-dual barrier algorithm", 2}, - { "3", "Start with 2 and maybe switch to 1 while solving", 3} + { "3", "Start with 2 and maybe switch to 1 while solving", 3}, + { "4", "Use the hybrid gradient algorithm", 4} }; static const mp::OptionValueInfo values_barstart[] = { @@ -743,7 +744,16 @@ std::string XpressmpBackend::DoXpressFixedModel() { "4", "skip pivots that are \"less numerically reliable\"", 4}, { "8", "do a slower but more numerically stable crossover", 8}, }; - + static const mp::OptionValueInfo values_barhgops[] = { + { "1", "use an asymmetric average for the primal averaging", 1}, + { "2", "use the 1-norm of the coefficient matrix in normalizing the initial solution", 2}, + { "4", "use the 2-norm of the coefficient matrix in normalizing the initial solution", 4}, + { "8", "use the infinity norm of the coefficient matrix in normalizing the initial solution", 8}, + { "16", "take the square root of omega", 16}, + { "32", "contract omega towards 1 if the infeasibility is small enough", 32}, + { "64", "omega is based on the infeasibility", 64}, + }; + static const mp::OptionValueInfo values_cutselect[] = { {"32", "clique cuts", 32}, {"64", "mixed - integer founding(MIR) cuts", 64}, @@ -1688,7 +1698,7 @@ AddSolverOption("alg:resourcestrategy resourcestrategy", AddSolverOption("bar:cpuplatform cpuplatform", "Which instruction are allowed to the Newton barrier method:\n:", - XPRS_CPUPLATFORM, values_cpuplatform, -1); + XPRS_CPUPLATFORM, values_cpuplatform, -2); AddSolverOption("bar:crash barcrash", "Choice of crash procedure for crossover, higher number " @@ -1730,6 +1740,20 @@ AddSolverOption("alg:resourcestrategy resourcestrategy", "dual infeasibilities; default = 0 (automatic choice)", XPRS_BARDUALSTOP, 0.0, DBL_MAX); + + + AddSolverOption("bar:hgextrapolate barhgextrapolate", + "Extrapolation parameter for the hybrid gradient algorithm; default = 0.99", + XPRS_BARHGEXTRAPOLATE, 0.0, 1.0); + + AddSolverOption("bar:hgmaxrestarts barhgmaxrestarts", + "Maximum number of restarts in the hybrid gradient algorithm; default =500", + XPRS_BARHGMAXRESTARTS, 0, INT_MAX); + + AddSolverOption("bar:hgops barhgops", + "Control option for hybrid gradient (default = 8); sum of:\n" + "\n.. value-table::\n", XPRS_BARHGOPS, values_barhgops, -1); + AddSolverOption("bar:gapstop bargapstop", "Barrier method convergence tolerance on the relative" " duality gap; default = 0", XPRS_BARGAPSTOP, 0.0, DBL_MAX); diff --git a/test/end2end/scripts/python/SolverCollection.py b/test/end2end/scripts/python/SolverCollection.py index eef216bdc..d93832cb8 100644 --- a/test/end2end/scripts/python/SolverCollection.py +++ b/test/end2end/scripts/python/SolverCollection.py @@ -35,8 +35,8 @@ def addStdSolvers(solvers: SolverCollection, binPath=""): solvers.addSolver(Solver.OcteractSolver(path.join(binPath, "octeract-engine"))) solvers.addSolver(Solver.GurobiDirectSolver(path.join(binPath,"gurobi"))) solvers.addSolver(Solver.GurobiSolver(path.join(binPath,"gurobiasl"))) - solvers.addSolver(Solver.CPLEXSolver(path.join(binPath,"cplex"))) - solvers.addSolver(Solver.CPLEXDirectSolver(path.join(binPath,"cplexmp"))) ## Need as long as the target is there + solvers.addSolver(Solver.CPLEXSolver(path.join(binPath,"cplexasl"))) + solvers.addSolver(Solver.CPLEXDirectSolver(path.join(binPath,"cplex"))) solvers.addSolver(Solver.BaronSolver(path.join(binPath,"baron"))) solvers.addSolver(Solver.ConoptSolver(path.join(binPath,"conopt4"))) solvers.addSolver(Solver.ConoptSolver(path.join(binPath,"conopt")))