Path: blob/master/sage/numerical/backends/gurobi_backend.pyx
4097 views
"""1Gurobi Backend23AUTHORS:45- Nathann Cohen (2011-10): initial implementation67TESTS:89Bug from :trac:`12833`::1011sage: g = DiGraph('IESK@XgAbCgH??KG??')12sage: g.feedback_edge_set(value_only = True, constraint_generation = False)13714sage: g.feedback_edge_set(value_only = True, constraint_generation = False, solver = "Gurobi") # optional - Gurobi1571617Methods18-------19"""2021##############################################################################22# Copyright (C) 2010 Nathann Cohen <[email protected]>23# Distributed under the terms of the GNU General Public License (GPL)24# The full text of the GPL is available at:25# http://www.gnu.org/licenses/26##############################################################################2728from sage.numerical.mip import MIPSolverException2930cdef class GurobiBackend(GenericBackend):31def __init__(self, maximization = True):32"""33Constructor3435EXAMPLE::3637sage: p = MixedIntegerLinearProgram(solver="Gurobi") # optional - Gurobi38"""39cdef int error4041cdef GRBenv ** env42env = <GRBenv **> sage_malloc(sizeof(GRBenv *))4344error = GRBloadenv(env, NULL)4546check(self.env, error)4748if env[0] == NULL:49raise Exception("Could not initialize Gurobi environment")5051self.model = <GRBmodel **> sage_malloc(sizeof(GRBmodel *))5253error = GRBnewmodel(env[0], self.model, NULL, 0, NULL, NULL, NULL, NULL, NULL)5455self.env = GRBgetenv (self.model[0])5657if error:58raise Exception("Could not initialize Gurobi model")5960if maximization:61error = GRBsetintattr(self.model[0], "ModelSense", -1)62else:63error = GRBsetintattr(self.model[0], "ModelSense", +1)6465check(self.env, error)6667self.set_sense(1 if maximization else -1)6869self.set_verbosity(0)70self.obj_constant_term = 0.071727374cpdef int add_variable(self, lower_bound=0.0, upper_bound=None, binary=False, continuous=False, integer=False, obj=0.0, name=None) except -1:75"""76Add a variable.7778This amounts to adding a new column to the matrix. By default,79the variable is both positive, real and the coefficient in the80objective function is 0.0.8182INPUT:8384- ``lower_bound`` - the lower bound of the variable (default: 0)8586- ``upper_bound`` - the upper bound of the variable (default: ``None``)8788- ``binary`` - ``True`` if the variable is binary (default: ``False``).8990- ``continuous`` - ``True`` if the variable is binary (default: ``True``).9192- ``integer`` - ``True`` if the variable is binary (default: ``False``).9394- ``obj`` - (optional) coefficient of this variable in the objective function (default: 0.0)9596- ``name`` - an optional name for the newly added variable (default: ``None``).9798OUTPUT: The index of the newly created variable99100EXAMPLE::101102sage: from sage.numerical.backends.generic_backend import get_solver # optional - Gurobi103sage: p = get_solver(solver = "Gurobi") # optional - Gurobi104sage: p.ncols() # optional - Gurobi1050106sage: p.add_variable() # optional - Gurobi1070108sage: p.ncols() # optional - Gurobi1091110sage: p.add_variable(binary=True) # optional - Gurobi1111112sage: p.add_variable(lower_bound=-2.0, integer=True) # optional - Gurobi1132114sage: p.add_variable(continuous=True, integer=True) # optional - Gurobi115Traceback (most recent call last):116...117ValueError: ...118sage: p.add_variable(name='x',obj=1.0) # optional - Gurobi1193120sage: p.col_name(3) # optional - Gurobi121'x'122sage: p.objective_coefficient(3) # optional - Gurobi1231.0124"""125# Checking the input126cdef char vtype = int(bool(binary)) + int(bool(continuous)) + int(bool(integer))127if vtype == 0:128continuous = True129elif vtype != 1:130raise ValueError("Exactly one parameter of 'binary', 'integer' and 'continuous' must be 'True'.")131132cdef int error133134if binary:135vtype = GRB_BINARY136elif continuous:137vtype = GRB_CONTINUOUS138elif integer:139vtype = GRB_INTEGER140141cdef char * c_name = ""142143if name is None:144name = "x_"+str(self.ncols())145146c_name = name147148if upper_bound is None:149upper_bound = GRB_INFINITY150if lower_bound is None:151lower_bound = -GRB_INFINITY152153154error = GRBaddvar(self.model[0], 0, NULL, NULL, obj, <double> lower_bound, <double> upper_bound, vtype, c_name)155156check(self.env,error)157158check(self.env,GRBupdatemodel(self.model[0]))159160return self.ncols()-1161162cpdef int add_variables(self, int number, lower_bound=0.0, upper_bound=None, binary=False, continuous=False, integer=False, obj=0.0, names=None) except -1:163"""164Add ``number`` new variables.165166This amounts to adding new columns to the matrix. By default,167the variables are both positive, real and theor coefficient in168the objective function is 0.0.169170INPUT:171172- ``n`` - the number of new variables (must be > 0)173174- ``lower_bound`` - the lower bound of the variable (default: 0)175176- ``upper_bound`` - the upper bound of the variable (default: ``None``)177178- ``binary`` - ``True`` if the variable is binary (default: ``False``).179180- ``continuous`` - ``True`` if the variable is binary (default: ``True``).181182- ``integer`` - ``True`` if the variable is binary (default: ``False``).183184- ``obj`` - (optional) coefficient of all variables in the objective function (default: 0.0)185186- ``names`` - optional list of names (default: ``None``)187188OUTPUT: The index of the variable created last.189190EXAMPLE::191192sage: from sage.numerical.backends.generic_backend import get_solver # optional - Gurobi193sage: p = get_solver(solver = "Gurobi") # optional - Gurobi194sage: p.ncols() # optional - Gurobi1950196sage: p.add_variables(5) # optional - Gurobi1974198sage: p.ncols() # optional - Gurobi1995200sage: p.add_variables(2, lower_bound=-2.0, integer=True, names=['a','b']) # optional - Gurobi2016202"""203cdef int i204cdef int value205for i in range(number):206value = self.add_variable(lower_bound = lower_bound,207upper_bound = upper_bound,208binary = binary,209continuous = continuous,210integer = integer,211obj = obj,212name = None if names is None else names[i])213return value214# cdef double * p_obj = NULL215# cdef double * p_lb = NULL216# cdef double * p_ub = NULL217# cdef char ** p_names = NULL218# cdef char * p_types = NULL219# cdef int i220# cdef int error221#222# cdef int NAME_MAX_LEN = 50223#224# # Objective coefficients225# if obj:226# p_obj = <double*> sage_malloc(number*sizeof(double))227# for i in range(number):228# p_obj[i] = obj229#230# # Lower bounds231# if lower_bound != 0:232# p_lb = <double*> sage_malloc(number*sizeof(double))233# for i in range(number):234# p_lb[i] = -GRB_INFINITY if lower_bound is None else lower_bound235#236# # Upper bounds237# if not upper_bound is None:238# p_ub = <double*> sage_malloc(number*sizeof(double))239# for i in range(number):240# p_ub[i] = upper_bound241#242# # Type243# if not continuous:244# p_types = <char *> sage_malloc(number*sizeof(char))245# if binary:246# p_types[0] = GRB_BINARY247# else:248# p_types[0] = GRB_INTEGER249#250# for i in range(2, number):251# p_types[i] = p_types[i-1]252#253# # Names254# if not names is None:255# p_names = <char **> sage_malloc((number+1)*sizeof(char *))256# p_names[0] = <char *> sage_malloc(number*NAME_MAX_LEN*sizeof(char))257# for i, name in enumerate(names):258# p_names[i+1] = p_names[i] + NAME_MAX_LEN259# p_names[i][0] = str(name)260#261# error = GRBaddvars (self.model[0], number, 0, NULL, NULL, NULL, p_obj, p_lb, p_ub, p_types, p_names)262#263# # Freeing the memory264# if p_obj != NULL:265# sage_free(p_obj)266# if p_lb != NULL:267# sage_free(p_lb)268# if p_ub != NULL:269# sage_free(p_ub)270# if p_names != NULL:271# for i in range(number+1):272# sage_free(p_names[i])273# sage_free(p_names)274# if p_types != NULL:275# sage_free(p_types)276#277# check(self.env, error)278279cpdef set_variable_type(self, int variable, int vtype):280"""281Set the type of a variable282283INPUT:284285- ``variable`` (integer) -- the variable's id286287- ``vtype`` (integer) :288289* 1 Integer290* 0 Binary291* -1 Real292293EXAMPLE::294295sage: from sage.numerical.backends.generic_backend import get_solver # optional - Gurobi296sage: p = get_solver(solver = "Gurobi") # optional - Gurobi297sage: p.ncols() # optional - Gurobi2980299sage: p.add_variable() # optional - Gurobi3000301sage: p.set_variable_type(0,1) # optional - Gurobi302sage: p.is_variable_integer(0) # optional - Gurobi303True304"""305cdef int error306307if vtype == 1:308error = GRBsetcharattrelement(self.model[0], "VType", variable, 'I')309elif vtype == 0:310error = GRBsetcharattrelement(self.model[0], "VType", variable, 'B')311else:312error = GRBsetcharattrelement(self.model[0], "VType", variable, 'C')313check(self.env, error)314315check(self.env,GRBupdatemodel(self.model[0]))316317cpdef set_sense(self, int sense):318"""319Set the direction (maximization/minimization).320321INPUT:322323- ``sense`` (integer) :324325* +1 => Maximization326* -1 => Minimization327328EXAMPLE::329330sage: from sage.numerical.backends.generic_backend import get_solver # optional - Gurobi331sage: p = get_solver(solver = "Gurobi") # optional - Gurobi332sage: p.is_maximization() # optional - Gurobi333True334sage: p.set_sense(-1) # optional - Gurobi335sage: p.is_maximization() # optional - Gurobi336False337"""338cdef int error339340if sense == 1:341error = GRBsetintattr(self.model[0], "ModelSense", -1)342else:343error = GRBsetintattr(self.model[0], "ModelSense", +1)344345check(self.env, error)346check(self.env,GRBupdatemodel(self.model[0]))347348cpdef objective_coefficient(self, int variable, coeff=None):349"""350Set or get the coefficient of a variable in the objective function351352INPUT:353354- ``variable`` (integer) -- the variable's id355356- ``coeff`` (double) -- its coefficient or ``None`` for357reading (default: ``None``)358359EXAMPLE::360361sage: from sage.numerical.backends.generic_backend import get_solver # optional - Gurobi362sage: p = get_solver(solver = "Gurobi") # optional - Gurobi363sage: p.add_variable() # optional - Gurobi3640365sage: p.objective_coefficient(0) == 0 # optional - Gurobi366True367sage: p.objective_coefficient(0,2) # optional - Gurobi368sage: p.objective_coefficient(0) # optional - Gurobi3692.0370"""371cdef int error372cdef double value[1]373374if coeff:375error = GRBsetdblattrelement(self.model[0], "Obj", variable, coeff)376check(self.env, error)377check(self.env,GRBupdatemodel(self.model[0]))378else:379error = GRBgetdblattrelement(self.model[0], "Obj", variable, value)380check(self.env, error)381return value[0]382383cpdef problem_name(self, char * name = NULL):384"""385Return or define the problem's name386387INPUT:388389- ``name`` (``char *``) -- the problem's name. When set to390``NULL`` (default), the method returns the problem's name.391392EXAMPLE::393394sage: from sage.numerical.backends.generic_backend import get_solver # optional - Gurobi395sage: p = get_solver(solver = "Gurobi") # optional - Gurobi396sage: p.problem_name("There once was a french fry") # optional - Gurobi397sage: print p.problem_name() # optional - Gurobi398There once was a french fry399400TESTS::401402sage: from sage.numerical.backends.generic_backend import get_solver # optional - Gurobi403sage: p = get_solver(solver = "Gurobi") # optional - Gurobi404sage: print p.problem_name() # optional - Gurobi405"""406cdef int error407cdef char * pp_name[1]408409if name:410error = GRBsetstrattr(self.model[0], "ModelName", name)411check(self.env, error)412check(self.env,GRBupdatemodel(self.model[0]))413414else:415check(self.env,GRBgetstrattr(self.model[0], "ModelName", <char **> pp_name))416if pp_name[0] == NULL:417value = ""418else:419value = str(pp_name[0])420421return value422423cpdef set_objective(self, list coeff, double d = 0.0):424"""425Set the objective function.426427INPUT:428429- ``coeff`` - a list of real values, whose ith element is the430coefficient of the ith variable in the objective function.431432- ``d`` (double) -- the constant term in the linear function (set to `0` by default)433434EXAMPLE::435436sage: from sage.numerical.backends.generic_backend import get_solver # optional - Gurobi437sage: p = get_solver(solver = "Gurobi") # optional - Gurobi438sage: p.add_variables(5) # optional - Gurobi4394440sage: p.set_objective([1, 1, 2, 1, 3]) # optional - Gurobi441sage: map(lambda x :p.objective_coefficient(x), range(5)) # optional - Gurobi442[1.0, 1.0, 2.0, 1.0, 3.0]443444Constants in the objective function are respected::445446sage: p = MixedIntegerLinearProgram(solver='Gurobi')# optional - Gurobi447sage: x,y = p[0], p[1] # optional - Gurobi448sage: p.add_constraint(2*x + 3*y, max = 6) # optional - Gurobi449sage: p.add_constraint(3*x + 2*y, max = 6) # optional - Gurobi450sage: p.set_objective(x + y + 7) # optional - Gurobi451sage: p.set_integer(x); p.set_integer(y) # optional - Gurobi452sage: p.solve() # optional - Gurobi4539.0454"""455cdef int i = 0456cdef double value457cdef int error458459for value in coeff:460error = GRBsetdblattrelement (self.model[0], "Obj", i, value)461check(self.env,error)462i += 1463464check(self.env,GRBupdatemodel(self.model[0]))465466self.obj_constant_term = d467468cpdef set_verbosity(self, int level):469"""470Set the verbosity level471472INPUT:473474- ``level`` (integer) -- From 0 (no verbosity) to 3.475476EXAMPLE::477478sage: from sage.numerical.backends.generic_backend import get_solver # optional - Gurobi479sage: p = get_solver(solver = "Gurobi") # optional - Gurobi480sage: p.set_verbosity(2) # optional - Gurobi481482"""483cdef int error484if level:485error = GRBsetintparam(self.env, "OutputFlag", 1)486else:487error = GRBsetintparam(self.env, "OutputFlag", 0)488489check(self.env, error)490491cpdef remove_constraint(self, int i):492r"""493Remove a constraint from self.494495INPUT:496497- ``i`` -- index of the constraint to remove498499EXAMPLE::500501sage: p = MixedIntegerLinearProgram(solver='Gurobi')# optional - Gurobi502sage: x,y = p[0], p[1] # optional - Gurobi503sage: p.add_constraint(2*x + 3*y, max = 6) # optional - Gurobi504sage: p.add_constraint(3*x + 2*y, max = 6) # optional - Gurobi505sage: p.set_objective(x + y + 7) # optional - Gurobi506sage: p.set_integer(x); p.set_integer(y) # optional - Gurobi507sage: p.solve() # optional - Gurobi5089.0509sage: p.remove_constraint(0) # optional - Gurobi510sage: p.solve() # optional - Gurobi51110.0512sage: p.get_values([x,y]) # optional - Gurobi513[0.0, 3.0]514"""515cdef int ind[1]516ind[0] = i517cdef int error518error = GRBdelconstrs (self.model[0], 1, ind )519check(self.env, error)520521error = GRBupdatemodel(self.model[0])522check(self.env,error)523524cpdef add_linear_constraint(self, coefficients, lower_bound, upper_bound, name=None):525"""526Add a linear constraint.527528INPUT:529530- ``coefficients`` an iterable with ``(c,v)`` pairs where ``c``531is a variable index (integer) and ``v`` is a value (real532value).533534- ``lower_bound`` - a lower bound, either a real value or ``None``535536- ``upper_bound`` - an upper bound, either a real value or ``None``537538- ``name`` - an optional name for this row (default: ``None``)539540EXAMPLE::541542sage: from sage.numerical.backends.generic_backend import get_solver # optional - Gurobi543sage: p = get_solver(solver = "Gurobi") # optional - Gurobi544sage: p.add_variables(5) # optional - Gurobi5454546sage: p.add_linear_constraint( zip(range(5), range(5)), 2.0, 2.0) # optional - Gurobi547sage: p.row(0) # optional - Gurobi548([0, 1, 2, 3, 4], [0.0, 1.0, 2.0, 3.0, 4.0])549sage: p.row_bounds(0) # optional - Gurobi550(2.0, 2.0)551sage: p.add_linear_constraint( zip(range(5), range(5)), 1.0, 1.0, name='foo') # optional - Gurobi552sage: p.row_name(1) # optional - Gurobi553'foo'554"""555556if lower_bound is None and upper_bound is None:557raise ValueError("At least one of 'upper_bound' or 'lower_bound' must be set.")558559cdef int * row_i560cdef double * row_values561562row_i = <int *> sage_malloc((len(coefficients)) * sizeof(int))563row_values = <double *> sage_malloc((len(coefficients)) * sizeof(double))564565566for i,(c,v) in enumerate(coefficients):567row_i[i] = c568row_values[i] = v569570if name is None:571name = ""572573if upper_bound is not None and lower_bound is None:574error = GRBaddconstr(self.model[0], len(coefficients), row_i, row_values, GRB_LESS_EQUAL, <double> upper_bound, name)575576elif lower_bound is not None and upper_bound is None:577error = GRBaddconstr(self.model[0], len(coefficients), row_i, row_values, GRB_GREATER_EQUAL, <double> lower_bound, name)578579elif upper_bound is not None and lower_bound is not None:580if lower_bound == upper_bound:581error = GRBaddconstr(self.model[0], len(coefficients), row_i, row_values, GRB_EQUAL, <double> lower_bound, name)582583else:584error = GRBaddrangeconstr(self.model[0], len(coefficients), row_i, row_values, <double> lower_bound, <double> upper_bound, name)585586check(self.env,error)587588error = GRBupdatemodel(self.model[0])589590check(self.env,error)591592sage_free(row_i)593sage_free(row_values)594595cpdef row(self, int index):596r"""597Return a row598599INPUT:600601- ``index`` (integer) -- the constraint's id.602603OUTPUT:604605A pair ``(indices, coeffs)`` where ``indices`` lists the606entries whose coefficient is nonzero, and to which ``coeffs``607associates their coefficient on the model of the608``add_linear_constraint`` method.609610EXAMPLE::611612sage: from sage.numerical.backends.generic_backend import get_solver # optional - Gurobi613sage: p = get_solver(solver = "Gurobi") # optional - Gurobi614sage: p.add_variables(5) # optional - Gurobi6154616sage: p.add_linear_constraint(zip(range(5), range(5)), 2, 2) # optional - Gurobi617sage: p.row(0) # optional - Gurobi618([0, 1, 2, 3, 4], [0.0, 1.0, 2.0, 3.0, 4.0])619sage: p.row_bounds(0) # optional - Gurobi620(2.0, 2.0)621"""622cdef int error623cdef int fake[1]624625cdef int length[1]626error = GRBgetconstrs(self.model[0], length, NULL, NULL, NULL, index, 1 )627check(self.env,error)628629cdef int * p_indices = <int *> sage_malloc(length[0] * sizeof(int))630cdef double * p_values = <double *> sage_malloc(length[0] * sizeof(double))631632error = GRBgetconstrs(self.model[0], length, <int *> fake, p_indices, p_values, index, 1 )633check(self.env,error)634635cdef list indices = []636cdef list values = []637638cdef int i639for i in range(length[0]):640indices.append(p_indices[i])641values.append(p_values[i])642643sage_free(p_indices)644sage_free(p_values)645646return indices, values647648649cpdef row_bounds(self, int index):650"""651Return the bounds of a specific constraint.652653INPUT:654655- ``index`` (integer) -- the constraint's id.656657OUTPUT:658659A pair ``(lower_bound, upper_bound)``. Each of them can be set660to ``None`` if the constraint is not bounded in the661corresponding direction, and is a real value otherwise.662663EXAMPLE::664665sage: from sage.numerical.backends.generic_backend import get_solver # optional - Gurobi666sage: p = get_solver(solver = "Gurobi") # optional - Gurobi667sage: p.add_variables(5) # optional - Gurobi6684669sage: p.add_linear_constraint(zip(range(5), range(5)), 2, 2) # optional - Gurobi670sage: p.row(0) # optional - Gurobi671([0, 1, 2, 3, 4], [0.0, 1.0, 2.0, 3.0, 4.0])672sage: p.row_bounds(0) # optional - Gurobi673(2.0, 2.0)674"""675cdef double d[1]676cdef char sense[1]677cdef int error678679error = GRBgetcharattrelement(self.model[0], "Sense", index, <char *> sense)680check(self.env, error)681682error = GRBgetdblattrelement(self.model[0], "RHS", index, <double *> d)683check(self.env, error)684685if sense[0] == '>':686return (d[0], None)687elif sense[0] == '<':688return (None, d[0])689else:690return (d[0],d[0])691692cpdef col_bounds(self, int index):693"""694Return the bounds of a specific variable.695696INPUT:697698- ``index`` (integer) -- the variable's id.699700OUTPUT:701702A pair ``(lower_bound, upper_bound)``. Each of them can be set703to ``None`` if the variable is not bounded in the704corresponding direction, and is a real value otherwise.705706EXAMPLE::707708sage: from sage.numerical.backends.generic_backend import get_solver # optional - Gurobi709sage: p = get_solver(solver = "Gurobi") # optional - Gurobi710sage: p.add_variable() # optional - Gurobi7110712sage: p.col_bounds(0) # optional - Gurobi713(0.0, None)714sage: p.variable_upper_bound(0, 5) # optional - Gurobi715sage: p.col_bounds(0) # optional - Gurobi716(0.0, 5.0)717"""718719cdef double lb[1], ub[1]720721error = GRBgetdblattrelement(self.model[0], "LB", index, <double *> lb)722check(self.env, error)723724error = GRBgetdblattrelement(self.model[0], "UB", index, <double *> ub)725check(self.env, error)726727return (None if lb[0] <= -2147483647 else lb[0],728None if ub[0] >= 2147483647 else ub[0])729730cpdef int solve(self) except -1:731"""732Solve the problem.733734.. NOTE::735736This method raises ``MIPSolverException`` exceptions when737the solution can not be computed for any reason (none738exists, or the LP solver was not able to find it, etc...)739740EXAMPLE::741742sage: from sage.numerical.backends.generic_backend import get_solver # optional - Gurobi743sage: p = get_solver(solver = "Gurobi") # optional - Gurobi744sage: p.add_variables(5) # optional - Gurobi7454746sage: p.add_linear_constraint([(0,1), (1, 1)], 1.2, 1.7) # optional - Gurobi747sage: p.set_variable_type(0, 1) # optional - Gurobi748sage: p.set_variable_type(1, 1) # optional - Gurobi749sage: p.solve() # optional - Gurobi750Traceback (most recent call last):751...752MIPSolverException: 'Gurobi: The problem is infeasible'753"""754cdef int error755global mip_status756757check(self.env, GRBoptimize(self.model[0]))758759cdef int status[1]760check(self.env, GRBgetintattr(self.model[0], "Status", <int *> status))761762# Has there been a problem ?763if status[0] != GRB_OPTIMAL:764raise MIPSolverException("Gurobi: "+mip_status.get(status[0], "unknown error during call to GRBoptimize : "+str(status[0])))765766767cpdef double get_objective_value(self):768"""769Returns the value of the objective function.770771.. NOTE::772773Behaviour is undefined unless ``solve`` has been called before.774775EXAMPLE::776777sage: from sage.numerical.backends.generic_backend import get_solver # optional - Gurobi778sage: p = get_solver(solver = "Gurobi") # optional - Gurobi779sage: p.add_variables(2) # optional - Gurobi7801781sage: p.add_linear_constraint([[0, 1], [1, 2]], None, 3) # optional - Gurobi782sage: p.set_objective([2, 5]) # optional - Gurobi783sage: p.solve() # optional - Gurobi7840785sage: p.get_objective_value() # optional - Gurobi7867.5787sage: p.get_variable_value(0) # optional - Gurobi7880.0789sage: p.get_variable_value(1) # optional - Gurobi7901.5791"""792cdef double p_value[1]793794check(self.env,GRBgetdblattr(self.model[0], "ObjVal", <double* >p_value))795796return p_value[0] + self.obj_constant_term797798cpdef double get_variable_value(self, int variable):799"""800Returns the value of a variable given by the solver.801802.. NOTE::803804Behaviour is undefined unless ``solve`` has been called before.805806EXAMPLE::807808sage: from sage.numerical.backends.generic_backend import get_solver # optional - Gurobi809sage: p = get_solver(solver = "Gurobi") # optional - Gurobi810sage: p.add_variables(2) # optional - Gurobi8111812sage: p.add_linear_constraint([[0, 1], [1, 2]], None, 3) # optional - Gurobi813sage: p.set_objective([2, 5]) # optional - Gurobi814sage: p.solve() # optional - Gurobi8150816sage: p.get_objective_value() # optional - Gurobi8177.5818sage: p.get_variable_value(0) # optional - Gurobi8190.0820sage: p.get_variable_value(1) # optional - Gurobi8211.5822"""823824cdef double value[1]825check(self.env,GRBgetdblattrelement(self.model[0], "X", variable, value))826return round(value[0]) if self.is_variable_binary(variable) else value[0]827828cpdef int ncols(self):829"""830Return the number of columns/variables.831832EXAMPLE::833834sage: from sage.numerical.backends.generic_backend import get_solver # optional - Gurobi835sage: p = get_solver(solver = "Gurobi") # optional - Gurobi836sage: p.ncols() # optional - Gurobi8370838sage: p.add_variables(2) # optional - Gurobi8391840sage: p.ncols() # optional - Gurobi8412842"""843cdef int i[1]844check(self.env,GRBgetintattr(self.model[0], "NumVars", i))845return i[0]846847cpdef int nrows(self):848"""849Return the number of rows/constraints.850851EXAMPLE::852853sage: from sage.numerical.backends.generic_backend import get_solver # optional - Gurobi854sage: p = get_solver(solver = "Gurobi") # optional - Gurobi855sage: p.nrows() # optional - Gurobi8560857sage: p.add_linear_constraint([], 2, None) # optional - Gurobi858sage: p.add_linear_constraint([], 2, None) # optional - Gurobi859sage: p.nrows() # optional - Gurobi8602861"""862cdef int i[1]863check(self.env,GRBgetintattr(self.model[0], "NumConstrs", i))864return i[0]865866cpdef col_name(self, int index):867"""868Return the ``index`` th col name869870INPUT:871872- ``index`` (integer) -- the col's id873874EXAMPLE::875876sage: from sage.numerical.backends.generic_backend import get_solver # optional - Gurobi877sage: p = get_solver(solver = "Gurobi") # optional - Gurobi878sage: p.add_variable(name='I am a variable') # optional - Gurobi8790880sage: p.col_name(0) # optional - Gurobi881'I am a variable'882"""883cdef char * name[1]884check(self.env,GRBgetstrattrelement(self.model[0], "VarName", index, <char **> name))885if name[0] == NULL:886value = ""887else:888value = str(name[0])889return value890891cpdef row_name(self, int index):892"""893Return the ``index`` th row name894895INPUT:896897- ``index`` (integer) -- the row's id898899EXAMPLE::900901sage: from sage.numerical.backends.generic_backend import get_solver # optional - Gurobi902sage: p = get_solver(solver = "Gurobi") # optional - Gurobi903sage: p.add_linear_constraint([], 2, None, name='Empty constraint 1') # optional - Gurobi904sage: p.row_name(0) # optional - Gurobi905'Empty constraint 1'906"""907cdef char * name[1]908check(self.env,GRBgetstrattrelement(self.model[0], "ConstrName", index, <char **> name))909if name[0] == NULL:910value = ""911else:912value = str(name[0])913return value914915cpdef bint is_variable_binary(self, int index):916"""917Test whether the given variable is of binary type.918919INPUT:920921- ``index`` (integer) -- the variable's id922923EXAMPLE::924925sage: from sage.numerical.backends.generic_backend import get_solver # optional - Gurobi926sage: p = get_solver(solver = "Gurobi") # optional - Gurobi927sage: p.ncols() # optional - Gurobi9280929sage: p.add_variable() # optional - Gurobi9300931sage: p.set_variable_type(0,0) # optional - Gurobi932sage: p.is_variable_binary(0) # optional - Gurobi933True934"""935cdef char vtype[1]936check(self.env, GRBgetcharattrelement(self.model[0], "VType", index, <char *> vtype))937return vtype[0] == 'B'938939940cpdef bint is_variable_integer(self, int index):941"""942Test whether the given variable is of integer type.943944INPUT:945946- ``index`` (integer) -- the variable's id947948EXAMPLE::949950sage: from sage.numerical.backends.generic_backend import get_solver # optional - Gurobi951sage: p = get_solver(solver = "Gurobi") # optional - Gurobi952sage: p.ncols() # optional - Gurobi9530954sage: p.add_variable() # optional - Gurobi9550956sage: p.set_variable_type(0,1) # optional - Gurobi957sage: p.is_variable_integer(0) # optional - Gurobi958True959"""960cdef char vtype[1]961check(self.env, GRBgetcharattrelement(self.model[0], "VType", index, <char *> vtype))962return vtype[0] == 'I'963964cpdef bint is_variable_continuous(self, int index):965"""966Test whether the given variable is of continuous/real type.967968INPUT:969970- ``index`` (integer) -- the variable's id971972EXAMPLE::973974sage: from sage.numerical.backends.generic_backend import get_solver # optional - Gurobi975sage: p = get_solver(solver = "Gurobi") # optional - Gurobi976sage: p.ncols() # optional - Gurobi9770978sage: p.add_variable() # optional - Gurobi9790980sage: p.is_variable_continuous(0) # optional - Gurobi981True982sage: p.set_variable_type(0,1) # optional - Gurobi983sage: p.is_variable_continuous(0) # optional - Gurobi984False985986"""987cdef char vtype[1]988check(self.env, GRBgetcharattrelement(self.model[0], "VType", index, <char *> vtype))989return vtype[0] == 'C'990991cpdef bint is_maximization(self):992"""993Test whether the problem is a maximization994995EXAMPLE::996997sage: from sage.numerical.backends.generic_backend import get_solver # optional - Gurobi998sage: p = get_solver(solver = "Gurobi") # optional - Gurobi999sage: p.is_maximization() # optional - Gurobi1000True1001sage: p.set_sense(-1) # optional - Gurobi1002sage: p.is_maximization() # optional - Gurobi1003False1004"""1005cdef int sense[1]1006check(self.env,GRBgetintattr(self.model[0], "ModelSense", <int *> sense))1007return sense[0] == -110081009cpdef variable_upper_bound(self, int index, value = False):1010"""1011Return or define the upper bound on a variable10121013INPUT:10141015- ``index`` (integer) -- the variable's id10161017- ``value`` -- real value, or ``None`` to mean that the1018variable has not upper bound. When set to ``False``1019(default), the method returns the current value.10201021EXAMPLE::10221023sage: from sage.numerical.backends.generic_backend import get_solver # optional - Gurobi1024sage: p = get_solver(solver = "Gurobi") # optional - Gurobi1025sage: p.add_variable() # optional - Gurobi102601027sage: p.col_bounds(0) # optional - Gurobi1028(0.0, None)1029sage: p.variable_upper_bound(0, 5) # optional - Gurobi1030sage: p.col_bounds(0) # optional - Gurobi1031(0.0, 5.0)1032"""1033cdef double b[1]10341035if not value is False:1036check(self.env, GRBsetdblattrelement(1037self.model[0], "UB",1038index,1039value if value is not None else GRB_INFINITY))10401041check(self.env,GRBupdatemodel(self.model[0]))1042else:1043error = GRBgetdblattrelement(self.model[0], "UB", index, <double *> b)1044check(self.env, error)1045return None if b[0] >= 2147483647 else b[0]10461047cpdef variable_lower_bound(self, int index, value = False):1048"""1049Return or define the lower bound on a variable10501051INPUT:10521053- ``index`` (integer) -- the variable's id10541055- ``value`` -- real value, or ``None`` to mean that the1056variable has not lower bound. When set to ``False``1057(default), the method returns the current value.10581059EXAMPLE::10601061sage: from sage.numerical.backends.generic_backend import get_solver # optional - Gurobi1062sage: p = get_solver(solver = "Gurobi") # optional - Gurobi1063sage: p.add_variable() # optional - Gurobi106401065sage: p.col_bounds(0) # optional - Gurobi1066(0.0, None)1067sage: p.variable_lower_bound(0, 5) # optional - Gurobi1068sage: p.col_bounds(0) # optional - Gurobi1069(5.0, None)1070"""1071cdef double b[1]107210731074if not value is False:1075check(self.env, GRBsetdblattrelement(1076self.model[0], "LB",1077index,1078value if value is not None else -GRB_INFINITY))10791080check(self.env,GRBupdatemodel(self.model[0]))10811082else:1083error = GRBgetdblattrelement(self.model[0], "LB", index, <double *> b)1084check(self.env, error)1085return None if b[0] <= -2147483647 else b[0]10861087cpdef write_lp(self, char * filename):1088"""1089Write the problem to a .lp file10901091INPUT:10921093- ``filename`` (string)10941095EXAMPLE::10961097sage: from sage.numerical.backends.generic_backend import get_solver # optional - Gurobi1098sage: p = get_solver(solver = "Gurobi") # optional - Gurobi1099sage: p.add_variables(2) # optional - Gurobi110011101sage: p.add_linear_constraint([[0, 1], [1, 2]], None, 3) # optional - Gurobi1102sage: p.set_objective([2, 5]) # optional - Gurobi1103sage: p.write_lp(SAGE_TMP+"/lp_problem.lp") # optional - Gurobi1104"""1105check(self.env, GRBwrite(self.model[0], filename))11061107cpdef write_mps(self, char * filename, int modern):1108"""1109Write the problem to a .mps file11101111INPUT:11121113- ``filename`` (string)11141115EXAMPLE::11161117sage: from sage.numerical.backends.generic_backend import get_solver # optional - Gurobi1118sage: p = get_solver(solver = "Gurobi") # optional - Gurobi1119sage: p.add_variables(2) # optional - Gurobi112011121sage: p.add_linear_constraint([[0, 1], [1, 2]], None, 3) # optional - Gurobi1122sage: p.set_objective([2, 5]) # optional - Gurobi1123sage: p.write_lp(SAGE_TMP+"/lp_problem.lp") # optional - Gurobi1124"""1125check(self.env, GRBwrite(self.model[0], filename))11261127def __dealloc__(self):1128"""1129Destructor1130"""1131if self.model != NULL:1132GRBfreemodel(self.model[0])113311341135cdef dict errors = {113610001 : "GRB_ERROR_OUT_OF_MEMORY",113710002 : "GRB_ERROR_NULL_ARGUMENT",113810003 : "GRB_ERROR_INVALID_ARGUMENT",113910004 : "GRB_ERROR_UNKNOWN_ATTRIBUTE",114010005 : "GRB_ERROR_DATA_NOT_AVAILABLE",114110006 : "GRB_ERROR_INDEX_OUT_OF_RANGE",114210007 : "GRB_ERROR_UNKNOWN_PARAMETER",114310008 : "GRB_ERROR_VALUE_OUT_OF_RANGE",114410009 : "GRB_ERROR_NO_LICENSE",114510010 : "GRB_ERROR_SIZE_LIMIT_EXCEEDED",114610011 : "GRB_ERROR_CALLBACK",114710012 : "GRB_ERROR_FILE_READ",114810013 : "GRB_ERROR_FILE_WRITE",114910014 : "GRB_ERROR_NUMERIC",115010015 : "GRB_ERROR_IIS_NOT_INFEASIBLE",115110016 : "GRB_ERROR_NOT_FOR_MIP",115210017 : "GRB_ERROR_OPTIMIZATION_IN_PROGRESS",115310018 : "GRB_ERROR_DUPLICATES",115410019 : "GRB_ERROR_NODEFILE",115510020 : "GRB_ERROR_Q_NOT_PSD",1156}11571158cdef dict mip_status = {1159GRB_INFEASIBLE: "The problem is infeasible",1160GRB_INF_OR_UNBD: "The problem is infeasible or unbounded",1161GRB_UNBOUNDED: "The problem is unbounded",1162GRB_ITERATION_LIMIT: "The iteration limit has been reached",1163GRB_NODE_LIMIT: "The node limit has been reached",1164GRB_TIME_LIMIT: "The time limit has been reached",1165GRB_SOLUTION_LIMIT: "The solution limit has been reached",1166}11671168cdef check(GRBenv * env, int error):1169if error:1170raise MIPSolverException("Gurobi: "+str(GRBgeterrormsg(env))+" ("+errors[error]+")")1171117211731174