Skip to content

Commit e28ff5e

Browse files
committed
Several changes
More careful license management and error message from Gurobi.
1 parent c2a58fc commit e28ff5e

File tree

12 files changed

+261
-60
lines changed

12 files changed

+261
-60
lines changed

lib/coek/coek/solvers/gurobi/coek_gurobi.hpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,17 @@ class GurobiSolver : public SolverRepn {
1212
std::unordered_map<size_t, GRBVar> x;
1313
std::shared_ptr<SolverResults> results;
1414

15+
enum class gurobi_license_status { available, error, unknown };
16+
gurobi_license_status license_status;
17+
1518
public:
16-
GurobiSolver() : SolverRepn(), env(0), gmodel(0) {}
19+
GurobiSolver() : SolverRepn(), env(0), gmodel(0), license_status(gurobi_license_status::unknown)
20+
{
21+
}
1722
~GurobiSolver();
1823

24+
bool available();
25+
1926
std::shared_ptr<SolverResults> resolve();
2027
std::shared_ptr<SolverResults> solve(Model&);
2128
#ifdef COEK_WITH_COMPACT_MODEL

lib/coek/coek/solvers/gurobi/gurobi.cpp

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,24 @@ GurobiSolver::~GurobiSolver()
140140
}
141141
}
142142

143+
bool GurobiSolver::available()
144+
{
145+
if (license_status == gurobi_license_status::available)
146+
return true;
147+
148+
try {
149+
auto _env = new GRBEnv(true);
150+
_env->start();
151+
license_status = gurobi_license_status::available;
152+
delete _env;
153+
}
154+
catch (GRBException e) {
155+
license_status = gurobi_license_status::error;
156+
}
157+
158+
return license_status == gurobi_license_status::available;
159+
}
160+
143161
void GurobiSolver::set_gurobi_options()
144162
{
145163
// All options are converted to strings for Gurobi
@@ -161,12 +179,19 @@ void GurobiSolver::pre_solve()
161179
results->tic();
162180

163181
if (initial_solve()) {
164-
env = new GRBEnv(true);
165-
auto it = integer_options().find("OutputFlag");
166-
if (it != integer_options().end())
167-
env->set(GRB_IntParam_OutputFlag, it->second);
168-
env->start();
169-
gmodel = new GRBModel(*env);
182+
try {
183+
env = new GRBEnv(true);
184+
auto it = integer_options().find("OutputFlag");
185+
if (it != integer_options().end())
186+
env->set(GRB_IntParam_OutputFlag, it->second);
187+
env->start();
188+
}
189+
catch (GRBException e) {
190+
license_status = gurobi_license_status::error;
191+
results->termination_condition = TerminationCondition::license_problems;
192+
}
193+
if (available())
194+
gmodel = new GRBModel(*env);
170195
}
171196
}
172197

