26 #include "gurobi_interface.hpp"
27 #include "casadi/core/casadi_misc.hpp"
28 #include "casadi/core/nlp_tools.hpp"
34 #define GUROBI_CALL_WARN(func, msg) do { \
37 casadi_warning(msg ": Gurobi error " + std::to_string(error)); \
44 if (sense ==
"<=")
return GRB_LESS_EQUAL;
45 if (sense ==
">=")
return GRB_GREATER_EQUAL;
46 if (sense ==
"=")
return GRB_EQUAL;
47 return GRB_LESS_EQUAL;
51 class CallbackDataHelper {
57 CallbackDataHelper(
void* cbdata,
int where) : cbdata_(cbdata), where_(where) {}
59 bool getDouble(
int what,
double& value) {
60 int error = GRBcbget(cbdata_, where_, what, &value);
64 bool getInt(
int what,
int& value) {
65 int error = GRBcbget(cbdata_, where_, what, &value);
69 bool getSolution(std::vector<double>& solution) {
70 int error = GRBcbget(cbdata_, where_, GRB_CB_MIPSOL_SOL, solution.data());
74 bool addLazyConstraint(
const std::vector<int>& ind,
75 const std::vector<double>& val,
76 char sense,
double rhs)
const {
77 if (where_ != GRB_CB_MIPSOL) {
78 casadi_warning(
"Can only add lazy constraints in MIPSOL context");
82 int error = GRBcblazy(cbdata_,
static_cast<int>(ind.size()),
83 ind.data(), val.data(), sense, rhs);
85 casadi_warning(
"GRBcblazy failed with code: " + std::to_string(error));
94 if (where == GRB_CB_MIPSOL && usrdata) {
98 }
catch (
const std::exception& e) {
100 casadi_warning(
"Exception in Gurobi callback: " + std::string(e.what()));
106 int CASADI_CONIC_GUROBI_EXPORT
109 plugin->name =
"gurobi";
111 plugin->version = CASADI_VERSION;
114 #ifdef GUROBI_ADAPTOR
116 int ret = gurobi_adaptor_load(buffer,
sizeof(buffer));
118 casadi_warning(
"Failed to load Gurobi adaptor: " + std::string(buffer) +
".");
131 const std::map<std::string, Sparsity>& st)
133 lazy_constraints_callback_(
Function()) {
145 "Type of variables: [CONTINUOUS|binary|integer|semicont|semiint]"}},
148 "Options to be passed to gurobi."}},
151 "Definition of SOS groups by indices."}},
154 "Weights corresponding to SOS entries."}},
157 "Specify 1 or 2 for each SOS group."}},
158 {
"lazy_constraints_callback",
160 "User callback for adding LazyConstraints at MIPSOL. "
161 "Input: dict with solution data. Output: dict with lazy constraints."}},
170 std::vector<std::string> vtype;
172 std::vector< std::vector<casadi_int> > sos_groups;
173 std::vector< std::vector<double> > sos_weights;
174 std::vector<casadi_int> sos_types;
177 for (
auto&& op : opts) {
178 if (op.first==
"vtype") {
180 }
else if (op.first==
"gurobi") {
182 }
else if (op.first==
"sos_groups") {
183 sos_groups = op.second.to_int_vector_vector();
184 }
else if (op.first==
"sos_weights") {
185 sos_weights = op.second.to_double_vector_vector();
186 }
else if (op.first==
"sos_types") {
187 sos_types = op.second.to_int_vector();
188 }
else if (op.first ==
"lazy_constraints_callback") {
192 casadi_message(
"Successfully obtained callback function");
197 casadi_error(
"Callback function has wrong signature. "
198 "Expected 5 inputs, 3 outputs");
200 }
catch (
const std::exception& e) {
201 casadi_error(
"Failed to get callback function: " + std::string(e.what()));
207 casadi_message(
"Callback setup complete");
215 if (!sos_weights.empty())
221 if (!vtype.empty()) {
222 casadi_assert(vtype.size()==
nx_,
"Option 'vtype' has wrong length");
224 for (casadi_int i=0; i<
nx_; ++i) {
225 if (vtype[i]==
"continuous") {
226 vtype_[i] = GRB_CONTINUOUS;
227 }
else if (vtype[i]==
"binary") {
229 }
else if (vtype[i]==
"integer") {
231 }
else if (vtype[i]==
"semicont") {
233 }
else if (vtype[i]==
"semiint") {
236 casadi_error(
"No such variable type: " + vtype[i]);
258 auto output_flag =
opts_.find(
"OutputFlag");
259 auto log_to_console =
opts_.find(
"LogToConsole");
261 if (output_flag !=
opts_.end() && output_flag->second.as_int() == 0 &&
262 log_to_console !=
opts_.end() && log_to_console->second.as_int() == 0) {
265 flag = GRBemptyenv(&m->env);
266 casadi_assert(!flag && m->env,
267 "Failed to create empty GUROBI environment. Flag: " +
str(flag));
269 flag = GRBsetintparam(m->env,
"OutputFlag", 0);
270 casadi_assert(!flag, GRBgeterrormsg(m->env));
272 flag = GRBsetintparam(m->env,
"LogToConsole", 0);
273 casadi_assert(!flag, GRBgeterrormsg(m->env));
275 flag = GRBstartenv(m->env);
276 casadi_assert(!flag, GRBgeterrormsg(m->env));
278 flag = GRBloadenv(&m->env,
nullptr);
279 casadi_assert(!flag && m->env,
280 "Failed to create GUROBI environment. Flag: " +
str(flag)
281 +
":" + GRBgeterrormsg(m->env));
318 cb->
w.resize(cb->
sz_w);
324 m->add_stat(
"preprocessing");
325 m->add_stat(
"solver");
326 m->add_stat(
"postprocessing");
338 case GRB_INF_OR_UNBD:
339 return "INF_OR_UNBD";
344 case GRB_ITERATION_LIMIT:
345 return "ITERATION_LIMIT";
350 case GRB_SOLUTION_LIMIT:
351 return "SOLUTION_LIMIT";
352 case GRB_INTERRUPTED:
353 return "INTERRUPTED";
365 solve(
const double** arg,
double** res, casadi_int* iw,
double* w,
void* mem)
const {
370 m->fstats.at(
"preprocessing").tic();
373 m->return_status = -1;
400 int *ind=
reinterpret_cast<int*
>(iw); iw+=sm.
indval_size;
401 int *ind2=
reinterpret_cast<int*
>(iw); iw+=
nx_;
402 char *vtypes=
reinterpret_cast<char*
>(iw); iw+=
nx_;
405 GRBmodel *model =
nullptr;
407 casadi_int flag = GRBnewmodel(m->env, &model,
name_.c_str(), 0,
408 nullptr,
nullptr,
nullptr,
nullptr,
nullptr);
409 casadi_assert(!flag, GRBgeterrormsg(m->env));
412 for (casadi_int i=0; i<
nx_; ++i) {
414 double lb = lbx ? lbx[i] : 0., ub = ubx ? ubx[i] : 0.;
415 if (isinf(lb)) lb = -GRB_INFINITY;
416 if (isinf(ub)) ub = GRB_INFINITY;
425 vtype = lb==0 && ub==1 ? GRB_BINARY : GRB_INTEGER;
428 vtype = GRB_CONTINUOUS;
433 flag = GRBaddvar(model, 0,
nullptr,
nullptr, g ? g[i] : 0., lb, ub, vtype,
nullptr);
434 casadi_assert(!flag, GRBgeterrormsg(m->env));
437 GRBupdatemodel(model);
438 for (casadi_int i=0; i<
nx_; ++i) {
440 if (vtypes[i] != GRB_CONTINUOUS) {
441 flag = GRBsetdblattrelement(model,
"Start", i, x0[i]);
442 casadi_assert(!flag, GRBgeterrormsg(m->env));
450 for (casadi_int i=0;i<sm.
r.size()-1;++i) {
451 for (casadi_int k=0;k<sm.
r[i+1]-sm.
r[i]-1;++k) {
452 flag = GRBaddvar(model, 0,
nullptr,
nullptr, 0, -GRB_INFINITY, GRB_INFINITY,
453 GRB_CONTINUOUS,
nullptr);
454 casadi_assert(!flag, GRBgeterrormsg(m->env));
456 flag = GRBaddvar(model, 0,
nullptr,
nullptr, 0, 0, GRB_INFINITY, GRB_CONTINUOUS,
nullptr);
457 casadi_assert(!flag, GRBgeterrormsg(m->env));
460 flag = GRBupdatemodel(model);
461 casadi_assert(!flag, GRBgeterrormsg(m->env));
465 for (
int i=0; i<
nx_; ++i) {
468 casadi_int numqnz = H_colind[1]-H_colind[0];
469 for (casadi_int k=0;k<numqnz;++k) ind[k]=H_row[k];
486 flag = GRBaddqpterms(model, numqnz, ind, ind2, val);
487 casadi_assert(!flag, GRBgeterrormsg(m->env));
490 std::vector<char> constraint_type(
na_);
494 const casadi_int *AT_colind=sm.
AT.
colind(), *AT_row=sm.
AT.
row();
495 for (casadi_int i=0; i<
na_; ++i) {
497 double lb = lba ? lba[i] : 0., ub = uba ? uba[i] : 0.;
499 casadi_int numnz = 0;
501 for (casadi_int k=AT_colind[i]; k<AT_colind[i+1]; ++k) {
502 casadi_int j = AT_row[k];
510 constraint_type[i] = 1;
514 constraint_type[i] = 0;
518 flag = GRBaddconstr(model, numnz, ind, val, GRB_LESS_EQUAL, ub,
nullptr);
519 casadi_assert(!flag, GRBgeterrormsg(m->env));
525 flag = GRBaddconstr(model, numnz, ind, val, GRB_GREATER_EQUAL, lb,
nullptr);
526 casadi_assert(!flag, GRBgeterrormsg(m->env));
530 flag = GRBaddconstr(model, numnz, ind, val, GRB_EQUAL, lb,
nullptr);
531 casadi_assert(!flag, GRBgeterrormsg(m->env));
535 flag = GRBaddrangeconstr(model, numnz, ind, val, lb, ub,
nullptr);
536 casadi_assert(!flag, GRBgeterrormsg(m->env));
541 std::vector<double>
pi(npi);
544 if (!m->sos_ind.empty()) {
545 flag = GRBaddsos(model, m->sos_beg.size()-1, m->sos_ind.size(),
548 casadi_assert(!flag, GRBgeterrormsg(m->env));
553 const casadi_int* colind = sp.
colind();
554 const casadi_int* row = sp.
row();
555 const casadi_int* data = sm.
map_Q.
ptr();
558 for (casadi_int i=0; i<sp.
size2(); ++i) {
560 casadi_int numnz = 0;
562 for (casadi_int k=colind[i]; k<colind[i+1]; ++k) {
563 casadi_int j = row[k];
566 val[numnz] = (q && j<
nx_) ? q[data[k]] : -1;
572 double bound = sm.
map_P[i]==-1 ? 0 : -p[sm.
map_P[i]];
574 flag = GRBaddconstr(model, numnz, ind, val, GRB_EQUAL, bound,
nullptr);
575 casadi_assert(!flag, GRBgeterrormsg(m->env));
579 for (casadi_int i=0; i<sm.
r.size()-1; ++i) {
580 casadi_int block_size = sm.
r[i+1]-sm.
r[i];
583 for (casadi_int j=0;j<block_size;++j) {
584 ind[j] =
nx_ + sm.
r[i] + j;
585 val[j] = j<block_size-1 ? 1 : -1;
588 flag = GRBaddqconstr(model, 0,
nullptr,
nullptr,
589 block_size, ind, ind, val,
590 GRB_LESS_EQUAL, 0,
nullptr);
591 casadi_assert(!flag, GRBgeterrormsg(m->env));
595 for (
auto && op :
opts_) {
596 int ret = GRBgetparamtype(m->env, op.first.c_str());
599 casadi_error(
"Parameter '" + op.first +
"' unknown to Gurobi.");
602 flag = GRBsetintparam(GRBgetenv(model), op.first.c_str(), op.second);
606 flag = GRBsetdblparam(GRBgetenv(model), op.first.c_str(), op.second);
610 std::string s = op.second;
611 flag = GRBsetstrparam(GRBgetenv(model), op.first.c_str(), s.c_str());
615 casadi_error(
"Not implememented : " +
str(ret));
617 casadi_assert(!flag, GRBgeterrormsg(m->env));
622 casadi_message(
"mipsol callback is not a null pointer")
624 int error = GRBsetintparam(GRBgetenv(model),
"LazyConstraints", 1);
626 casadi_warning(
"Failed to enable LazyConstraints parameter");
632 casadi_warning(
"Failed to set callback function");
637 m->fstats.at(
"preprocessing").toc();
638 m->fstats.at(
"solver").tic();
641 flag = GRBoptimize(model);
642 casadi_assert(!flag, GRBgeterrormsg(m->env));
644 m->fstats.at(
"solver").toc();
645 m->fstats.at(
"postprocessing").tic();
648 flag = GRBgetintattr(model,
"Status", &optimstatus);
649 casadi_assert(!flag, GRBgeterrormsg(m->env));
652 " (" << optimstatus <<
")" << std::endl;
654 m->return_status = optimstatus;
655 m->d_qp.success = optimstatus==GRB_OPTIMAL;
656 if (optimstatus==GRB_ITERATION_LIMIT || optimstatus==GRB_TIME_LIMIT
657 || optimstatus==GRB_NODE_LIMIT || optimstatus==GRB_SOLUTION_LIMIT)
662 flag = GRBgetdblattr(model,
"ObjVal", cost);
668 flag = GRBgetdblattrarray(model,
"X", 0,
nx_, x);
672 flag = GRBgetdblattrarray(model,
"RC", 0,
nx_, lam_x);
677 flag = GRBgetdblattrarray(model,
"Pi", 0, npi,
get_ptr(
pi));
682 for (casadi_int i=0;i<
na_;++i) {
683 if (constraint_type[i]==0) {
685 }
else if (constraint_type[i]==1) {
693 flag = GRBgetintattr(model, GRB_INT_ATTR_SOLCOUNT, &(m->pool_sol_nr));
694 if (!flag && m->pool_sol_nr > 0) {
695 m->pool_obj_vals = std::vector<double>(m->pool_sol_nr,
casadi::nan);
696 for (
int idx = 0; idx < m->pool_sol_nr; ++idx) {
697 std::vector<double> x_pool(
nx_);
699 flag = GRBsetintparam(GRBgetenv(model), GRB_INT_PAR_SOLUTIONNUMBER, idx);
700 if (!flag) flag = GRBgetdblattr(model, GRB_DBL_ATTR_POOLOBJVAL, &(m->pool_obj_vals[idx]));
701 if (!flag) flag = GRBgetdblattrarray(model, GRB_DBL_ATTR_XN, 0,
nx_,
get_ptr(x_pool));
705 std::fill(x_pool.begin(), x_pool.end(),
casadi::nan);
708 m->pool_solutions.push_back(x_pool);
714 GRBsetcallbackfunc(model,
nullptr,
nullptr);
719 m->fstats.at(
"postprocessing").toc();
723 if (model) GRBfreemodel(model);
734 stats[
"pool_sol_nr"] = m->pool_sol_nr;
735 stats[
"pool_obj_val"] = m->pool_obj_vals;
736 stats[
"pool_solutions"] = m->pool_solutions;
745 if (this->
env) GRBfreeenv(this->
env);
749 int version = s.
version(
"GurobiInterface", 1, 2);
764 s.
version(
"GurobiInterface", 2);
776 GRBmodel *model,
void *cbdata,
int where)
const {
780 casadi_warning(
"Lazy callback triggered but memory not initialized");
785 CallbackDataHelper helper(cbdata, where);
788 if (!helper.getSolution(cb.x_vals)) {
789 casadi_warning(
"Failed to get solution in MIPSOL callback");
794 if (!helper.getDouble(GRB_CB_MIPSOL_OBJ, cb.obj_val) ||
795 !helper.getDouble(GRB_CB_MIPSOL_OBJBST, cb.obj_best) ||
796 !helper.getDouble(GRB_CB_MIPSOL_OBJBND, cb.obj_bound) ||
797 !helper.getDouble(GRB_CB_MIPSOL_SOLCNT, cb.sol_count)) {
798 casadi_warning(
"Failed to get callback information");
803 std::copy(cb.x_vals.begin(), cb.x_vals.end(), cb.input_data.begin());
804 cb.input_data[cb.nx + 0] = cb.obj_val;
805 cb.input_data[cb.nx + 1] = cb.obj_best;
806 cb.input_data[cb.nx + 2] = cb.obj_bound;
807 cb.input_data[cb.nx + 3] = cb.sol_count;
811 std::fill(cb.a_vec.begin(), cb.a_vec.end(), 0.0);
816 for (casadi_int i = 0; i < cb.sz_arg; ++i) {
817 cb.arg[i] = &cb.input_data[offset];
818 offset += (i == 0 ? cb.nx : 1);
821 if (cb.sz_res >= 1) cb.res[0] = &cb.flag;
822 if (cb.sz_res >= 2) cb.res[1] = cb.a_vec.data();
823 if (cb.sz_res >= 3) cb.res[2] = &cb.b_val;
827 cb.arg.data(), cb.res.data(),
828 cb.iw.data(), cb.w.data(), 0);
831 casadi_warning(
"Lazy callback returned error " + std::to_string(ret));
837 std::vector<int> cind;
838 std::vector<double> cval;
840 for (casadi_int j = 0; j <
nx_; ++j) {
841 if (std::abs(cb.a_vec[j]) > 1e-12) {
842 cind.push_back(
static_cast<int>(j));
843 cval.push_back(cb.a_vec[j]);
848 if (!helper.addLazyConstraint(cind, cval, gurobi_sense, cb.b_val)) {
849 casadi_warning(
"Failed to add lazy constraint");
854 }
catch (
const std::exception& e) {
855 casadi_warning(
"Error in handle_lazy_constraints_callback: "
856 + std::string(e.what()));
static const Options options_
Options.
casadi_int nx_
Number of decision variables.
int init_mem(void *mem) const override
Initalize memory block.
casadi_int na_
The number of constraints (counting both equality and inequality) == A.size1()
virtual void check_inputs(const double *lbx, const double *ubx, const double *lba, const double *uba) const
Check if the numerical values of the supplied bounds make sense.
Sparsity H_
Problem structure.
void init(const Dict &opts) override
Initialize.
void deserialize(DeserializingStream &s, SDPToSOCPMem &m)
std::vector< bool > discrete_
Options.
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
Dict get_stats(void *mem) const override
Get all statistics.
void sdp_to_socp_init(SDPToSOCPMem &mem) const
SDP to SOCP conversion initialization.
void serialize(SerializingStream &s, const SDPToSOCPMem &m) const
Helper class for Serialization.
void unpack(Sparsity &e)
Reconstruct an object from the input stream.
void version(const std::string &name, int v)
void alloc_iw(size_t sz_iw, bool persistent=false)
Ensure required length of iw field.
bool inputs_check_
Errors are thrown if numerical values of inputs look bad.
void alloc_w(size_t sz_w, bool persistent=false)
Ensure required length of w field.
size_t sz_res() const
Get required length of res field.
size_t sz_iw() const
Get required length of iw field.
casadi_int n_out() const
Get the number of function outputs.
casadi_int n_in() const
Get the number of function inputs.
size_t sz_w() const
Get required length of w field.
size_t sz_arg() const
Get required length of arg field.
bool is_null() const
Is a null pointer?
std::vector< int > sos_ind_
Dict get_stats(void *mem) const override
Get all statistics.
GurobiInterface(const std::string &name, const std::map< std::string, Sparsity > &st)
Create a new Solver.
static Conic * creator(const std::string &name, const std::map< std::string, Sparsity > &st)
Create a new QP Solver.
std::vector< int > sos_types_
void handle_lazy_constraints_callback(GurobiMemory *mem, GRBmodel *model, void *cbdata, int where) const
Handle Lazy Constraints callback events (C API version)
~GurobiInterface() override
Destructor.
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
std::vector< int > sos_beg_
std::vector< char > vtype_
Function lazy_constraints_callback_
User-provided callback function for MIPSOL events.
void init(const Dict &opts) override
Initialize.
Dict opts_
Gurobi options.
static ProtoFunction * deserialize(DeserializingStream &s)
Deserialize with type disambiguation.
SDPToSOCPMem sdp_to_socp_mem_
SDP to SOCP conversion memory.
int solve(const double **arg, double **res, casadi_int *iw, double *w, void *mem) const override
Solve the QP.
static const std::string meta_doc
A documentation string.
static const Options options_
Options.
int init_mem(void *mem) const override
Initalize memory block.
std::vector< double > sos_weights_
const Sparsity & sparsity() const
Const access the sparsity - reference to data member.
static void registerPlugin(const Plugin &plugin, bool needs_lock=true)
Register an integrator in the factory.
bool verbose_
Verbose printout.
void clear_mem()
Clear all memory (called from destructor)
Helper class for Serialization.
void version(const std::string &name, int v)
void pack(const Sparsity &e)
Serializes an object to the output stream.
casadi_int size2() const
Get the number of columns.
const casadi_int * row() const
Get a reference to row-vector,.
const casadi_int * colind() const
Get a reference to the colindex of all column element (see class description)
int CASADI_CONIC_GUROBI_EXPORT casadi_register_conic_gurobi(Conic::Plugin *plugin)
@ CONIC_UBA
dense, (nc x 1)
@ CONIC_A
The matrix A: sparse, (nc x n) - product with x must be dense.
@ CONIC_G
The vector g: dense, (n x 1)
@ CONIC_Q
The matrix Q: sparse symmetric, (np^2 x n)
@ CONIC_LBA
dense, (nc x 1)
@ CONIC_UBX
dense, (n x 1)
@ CONIC_LBX
dense, (n x 1)
@ CONIC_P
The matrix P: sparse symmetric, (np x np)
int to_int(casadi_int rhs)
void flatten_nested_vector(const std::vector< std::vector< T > > &nested, std::vector< S > &flat)
Flatten a nested std::vector tot a single flattened vector.
void casadi_copy(const T1 *x, casadi_int n, T1 *y)
COPY: y <-x.
void casadi_fill(T1 *x, casadi_int n, T1 alpha)
FILL: x <- alpha.
const char * return_status_string(Bonmin::TMINLP::SolverReturn status)
static int gurobi_callback_function(GRBmodel *model, void *cbdata, int where, void *usrdata)
std::string str(const T &v)
String representation, any type.
void check_sos(casadi_int nx, const std::vector< std::vector< T > > &groups, std::vector< std::vector< double > > &weights, std::vector< casadi_int > &types)
Check sos structure and generate defaults.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
const double nan
Not a number.
void casadi_scal(casadi_int n, T1 alpha, T1 *x)
SCAL: x <- alpha*x.
T * get_ptr(std::vector< T > &v)
Get a pointer to the data contained in the vector.
void casadi_clear(T1 *x, casadi_int n)
CLEAR: x <- 0.
char sense_to_gurobi(const std::string &sense)
void CASADI_CONIC_GUROBI_EXPORT casadi_load_conic_gurobi()
const double pi
Define pi.
@ CONIC_X
The primal solution.
@ CONIC_LAM_A
The dual solution corresponding to linear bounds.
@ CONIC_COST
The optimal cost.
@ CONIC_LAM_X
The dual solution corresponding to simple bounds.
SDP to SOCP conversion memory.
std::vector< casadi_int > r
std::vector< casadi_int > A_mapping
std::vector< casadi_int > map_P
~GurobiMemory()
Destructor.
const GurobiInterface * interface
GurobiMemory()
Constructor.
LazyCallbackMemory lazy_cb_mem
std::vector< double > input_data
std::vector< double > a_vec
std::vector< const double * > arg
std::vector< casadi_int > iw
std::vector< double > x_vals
std::vector< double * > res
Options metadata for a class.