From 7013a1dd396c1e4f58afbb82fa03296e176fc942 Mon Sep 17 00:00:00 2001 From: Riley Clement Date: Sat, 13 Jul 2024 00:25:24 +1000 Subject: [PATCH 1/3] Gurobi FLK compatibility --- mip/gurobi.py | 65 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 59 insertions(+), 6 deletions(-) diff --git a/mip/gurobi.py b/mip/gurobi.py index 474b0c3d..0729d2f1 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, @@ -247,6 +253,8 @@ ) GRBloadenv = grblib.GRBloadenv + GRBemptyenv = grblib.GRBemptyenv + GRBstartenv = grblib.GRBstartenv GRBnewmodel = grblib.GRBnewmodel GRBfreeenv = grblib.GRBfreeenv GRBfreemodel = grblib.GRBfreemodel @@ -279,6 +287,7 @@ GRBgetdblattr = grblib.GRBgetdblattr GRBsetdblparam = grblib.GRBsetdblparam GRBgetdblparam = grblib.GRBgetdblparam + GRBsetstrparam = grblib.GRBsetstrparam GRBgetstrattrelement = grblib.GRBgetstrattrelement GRBcbget = grblib.GRBcbget GRBcbsetparam = grblib.GRBcbsetparam @@ -337,6 +346,11 @@ class SolverGurobi(Solver): + + # class dictionary to facilitate setting of Gurobi's Flexible Licensing Key parameters + # which are used in the __init__ method. + _FLK_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 +378,42 @@ 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._FLK_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." + ) + + for key, val in self._FLK_params.items(): + if isinstance(val, str): + st = GRBsetstrparam(self._env[0], key.encode("utf-8"), val.encode("utf-8")) + if st != 0: + raise ParameterNotAvailable( + f"Could not set str parameter: {key}. Check parameter name. Check value type." + ) + else: + st = GRBsetintparam(self._env[0], key.encode("utf-8"), val) + if st != 0: + raise ParameterNotAvailable( + f"Could not set int parameter: {key}. Check parameter name. Check value type." + ) + + st = GRBstartenv(self._env[0]) + if st != 0: + raise InterfacingError( + "Gurobi 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 +1584,15 @@ def var_get_x(self, var: Var): def __del__(self): return + + +def set_flk_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._FLK_params = kwargs class ModelGurobiCB(Model): From d1ab2ab63fc6e1f7e30c5a6e1657a1205afab0ef Mon Sep 17 00:00:00 2001 From: Riley Clement Date: Tue, 16 Jul 2024 02:02:15 +1000 Subject: [PATCH 2/3] Added Gurobi error messages --- mip/gurobi.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mip/gurobi.py b/mip/gurobi.py index 0729d2f1..6e1a832f 100644 --- a/mip/gurobi.py +++ b/mip/gurobi.py @@ -249,6 +249,8 @@ int GRBdelconstrs (GRBmodel *model, int numdel, int *ind); int GRBreset (GRBmodel *model, int clearall); + + char * GRBgeterrormsg (GRBenv *env); """ ) @@ -302,6 +304,7 @@ GRBsetstrattr = grblib.GRBsetstrattr GRBgetdblattrarray = grblib.GRBgetdblattrarray GRBreset = grblib.GRBreset + GRBgeterrormsg = grblib.GRBgeterrormsg GRB_CB_MIPSOL = 4 GRB_CB_MIPNODE = 5 @@ -392,18 +395,21 @@ def __init__(self, model: Model, name: str, sense: str, modelp: CData = ffi.NULL if isinstance(val, str): st = GRBsetstrparam(self._env[0], key.encode("utf-8"), val.encode("utf-8")) if st != 0: + print("GurobiError:", ffi.string(GRBgeterrormsg(self._env[0])).decode("utf-8")) raise ParameterNotAvailable( f"Could not set str parameter: {key}. Check parameter name. Check value type." ) else: st = GRBsetintparam(self._env[0], key.encode("utf-8"), val) if st != 0: + print("GurobiError:", ffi.string(GRBgeterrormsg(self._env[0])).decode("utf-8")) raise ParameterNotAvailable( f"Could not set int parameter: {key}. Check parameter name. Check value type." ) st = GRBstartenv(self._env[0]) if st != 0: + print("GurobiError:", ffi.string(GRBgeterrormsg(self._env[0])).decode("utf-8")) raise InterfacingError( "Gurobi environment could not be started" ) From 9150a28f887c7a575c9de0a48394233bb40f471c Mon Sep 17 00:00:00 2001 From: Riley Clement Date: Tue, 16 Jul 2024 10:18:59 +1000 Subject: [PATCH 3/3] Generalizing FLK params to any environment parameter --- mip/gurobi.py | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/mip/gurobi.py b/mip/gurobi.py index 6e1a832f..ee5bbbf0 100644 --- a/mip/gurobi.py +++ b/mip/gurobi.py @@ -352,7 +352,7 @@ class SolverGurobi(Solver): # class dictionary to facilitate setting of Gurobi's Flexible Licensing Key parameters # which are used in the __init__ method. - _FLK_params = {} + _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, @@ -381,7 +381,7 @@ def __init__(self, model: Model, name: str, sense: str, modelp: CData = ffi.NULL self._ownsModel = True self._env = ffi.new("GRBenv **") - if self._FLK_params: + if self._env_params: # The following is the only way in which environments relying on FLK licenses # can be created. @@ -391,27 +391,35 @@ def __init__(self, model: Model, name: str, sense: str, modelp: CData = ffi.NULL "Could not start (empty) Gurobi environment." ) - for key, val in self._FLK_params.items(): + 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: - print("GurobiError:", ffi.string(GRBgeterrormsg(self._env[0])).decode("utf-8")) - raise ParameterNotAvailable( - f"Could not set str parameter: {key}. Check parameter name. Check value type." - ) - else: + raise _gurobi_param_error("str", key) + elif isinstance(val, int): st = GRBsetintparam(self._env[0], key.encode("utf-8"), val) if st != 0: - print("GurobiError:", ffi.string(GRBgeterrormsg(self._env[0])).decode("utf-8")) - raise ParameterNotAvailable( - f"Could not set int parameter: {key}. Check parameter name. Check value type." - ) + 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: - print("GurobiError:", ffi.string(GRBgeterrormsg(self._env[0])).decode("utf-8")) + gurobi_err_msg = ffi.string(GRBgeterrormsg(self._env[0])).decode("utf-8") raise InterfacingError( - "Gurobi environment could not be started" + f"GurobiError: {gurobi_err_msg}.\nGurobi environment could not be started" ) else: # creating Gurobi environment @@ -1592,13 +1600,13 @@ def __del__(self): return -def set_flk_params(**kwargs): +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._FLK_params = kwargs + SolverGurobi._env_params = kwargs class ModelGurobiCB(Model):