@@ -183,6 +208,9 @@ void GurobiSolver::post_solve()
183208
std::shared_ptr<SolverResults> GurobiSolver::solve(Model& model)
184209
{
185210
pre_solve();
211+
if (not available())
212+
return results;
213+
186214
auto _model = model.repn.get();
187215
results->model_name = _model->name;
188216

@@ -281,6 +309,9 @@ std::shared_ptr<SolverResults> GurobiSolver::solve(Model& model)
281309
std::shared_ptr<SolverResults> GurobiSolver::solve(CompactModel& compact_model)
282310
{
283311
pre_solve();
312+
if (not available())
313+
return results;
314+
284315
results->model_name = compact_model.name();
285316

286317
// Add Gurobi variables
@@ -395,6 +426,8 @@ std::shared_ptr<SolverResults> GurobiSolver::solve(CompactModel& compact_model)
395426
std::shared_ptr<SolverResults> GurobiSolver::resolve()
396427
{
397428
pre_solve();
429+
if (not available())
430+
return results;
398431

399432
auto _model = model.repn.get();
400433
results->model_name = _model->name;

lib/coek/coek/solvers/solver.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ void Solver::initialize(std::string name_)
1717
name = name_;
1818
}
1919

20-
bool Solver::available() const { return repn.get(); }
20+
bool Solver::available() { return repn.get() and repn->available(); }
2121

2222
std::shared_ptr<SolverResults> Solver::solve(Model& model)
2323
{
@@ -71,7 +71,7 @@ void NLPSolver::initialize(std::string name_)
7171
name = name_;
7272
}
7373

74-
bool NLPSolver::available() const { return repn.get() && repn->available(); }
74+
bool NLPSolver::available() { return repn.get() and repn->available(); }
7575

7676
std::shared_ptr<SolverResults> NLPSolver::solve(NLPModel& model)
7777
{

lib/coek/coek/solvers/solver.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class Solver : public OptionCache {
3232
void initialize(std::string solver);
3333

3434
/** \returns \c true if the solver is available */
35-
bool available() const;
35+
virtual bool available();
3636

3737
/** Optimize the specified model
3838
*
@@ -74,7 +74,7 @@ class NLPSolver : public OptionCache {
7474
void initialize(std::string solver);
7575

7676
/** \returns \c true if the solver is available */
77-
bool available() const;
77+
bool available();
7878

7979
/** Optimize the specified model
8080
*

lib/coek/coek/solvers/solver_repn.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ class SolverRepn : public SolverCache, public OptionCache {
8484
virtual std::shared_ptr<SolverResults> resolve() = 0;
8585

8686
void find_updated_coefs();
87+
88+
virtual bool available() { return true; }
8789
};
8890

8991
SolverRepn* create_solver(std::string& name, OptionCache& options);

lib/coek/coek/solvers/solver_results.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ std::string to_string(TerminationCondition tc)
4444
case TerminationCondition::interrupted:
4545
return "interrupted";
4646

47-
case TerminationCondition::licensing_problems:
48-
return "licensing_problems";
47+
case TerminationCondition::license_problems:
48+
return "license_problems";
4949

5050
case TerminationCondition::solver_not_available:
5151
return "solver_not_available";

lib/coek/coek/solvers/solver_results.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,10 @@ enum class TerminationCondition {
5959
// The solver was interrupted while running.
6060
interrupted,
6161

62-
// The solver experienced issues with licensing. This could be that no license was found, the
62+
// The solver experienced issues with license. This could be that no license was found, the
6363
// license is of the wrong type for the problem (e.g., problem is too big for type of license),
64-
// or there was an issue contacting a licensing server.
65-
licensing_problems,
64+
// or there was an issue contacting a license server.
65+
license_problems,
6666

6767
// The solver cannot be found. This could be a problem the installation of the solver, the
6868
// solver link library may not be found, or the solver executable may not be found.

lib/coek/test/CMakeLists.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,16 @@ TARGET_LINK_LIBRARIES(coek_micro_test PUBLIC coek ${coek_link_libraries})
9797
TARGET_COMPILE_OPTIONS(coek_micro_test PUBLIC ${coek_compile_options})
9898
target_compile_definitions(coek_micro_test PUBLIC COEK_TEST_DIR="${CMAKE_CURRENT_SOURCE_DIR}/")
9999

100+
# gurobi
101+
add_executable(gurobi_test coek_gurobi_test.cpp solver/TestModels.cpp)
102+
TARGET_LINK_LIBRARIES(gurobi_test PUBLIC coek ${coek_link_libraries} ${coek_test_libraries})
103+
TARGET_COMPILE_OPTIONS(gurobi_test PUBLIC ${coek_compile_options})
104+
target_compile_definitions(gurobi_test PUBLIC COEK_TEST_DIR="${CMAKE_CURRENT_SOURCE_DIR}/")
105+
100106

101107
# testing
102108
add_custom_target(test_coek
103-
COMMAND ${CMAKE_CTEST_COMMAND})
109+
COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure)
104110
add_custom_target(test_coek_verbose
105111
COMMAND ${CMAKE_CTEST_COMMAND} --verbose)
106112
add_custom_target(test_coek_memcheck

lib/coek/test/coek_gurobi_test.cpp

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// #include "catch2/catch_test_macros.hpp"
2+
// #include "catch2/catch_approx.hpp"
3+
// #include "catch2/generators/catch_generators.hpp"
4+
5+
#include <cassert>
6+
#include "coek/coek.hpp"
7+
#include "test/solver/TestModel.hpp"
8+
9+
coek::Solver test_solver()
10+
{
11+
coek::Solver solver("gurobi");
12+
solver.set_option("OutputFlag", 0);
13+
return solver;
14+
}
15+
16+
int main()
17+
{
18+
//
19+
// Gurobi Checks
20+
//
21+
22+
auto solver = test_solver();
23+
if (solver.available()) {
24+
// SECTION("empty")
25+
{
26+
std::cout << "empty" << std::endl;
27+
auto solver = test_solver();
28+
auto test = test::model("empty");
29+
auto m = test->model;
30+
assert(m.name() == "empty");
31+
auto res = solver.solve(m);
32+
assert(res->termination_condition == coek::TerminationCondition::empty_model);
33+
}
34+
// SECTION("rosenbr")
35+
{
36+
std::cout << "rosenbr" << std::endl;
37+
auto solver = test_solver();
38+
auto test = test::model("rosenbr");
39+
auto m = test->model;
40+
assert(m.name() == "rosenbr");
41+
auto res = solver.solve(m);
42+
assert(res->termination_condition
43+
== coek::TerminationCondition::invalid_model_for_solver);
44+
}
45+
// SECTION("simplelp1")
46+
{
47+
std::cout << "simplelp1" << std::endl;
48+
auto solver = test_solver();
49+
auto test = test::model("simplelp1");
50+
auto m = test->model;
51+
assert(m.name() == "simplelp1");
52+
auto res = solver.solve(m);
53+
assert(test->check_results(m, res) == true);
54+
}
55+
// SECTION("simpleqp1")
56+
{
57+
std::cout << "simpleqp1" << std::endl;
58+
auto solver = test_solver();
59+
auto test = test::model("simpleqp1");
60+
auto m = test->model;
61+
assert(m.name() == "simpleqp1");
62+
auto res = solver.solve(m);
63+
assert(test->check_results(m, res) == true);
64+
}
65+
// SECTION("simpleqp2")
66+
{
67+
std::cout << "simpleqp2" << std::endl;
68+
auto solver = test_solver();
69+
auto test = test::model("simpleqp2");
70+
auto m = test->model;
71+
assert(m.name() == "simpleqp2");
72+
auto res = solver.solve(m);
73+
assert(test->check_results(m, res) == true);
74+
}
75+
// SECTION("simpleqp3")
76+
{
77+
std::cout << "simpleqp3" << std::endl;
78+
auto solver = test_solver();
79+
auto test = test::model("simpleqp3");
80+
auto m = test->model;
81+
assert(m.name() == "simpleqp3");
82+
auto res = solver.solve(m);
83+
assert(test->check_results(m, res) == true);
84+
}
85+
// SECTION("simpleqp4")
86+
{
87+
std::cout << "simpleqp4" << std::endl;
88+
auto solver = test_solver();
89+
auto test = test::model("simpleqp4");
90+
auto m = test->model;
91+
assert(m.name() == "simpleqp4");
92+
auto res = solver.solve(m);
93+
assert(test->check_results(m, res) == true);
94+
}
95+
// SECTION("lp_bounds1")
96+
{
97+
std::cout << "lp_bounds" << std::endl;
98+
auto solver = test_solver();
99+
auto test = test::model("lp_bounds1");
100+
auto m = test->model;
101+
assert(m.name() == "lp_bounds1");
102+
auto res = solver.solve(m);
103+
assert(test->check_results(m, res) == true);
104+
}
105+
// SECTION("lp_bounds2")
106+
{
107+
std::cout << "lp_bounds2" << std::endl;
108+
auto solver = test_solver();
109+
auto test = test::model("lp_bounds2");
110+
auto m = test->model;
111+
assert(m.name() == "lp_bounds2");
112+
auto res = solver.solve(m);
113+
assert(test->check_results(m, res) == true);
114+
}
115+
// SECTION("qp_bounds")
116+
{
117+
std::cout << "qp_bounds" << std::endl;
118+
auto solver = test_solver();
119+
auto test = test::model("qp_bounds");
120+
auto m = test->model;
121+
assert(m.name() == "qp_bounds");
122+
auto res = solver.solve(m);
123+
assert(test->check_results(m, res) == true);
124+
}
125+
}
126+
else {
127+
// SECTION("simplelp1")
128+
{
129+
std::cout << "simplelp1" << std::endl;
130+
auto solver = test_solver();
131+
auto test = test::model("simplelp1");
132+
auto m = test->model;
133+
assert(m.name() == "simplelp1");
134+
auto res = solver.solve(m);
135+
assert((res->termination_condition == coek::TerminationCondition::solver_not_available)
136+
or (res->termination_condition == coek::TerminationCondition::license_problems));
137+
}
138+
}
139+
}

0 commit comments

Comments
 (0)