diff --git a/mip/gurobi.py b/mip/gurobi.py index 474b0c3d..ee5bbbf0 100644 --- a/mip/gurobi.py +++ b/mip/gurobi.py @@ -112,6 +112,10 @@ int GRBloadenv(GRBenv **envP, const char *logfilename); + int GRBemptyenv (GRBenv **envP); + + int GRBstartenv (GRBenv *env); + int GRBnewmodel(GRBenv *env, GRBmodel **modelP, const char *Pname, int numvars, double *obj, double *lb, double *ub, char *vtype, @@ -180,6 +184,8 @@ int GRBsetdblparam(GRBenv *env, const char *paramname, double value); + int GRBsetstrparam(GRBenv *env, const char *paramname, const char *newvalue); + int GRBsetobjectiven(GRBmodel *model, int index, int priority, double weight, double abstol, double reltol, const char *name, @@ -243,10 +249,14 @@ int GRBdelconstrs (GRBmodel *model, int numdel, int *ind); int GRBreset (GRBmodel *model, int clearall); + + char * GRBgeterrormsg (GRBenv *env); """ ) GRBloadenv = grblib.GRBloadenv + GRBemptyenv = grblib.GRBemptyenv + GRBstartenv = grblib.GRBstartenv GRBnewmodel = grblib.GRBnewmodel GRBfreeenv = grblib.GRBfreeenv GRBfreemodel = grblib.GRBfreemodel @@ -279,6 +289,7 @@ GRBgetdblattr = grblib.GRBgetdblattr GRBsetdblparam = grblib.GRBsetdblparam GRBgetdblparam = grblib.GRBgetdblparam + GRBsetstrparam = grblib.GRBsetstrparam GRBgetstrattrelement = grblib.GRBgetstrattrelement GRBcbget = grblib.GRBcbget GRBcbsetparam = grblib.GRBcbsetparam @@ -293,6 +304,7 @@ GRBsetstrattr = grblib.GRBsetstrattr GRBgetdblattrarray = grblib.GRBgetdblattrarray GRBreset = grblib.GRBreset + GRBgeterrormsg = grblib.GRBgeterrormsg GRB_CB_MIPSOL = 4 GRB_CB_MIPNODE = 5 @@ -337,6 +349,11 @@ class SolverGurobi(Solver): + + # class dictionary to facilitate setting of Gurobi's Flexible Licensing Key parameters + # which are used in the __init__ method. + _env_params = {} + def __init__(self, model: Model, name: str, sense: str, modelp: CData = ffi.NULL): """modelp should be informed if a model should not be created, but only allow access to an existing one""" @@ -364,12 +381,53 @@ def __init__(self, model: Model, name: str, sense: str, modelp: CData = ffi.NULL self._ownsModel = True self._env = ffi.new("GRBenv **") - # creating Gurobi environment - st = GRBloadenv(self._env, "".encode("utf-8")) - if st != 0: - raise InterfacingError( - "Gurobi environment could not be loaded, check your license." - ) + if self._env_params: + # The following is the only way in which environments relying on FLK licenses + # can be created. + + st = GRBemptyenv(self._env) + if st != 0: + raise InterfacingError( + "Could not start (empty) Gurobi environment." + ) + + def _gurobi_param_error(param_type, key): + gurobi_err_msg = ffi.string(GRBgeterrormsg(self._env[0])).decode("utf-8") + return ParameterNotAvailable( + f"GurobiError: {gurobi_err_msg}.\nCould not set {param_type} parameter: {key}. Check parameter name. Check value type." + ) + + for key, val in self._env_params.items(): + if isinstance(val, str): + st = GRBsetstrparam(self._env[0], key.encode("utf-8"), val.encode("utf-8")) + if st != 0: + raise _gurobi_param_error("str", key) + elif isinstance(val, int): + st = GRBsetintparam(self._env[0], key.encode("utf-8"), val) + if st != 0: + raise _gurobi_param_error("int", key) + elif isinstance(val, float): + st = GRBsetintparam(self._env[0], key.encode("utf-8"), val) + if st != 0: + raise _gurobi_param_error("float", key) + else: + raise ParameterNotAvailable( + f"Unrecognized type ({type(val)}) for parameter value {key}. Supported types: str, int, float." + ) + + st = GRBstartenv(self._env[0]) + if st != 0: + gurobi_err_msg = ffi.string(GRBgeterrormsg(self._env[0])).decode("utf-8") + raise InterfacingError( + f"GurobiError: {gurobi_err_msg}.\nGurobi environment could not be started" + ) + else: + # creating Gurobi environment + st = GRBloadenv(self._env, "".encode("utf-8")) + if st != 0: + raise InterfacingError( + "Gurobi environment could not be loaded, check your license." + ) self._env = self._env[0] # creating Gurobi model @@ -1540,6 +1598,15 @@ def var_get_x(self, var: Var): def __del__(self): return + + +def set_env_params(**kwargs): + # Gurobi offers a licensing method called FLK (Flexible Licensing Key) + # Previously this was known as ISV. This license requires parameters + # to be set on an empty Gurobi environment. Gurobi users with FLK + # licenses will be instructed to use this method in order to be able + # to use their license. + SolverGurobi._env_params = kwargs class ModelGurobiCB(Model):