Path: blob/master/src/sage/numerical/backends/coin_backend.pyx
8817 views
"""1COIN Backend23AUTHORS:45- Nathann Cohen (2010-10): initial implementation67- John Perry (2012-03): major modifications to the interface in order to update8the Coin package to version 2.7.59"""1011##############################################################################12# Copyright (C) 2010 Nathann Cohen <[email protected]>13# Distributed under the terms of the GNU General Public License (GPL)14# The full text of the GPL is available at:15# http://www.gnu.org/licenses/16##############################################################################171819from sage.numerical.mip import MIPSolverException20from copy import copy2122cdef class CoinBackend(GenericBackend):2324def __cinit__(self, maximization = True):25"""26Cython constructor2728EXAMPLE::2930sage: from sage.numerical.backends.generic_backend import get_solver31sage: p = get_solver(solver = "Coin") # optional - Coin3233"""3435# Coin devs seem to favor OsiClpSolverInterface36self.si = new OsiClpSolverInterface()37self.model = new CbcModel(self.si[0])38self.prob_name = None39self.row_names = []40self.col_names = []41self.set_verbosity(0)4243if maximization:44self.set_sense(+1)45else:46self.set_sense(-1)4748self.obj_constant_term = 0.04950def __dealloc__(self):51r"""52Destructor function53"""54del self.si5556cpdef int add_variable(self, lower_bound=0.0, upper_bound=None, binary=False, continuous=False, integer=False, obj=0.0, name=None) except -1:57r"""58Add a variable.5960This amounts to adding a new column to the matrix. By default,61the variable is both positive and real.6263INPUT:6465- ``lower_bound`` - the lower bound of the variable (default: 0)6667- ``upper_bound`` - the upper bound of the variable (default: ``None``)6869- ``binary`` - ``True`` if the variable is binary (default: ``False``).7071- ``continuous`` - ``True`` if the variable is binary (default: ``True``).7273- ``integer`` - ``True`` if the variable is binary (default: ``False``).7475- ``obj`` - (optional) coefficient of this variable in the objective function (default: 0.0)7677- ``name`` - an optional name for the newly added variable (default: ``None``).7879OUTPUT: The index of the newly created variable8081EXAMPLE::8283sage: from sage.numerical.backends.generic_backend import get_solver84sage: p = get_solver(solver = "Coin") # optional - Coin85sage: p.ncols() # optional - Coin86087sage: p.add_variable() # optional - Coin88089sage: p.ncols() # optional - Coin90191sage: p.add_variable(binary=True) # optional - Coin92193sage: p.add_variable(lower_bound=-2.0, integer=True) # optional - Coin94295sage: p.add_variable(continuous=True, integer=True) # optional - Coin96Traceback (most recent call last):97...98ValueError: ...99sage: p.add_variable(name='x',obj=1.0) # optional - Coin1003101sage: p.col_name(3) # optional - Coin102'x'103sage: p.objective_coefficient(3) # optional - Coin1041.0105"""106107# for some reason, Cython is not accepting the line below, which appeare108#cdef int vtype = int(bool(binary)) + int(bool(continuous)) + int(bool(integer))109cdef int vtype = int(binary) + int(continuous) + int(integer)110if vtype == 0:111continuous = True112elif vtype != 1:113raise ValueError("Exactly one parameter of 'binary', 'integer' and 'continuous' must be 'True'.")114115self.si.addCol(0, NULL, NULL, 0, self.si.getInfinity(), 0)116117cdef int n118n = self.si.getNumCols() - 1119120if lower_bound != 0.0:121self.variable_lower_bound(n, lower_bound)122if upper_bound is not None:123self.variable_upper_bound(n, upper_bound)124125if binary:126self.set_variable_type(n,0)127elif integer:128self.set_variable_type(n,1)129130if name:131self.col_names.append(name)132else:133self.col_names.append("")134135if obj:136self.si.setObjCoeff(n, obj)137138return n139140cpdef 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:141"""142Add ``number`` new variables.143144This amounts to adding new columns to the matrix. By default,145the variables are both positive and real.146147INPUT:148149- ``n`` - the number of new variables (must be > 0)150151- ``lower_bound`` - the lower bound of the variable (default: 0)152153- ``upper_bound`` - the upper bound of the variable (default: ``None``)154155- ``binary`` - ``True`` if the variable is binary (default: ``False``).156157- ``continuous`` - ``True`` if the variable is binary (default: ``True``).158159- ``integer`` - ``True`` if the variable is binary (default: ``False``).160161- ``obj`` - (optional) coefficient of all variables in the objective function (default: 0.0)162163- ``names`` - optional list of names (default: ``None``)164165OUTPUT: The index of the variable created last.166167EXAMPLE::168169sage: from sage.numerical.backends.generic_backend import get_solver170sage: p = get_solver(solver = "Coin") # optional - Coin171sage: p.ncols() # optional - Coin1720173sage: p.add_variables(5) # optional - Coin1744175sage: p.ncols() # optional - Coin1765177sage: p.add_variables(2, lower_bound=-2.0, integer=True, names=['a','b']) # optional - Coin1786179sage: p.col_name(5) # optional - Coin180'a'181"""182#cdef int vtype = int(bool(binary)) + int(bool(continuous)) + int(bool(integer))183cdef int vtype = int(binary) + int(continuous) + int(integer)184if vtype == 0:185continuous = True186elif vtype != 1:187raise ValueError("Exactly one parameter of 'binary', 'integer' and 'continuous' must be 'True'.")188189cdef int n190n = self.si.getNumCols()191192cdef int i193194for 0<= i < number:195196self.si.addCol(0, NULL, NULL, 0, self.si.getInfinity(), 0)197198if lower_bound != 0.0:199self.variable_lower_bound(n + i, lower_bound)200if upper_bound is not None:201self.variable_upper_bound(n + i, upper_bound)202203if binary:204self.set_variable_type(n + i,0)205elif integer:206self.set_variable_type(n + i,1)207208if obj:209self.si.setObjCoeff(n + i, obj)210211if names != None:212for name in names:213self.col_names.append(name)214else:215self.col_names.extend(['' for i in range(number)])216217return n + number -1218219cpdef set_variable_type(self, int variable, int vtype):220r"""221Sets the type of a variable222223INPUT:224225- ``variable`` (integer) -- the variable's id226227- ``vtype`` (integer) :228229* 1 Integer230* 0 Binary231* -1 Real232233EXAMPLE::234235sage: from sage.numerical.backends.generic_backend import get_solver236sage: p = get_solver(solver = "Coin") # optional - Coin237sage: p.ncols() # optional - Coin2380239sage: p.add_variable() # optional - Coin2400241sage: p.set_variable_type(0,1) # optional - Coin242sage: p.is_variable_integer(0) # optional - Coin243True244"""245246if vtype == 1:247self.si.setInteger(variable)248elif vtype == 0:249self.si.setColLower(variable, 0)250self.si.setInteger(variable)251self.si.setColUpper(variable, 1)252else:253self.si.setContinuous(variable)254255cpdef set_sense(self, int sense):256r"""257Sets the direction (maximization/minimization).258259INPUT:260261- ``sense`` (integer) :262263* +1 => Maximization264* -1 => Minimization265266EXAMPLE::267268sage: from sage.numerical.backends.generic_backend import get_solver269sage: p = get_solver(solver = "Coin") # optional - Coin270sage: p.is_maximization() # optional - Coin271True272sage: p.set_sense(-1) # optional - Coin273sage: p.is_maximization() # optional - Coin274False275"""276self.si.setObjSense(-sense)277278cpdef objective_coefficient(self, int variable, coeff=None):279"""280Set or get the coefficient of a variable in the objective function281282INPUT:283284- ``variable`` (integer) -- the variable's id285286- ``coeff`` (double) -- its coefficient or ``None`` for287reading (default: ``None``)288289EXAMPLE::290291sage: from sage.numerical.backends.generic_backend import get_solver292sage: p = get_solver(solver = "Coin") # optional -- Coin293sage: p.add_variable() # optional -- Coin2940295sage: p.objective_coefficient(0) # optional -- Coin2960.0297sage: p.objective_coefficient(0,2) # optional -- Coin298sage: p.objective_coefficient(0) # optional -- Coin2992.0300"""301if coeff is not None:302self.si.setObjCoeff(variable, coeff)303else:304return self.si.getObjCoefficients()[variable]305306cpdef set_objective(self, list coeff, d = 0.0):307r"""308Sets the objective function.309310INPUT:311312- ``coeff`` -- a list of real values, whose ith element is the313coefficient of the ith variable in the objective function.314315- ``d`` (double) -- the constant term in the linear function (set to `0` by default)316317EXAMPLE::318319sage: from sage.numerical.backends.generic_backend import get_solver320sage: p = get_solver(solver = "Coin") # optional - Coin321sage: p.add_variables(5) # optional - Coin3224323sage: p.set_objective([1, 1, 2, 1, 3]) # optional - Coin324sage: map(lambda x :p.objective_coefficient(x), range(5)) # optional - Coin325[1.0, 1.0, 2.0, 1.0, 3.0]326327Constants in the objective function are respected::328329sage: p = MixedIntegerLinearProgram(solver='Coin') # optional - Coin330sage: x,y = p[0], p[1] # optional - Coin331sage: p.add_constraint(2*x + 3*y, max = 6) # optional - Coin332sage: p.add_constraint(3*x + 2*y, max = 6) # optional - Coin333sage: p.set_objective(x + y + 7) # optional - Coin334sage: p.set_integer(x); p.set_integer(y) # optional - Coin335sage: p.solve() # optional - Coin3369.0337"""338339cdef int i340341for i,v in enumerate(coeff):342self.si.setObjCoeff(i, v)343344self.obj_constant_term = d345346cpdef set_verbosity(self, int level):347r"""348Sets the log (verbosity) level349350INPUT:351352- ``level`` (integer) -- From 0 (no verbosity) to 3.353354EXAMPLE::355356sage: from sage.numerical.backends.generic_backend import get_solver357sage: p = get_solver(solver = "Coin") # optional - Coin358sage: p.set_verbosity(2) # optional - Coin359360"""361362self.model.setLogLevel(level)363364cpdef remove_constraint(self, int i):365r"""366Remove a constraint from self.367368INPUT:369370- ``i`` -- index of the constraint to remove371372EXAMPLE::373374sage: p = MixedIntegerLinearProgram(solver='Coin') # optional - Coin375sage: x,y = p[0], p[1] # optional - Coin376sage: p.add_constraint(2*x + 3*y, max = 6) # optional - Coin377sage: p.add_constraint(3*x + 2*y, max = 6) # optional - Coin378sage: p.set_objective(x + y + 7) # optional - Coin379sage: p.set_integer(x); p.set_integer(y) # optional - Coin380sage: p.solve() # optional - Coin3819.0382sage: p.remove_constraint(0) # optional - Coin383sage: p.solve() # optional - Coin38410.0385sage: p.get_values([x,y]) # optional - Coin386[0.0, 3.0]387388TESTS:389390Removing fancy constraints does not make Sage crash::391392sage: MixedIntegerLinearProgram(solver='Coin').remove_constraint(-2) # optional - Coin393Traceback (most recent call last):394...395ValueError: The constraint's index i must satisfy 0 <= i < number_of_constraints396"""397cdef int rows [1]398399if i < 0 or i >= self.si.getNumRows():400raise ValueError("The constraint's index i must satisfy 0 <= i < number_of_constraints")401rows[0] = i402self.si.deleteRows(1,rows)403404cpdef remove_constraints(self, constraints):405r"""406Remove several constraints.407408INPUT:409410- ``constraints`` -- an interable containing the indices of the rows to remove411412EXAMPLE::413414sage: p = MixedIntegerLinearProgram(solver='Coin') # optional - Coin415sage: x,y = p[0], p[1] # optional - Coin416sage: p.add_constraint(2*x + 3*y, max = 6) # optional - Coin417sage: p.add_constraint(3*x + 2*y, max = 6) # optional - Coin418sage: p.set_objective(x + y + 7) # optional - Coin419sage: p.set_integer(x); p.set_integer(y) # optional - Coin420sage: p.solve() # optional - Coin4219.0422sage: p.get_values(x) # optional - Coin4232.0...424sage: p.get_values(y) # optional - Coin4250.0...426sage: p.remove_constraints([0]) # optional - Coin427sage: p.solve() # optional - Coin42810.0429sage: p.get_values([x,y]) # optional - Coin430[0.0, 3.0]431432TESTS:433434Removing fancy constraints do not make Sage crash::435436sage: MixedIntegerLinearProgram(solver='Coin').remove_constraints([0, -2]) # optional - Coin437Traceback (most recent call last):438...439ValueError: The constraint's index i must satisfy 0 <= i < number_of_constraints440"""441cdef int i, c442cdef int m = len(constraints)443cdef int * rows = <int *>sage_malloc(m * sizeof(int *))444cdef int nrows = self.si.getNumRows()445446for i in xrange(m):447448c = constraints[i]449if c < 0 or c >= nrows:450sage_free(rows)451raise ValueError("The constraint's index i must satisfy 0 <= i < number_of_constraints")452453rows[i] = c454455self.si.deleteRows(m,rows)456sage_free(rows)457458cpdef add_linear_constraints(self, int number, lower_bound, upper_bound, names = None):459"""460Add ``'number`` linear constraints.461462INPUT:463464- ``number`` (integer) -- the number of constraints to add.465466- ``lower_bound`` - a lower bound, either a real value or ``None``467468- ``upper_bound`` - an upper bound, either a real value or ``None``469470- ``names`` - an optional list of names (default: ``None``)471472EXAMPLE::473474sage: from sage.numerical.backends.generic_backend import get_solver475sage: p = get_solver(solver = "Coin") # optional - Coin476sage: p.add_variables(5) # optional - Coin4774478sage: p.add_linear_constraints(5, None, 2) # optional - Coin479sage: p.row(4) # optional - Coin480([], [])481sage: p.row_bounds(4) # optional - Coin482(None, 2.0)483sage: p.add_linear_constraints(2, None, 2, names=['foo','bar']) # optional - Coin484sage: p.row_name(6) # optional - Coin485'bar'486"""487488cdef int i489for 0<= i<number:490self.add_linear_constraint([],lower_bound, upper_bound, name = (names[i] if names else None))491492493cpdef add_linear_constraint(self, coefficients, lower_bound, upper_bound, name = None):494"""495Add a linear constraint.496497INPUT:498499- ``coefficients`` an iterable with ``(c,v)`` pairs where ``c``500is a variable index (integer) and ``v`` is a value (real501value).502503- ``lower_bound`` - a lower bound, either a real value or ``None``504505- ``upper_bound`` - an upper bound, either a real value or ``None``506507- ``name`` - an optional name for this row (default: ``None``)508509EXAMPLE::510511sage: from sage.numerical.backends.generic_backend import get_solver512sage: p = get_solver(solver = "Coin") # optional - Coin513sage: p.add_variables(5) # optional - Coin5144515sage: p.add_linear_constraint( zip(range(5), range(5)), 2.0, 2.0) # optional - Coin516sage: p.row(0) # optional - Coin517([0, 1, 2, 3, 4], [0.0, 1.0, 2.0, 3.0, 4.0])518sage: p.row_bounds(0) # optional - Coin519(2.0, 2.0)520sage: p.add_linear_constraint( zip(range(5), range(5)), 1.0, 1.0, name='foo') # optional - Coin521sage: p.row_name(1) # optional - Coin522'foo'523"""524if lower_bound is None and upper_bound is None:525raise ValueError("At least one of 'upper_bound' or 'lower_bound' must be set.")526527cdef int i528cdef double c529cdef CoinPackedVector* row530row = new_CoinPackedVector();531532533for i,c in coefficients:534row.insert(i, c)535536self.si.addRow (row[0],537lower_bound if lower_bound != None else -self.si.getInfinity(),538upper_bound if upper_bound != None else +self.si.getInfinity())539if name != None:540self.row_names.append(name)541else:542self.row_names.append("")543544cpdef row(self, int index):545r"""546Returns a row547548INPUT:549550- ``index`` (integer) -- the constraint's id.551552OUTPUT:553554A pair ``(indices, coeffs)`` where ``indices`` lists the555entries whose coefficient is nonzero, and to which ``coeffs``556associates their coefficient on the model of the557``add_linear_constraint`` method.558559EXAMPLE::560561sage: from sage.numerical.backends.generic_backend import get_solver562sage: p = get_solver(solver = "Coin") # optional - Coin563sage: p.add_variables(5) # optional - Coin5644565sage: p.add_linear_constraint(zip(range(5), range(5)), 2, 2) # optional - Coin566sage: p.row(0) # optional - Coin567([0, 1, 2, 3, 4], [0.0, 1.0, 2.0, 3.0, 4.0])568sage: p.row_bounds(0) # optional - Coin569(2.0, 2.0)570"""571572cdef list indices = []573cdef list values = []574cdef int * c_indices575cdef int i576cdef double * c_values577cdef CoinPackedMatrix * M = <CoinPackedMatrix *> self.si.getMatrixByRow()578cdef CoinShallowPackedVector V = <CoinShallowPackedVector> M.getVector(index)579cdef int n = V.getNumElements()580581c_indices = <int*> V.getIndices()582c_values = <double*> V.getElements()583584for 0<= i < n:585indices.append(c_indices[i])586values.append(c_values[i])587588return (indices, values)589590cpdef row_bounds(self, int i):591r"""592Returns the bounds of a specific constraint.593594INPUT:595596- ``index`` (integer) -- the constraint's id.597598OUTPUT:599600A pair ``(lower_bound, upper_bound)``. Each of them can be set601to ``None`` if the constraint is not bounded in the602corresponding direction, and is a real value otherwise.603604EXAMPLE::605606sage: from sage.numerical.backends.generic_backend import get_solver607sage: p = get_solver(solver = "Coin") # optional - Coin608sage: p.add_variables(5) # optional - Coin6094610sage: p.add_linear_constraint(zip(range(5), range(5)), 2, 2) # optional - Coin611sage: p.row(0) # optional - Coin612([0, 1, 2, 3, 4], [0.0, 1.0, 2.0, 3.0, 4.0])613sage: p.row_bounds(0) # optional - Coin614(2.0, 2.0)615"""616617cdef double * ub618cdef double * lb619620ub = <double*> self.si.getRowUpper()621lb = <double*> self.si.getRowLower()622623return (lb[i] if lb[i] != - self.si.getInfinity() else None,624ub[i] if ub[i] != + self.si.getInfinity() else None)625626cpdef col_bounds(self, int i):627r"""628Returns the bounds of a specific variable.629630INPUT:631632- ``index`` (integer) -- the variable's id.633634OUTPUT:635636A pair ``(lower_bound, upper_bound)``. Each of them can be set637to ``None`` if the variable is not bounded in the638corresponding direction, and is a real value otherwise.639640EXAMPLE::641642sage: from sage.numerical.backends.generic_backend import get_solver643sage: p = get_solver(solver = "Coin") # optional - Coin644sage: p.add_variable() # optional - Coin6450646sage: p.col_bounds(0) # optional - Coin647(0.0, None)648sage: p.variable_upper_bound(0, 5) # optional - Coin649sage: p.col_bounds(0) # optional - Coin650(0.0, 5.0)651"""652653cdef double * ub654cdef double * lb655656ub = <double*> self.si.getColUpper()657lb = <double*> self.si.getColLower()658659return (lb[i] if lb[i] != - self.si.getInfinity() else None,660ub[i] if ub[i] != + self.si.getInfinity() else None)661662cpdef add_col(self, list indices, list coeffs):663r"""664Adds a column.665666INPUT:667668- ``indices`` (list of integers) -- this list constains the669indices of the constraints in which the variable's670coefficient is nonzero671672- ``coeffs`` (list of real values) -- associates a coefficient673to the variable in each of the constraints in which it674appears. Namely, the ith entry of ``coeffs`` corresponds to675the coefficient of the variable in the constraint676represented by the ith entry in ``indices``.677678.. NOTE::679680``indices`` and ``coeffs`` are expected to be of the same681length.682683EXAMPLE::684685sage: from sage.numerical.backends.generic_backend import get_solver686sage: p = get_solver(solver = "Coin") # optional - Coin687sage: p.ncols() # optional - Coin6880689sage: p.nrows() # optional - Coin6900691sage: p.add_linear_constraints(5, 0, None) # optional - Coin692sage: p.add_col(range(5), range(5)) # optional - Coin693sage: p.nrows() # optional - Coin6945695"""696697cdef int n = len(indices)698cdef int * c_indices = <int*>sage_malloc(n*sizeof(int))699cdef double * c_values = <double*>sage_malloc(n*sizeof(double))700cdef int i701702for 0<= i< n:703c_indices[i] = indices[i]704c_values[i] = coeffs[i]705706self.si.addCol (1, c_indices, c_values, 0, self.si.getInfinity(), 0)707708cpdef int solve(self) except -1:709r"""710Solves the problem.711712.. NOTE::713714This method raises ``MIPSolverException`` exceptions when715the solution can not be computed for any reason (none716exists, or the LP solver was not able to find it, etc...)717718EXAMPLE::719720sage: from sage.numerical.backends.generic_backend import get_solver721sage: p = get_solver(solver = "Coin") # optional - Coin722sage: p.add_linear_constraints(5, 0, None) # optional - Coin723sage: p.add_col(range(5), [1,2,3,4,5]) # optional - Coin724sage: p.solve() # optional - Coin7250726727TESTS::728729sage: from sage.numerical.backends.generic_backend import get_solver730sage: p = get_solver(solver = "Coin") # optional - Coin731sage: p.add_variable() # optional - Coin7320733sage: p.add_linear_constraint([(0, 1)], None, 4) # optional - Coin734sage: p.add_linear_constraint([(0, 1)], 6, None) # optional - Coin735sage: p.objective_coefficient(0,1) # optional - Coin736sage: p.solve() # optional - Coin737Traceback (most recent call last):738...739MIPSolverException: ...740"""741742# set up the model743cdef OsiSolverInterface * si = self.si744745cdef CbcModel * model746model = new CbcModel(si[0])747model.setLogLevel(self.model.logLevel())748749# multithreading750import multiprocessing751model.setNumberThreads(multiprocessing.cpu_count())752753model.branchAndBound()754755if model.solver().isAbandoned():756raise MIPSolverException("CBC : The solver has abandoned!")757758elif model.solver().isProvenPrimalInfeasible() or model.solver().isProvenDualInfeasible():759raise MIPSolverException("CBC : The problem or its dual has been proven infeasible!")760761elif (model.solver().isPrimalObjectiveLimitReached() or model.solver().isDualObjectiveLimitReached()):762raise MIPSolverException("CBC : The objective limit has been reached for the problem or its dual!")763764elif model.solver().isIterationLimitReached():765raise MIPSolverException("CBC : The iteration limit has been reached!")766767elif not model.solver().isProvenOptimal():768raise MIPSolverException("CBC : Unknown error")769770del self.model771self.model = model772773cpdef get_objective_value(self):774r"""775Returns the value of the objective function.776777.. NOTE::778779Has no meaning unless ``solve`` has been called before.780781EXAMPLE::782783sage: from sage.numerical.backends.generic_backend import get_solver784sage: p = get_solver(solver = "Coin") # optional - Coin785sage: p.add_variables(2) # optional - Coin7861787sage: p.add_linear_constraint([(0, 1), (1, 2)], None, 3) # optional - Coin788sage: p.set_objective([2, 5]) # optional - Coin789sage: p.solve() # optional - Coin7900791sage: p.get_objective_value() # optional - Coin7927.5793sage: p.get_variable_value(0) # optional - Coin7940.0795sage: p.get_variable_value(1) # optional - Coin7961.5797"""798return self.model.solver().getObjValue() + self.obj_constant_term799800cpdef get_variable_value(self, int variable):801r"""802Returns the value of a variable given by the solver.803804.. NOTE::805806Has no meaning unless ``solve`` has been called before.807808EXAMPLE::809810sage: from sage.numerical.backends.generic_backend import get_solver811sage: p = get_solver(solver = "Coin") # optional - Coin812sage: p.add_variables(2) # optional - Coin8131814sage: p.add_linear_constraint([(0, 1), (1, 2)], None, 3) # optional - Coin815sage: p.set_objective([2, 5]) # optional - Coin816sage: p.solve() # optional - Coin8170818sage: p.get_objective_value() # optional - Coin8197.5820sage: p.get_variable_value(0) # optional - Coin8210.0822sage: p.get_variable_value(1) # optional - Coin8231.5824"""825826cdef double * solution827solution = <double*> self.model.solver().getColSolution()828return solution[variable]829830cpdef int ncols(self):831r"""832Returns the number of columns/variables.833834EXAMPLE::835836sage: from sage.numerical.backends.generic_backend import get_solver837sage: p = get_solver(solver = "Coin") # optional - Coin838sage: p.ncols() # optional - Coin8390840sage: p.add_variables(2) # optional - Coin8411842sage: p.ncols() # optional - Coin8432844"""845846return self.si.getNumCols()847848cpdef int nrows(self):849r"""850Returns the number of rows/constraints.851852EXAMPLE::853854sage: from sage.numerical.backends.generic_backend import get_solver855sage: p = get_solver(solver = "Coin") # optional - Coin856sage: p.nrows() # optional - Coin8570858sage: p.add_linear_constraints(2, 2, None) # optional - Coin859sage: p.nrows() # optional - Coin8602861"""862return self.si.getNumRows()863864865cpdef bint is_variable_binary(self, int index):866r"""867Tests whether the given variable is of binary type.868869INPUT:870871- ``index`` (integer) -- the variable's id872873EXAMPLE::874875sage: from sage.numerical.backends.generic_backend import get_solver876sage: p = get_solver(solver = "Coin") # optional - Coin877sage: p.ncols() # optional - Coin8780879sage: p.add_variable() # optional - Coin8800881sage: p.set_variable_type(0,0) # optional - Coin882sage: p.is_variable_binary(0) # optional - Coin883True884885"""886887return (0 == self.si.isContinuous(index) and888self.variable_lower_bound(index) == 0 and889self.variable_upper_bound(index) == 1)890891cpdef bint is_variable_integer(self, int index):892r"""893Tests whether the given variable is of integer type.894895INPUT:896897- ``index`` (integer) -- the variable's id898899EXAMPLE::900901sage: from sage.numerical.backends.generic_backend import get_solver902sage: p = get_solver(solver = "Coin") # optional - Coin903sage: p.ncols() # optional - Coin9040905sage: p.add_variable() # optional - Coin9060907sage: p.set_variable_type(0,1) # optional - Coin908sage: p.is_variable_integer(0) # optional - Coin909True910"""911return (0 == self.si.isContinuous(index) and912(self.variable_lower_bound(index) != 0 or913self.variable_upper_bound(index) != 1))914915cpdef bint is_variable_continuous(self, int index):916r"""917Tests whether the given variable is of continuous/real type.918919INPUT:920921- ``index`` (integer) -- the variable's id922923EXAMPLE::924925sage: from sage.numerical.backends.generic_backend import get_solver926sage: p = get_solver(solver = "Coin") # optional - Coin927sage: p.ncols() # optional - Coin9280929sage: p.add_variable() # optional - Coin9300931sage: p.is_variable_continuous(0) # optional - Coin932True933sage: p.set_variable_type(0,1) # optional - Coin934sage: p.is_variable_continuous(0) # optional - Coin935False936937"""938return 1 == self.si.isContinuous(index)939940941cpdef bint is_maximization(self):942r"""943Tests whether the problem is a maximization944945EXAMPLE::946947sage: from sage.numerical.backends.generic_backend import get_solver948sage: p = get_solver(solver = "Coin") # optional - Coin949sage: p.is_maximization() # optional - Coin950True951sage: p.set_sense(-1) # optional - Coin952sage: p.is_maximization() # optional - Coin953False954"""955956return self.si.getObjSense() == -1957958cpdef variable_upper_bound(self, int index, value = False):959r"""960Returns or defines the upper bound on a variable961962INPUT:963964- ``index`` (integer) -- the variable's id965966- ``value`` -- real value, or ``None`` to mean that the967variable has not upper bound. When set to ``False``968(default), the method returns the current value.969970EXAMPLE::971972sage: from sage.numerical.backends.generic_backend import get_solver973sage: p = get_solver(solver = "Coin") # optional - Coin974sage: p.add_variable() # optional - Coin9750976sage: p.col_bounds(0) # optional - Coin977(0.0, None)978sage: p.variable_upper_bound(0, 5) # optional - Coin979sage: p.col_bounds(0) # optional - Coin980(0.0, 5.0)981982TESTS:983984:trac:`14581`::985986sage: P = MixedIntegerLinearProgram(solver="Coin") # optional - Coin987sage: x = P["x"] # optional - Coin988sage: P.set_max(x, 0) # optional - Coin989sage: P.get_max(x) # optional - Coin9900.0991992"""993cdef double * ub994995if value is False:996ub = <double*> self.si.getColUpper()997return ub[index] if ub[index] != + self.si.getInfinity() else None998else:999self.si.setColUpper(index, value if value is not None else +self.si.getInfinity())10001001cpdef variable_lower_bound(self, int index, value = False):1002r"""1003Returns or defines the lower bound on a variable10041005INPUT:10061007- ``index`` (integer) -- the variable's id10081009- ``value`` -- real value, or ``None`` to mean that the1010variable has not lower bound. When set to ``False``1011(default), the method returns the current value.10121013EXAMPLE::10141015sage: from sage.numerical.backends.generic_backend import get_solver1016sage: p = get_solver(solver = "Coin") # optional - Coin1017sage: p.add_variable() # optional - Coin101801019sage: p.col_bounds(0) # optional - Coin1020(0.0, None)1021sage: p.variable_lower_bound(0, 5) # optional - Coin1022sage: p.col_bounds(0) # optional - Coin1023(5.0, None)10241025TESTS:10261027:trac:`14581`::10281029sage: P = MixedIntegerLinearProgram(solver="Coin") # optional - Coin1030sage: x = P["x"] # optional - Coin1031sage: P.set_min(x, 5) # optional - Coin1032sage: P.set_min(x, 0) # optional - Coin1033sage: P.get_min(x) # optional - Coin10340.01035"""1036cdef double * lb10371038if value is False:1039lb = <double*> self.si.getColLower()1040return lb[index] if lb[index] != - self.si.getInfinity() else None1041else:1042self.si.setColLower(index, value if value is not None else -self.si.getInfinity())10431044cpdef write_mps(self, char * filename, int modern):1045r"""1046Writes the problem to a .mps file10471048INPUT:10491050- ``filename`` (string)10511052EXAMPLE::10531054sage: from sage.numerical.backends.generic_backend import get_solver1055sage: p = get_solver(solver = "Coin") # optional - Coin1056sage: p.add_variables(2) # optional - Coin105711058sage: p.add_linear_constraint([(0, 1), (1, 2)], None, 3) # optional - Coin1059sage: p.set_objective([2, 5]) # optional - Coin1060sage: p.write_mps(os.path.join(SAGE_TMP, "lp_problem.mps"), 0) # optional - Coin1061"""10621063cdef char * mps = "mps"1064self.si.writeMps(filename, mps, -1 if self.is_maximization() else 1)10651066cpdef write_lp(self, char * filename):1067r"""1068Writes the problem to a .lp file10691070INPUT:10711072- ``filename`` (string)10731074EXAMPLE::10751076sage: from sage.numerical.backends.generic_backend import get_solver1077sage: p = get_solver(solver = "Coin") # optional - Coin1078sage: p.add_variables(2) # optional - Coin107911080sage: p.add_linear_constraint([(0, 1), (1, 2)], None, 3) # optional - Coin1081sage: p.set_objective([2, 5]) # optional - Coin1082sage: p.write_lp(os.path.join(SAGE_TMP, "lp_problem.lp")) # optional - Coin1083"""10841085cdef char * lp = "lp"1086self.si.writeLp(filename, lp, 0.00001, 10, 5, -1 if self.is_maximization() else 1, 1)10871088cpdef problem_name(self, char * name = NULL):1089r"""1090Returns or defines the problem's name10911092INPUT:10931094- ``name`` (``char *``) -- the problem's name. When set to1095``NULL`` (default), the method returns the problem's name.10961097EXAMPLE::10981099sage: from sage.numerical.backends.generic_backend import get_solver1100sage: p = get_solver(solver = "Coin") # optional - Coin1101sage: p.problem_name("There once was a french fry") # optional - Coin1102sage: print p.problem_name() # optional - Coin1103There once was a french fry1104"""1105if name == NULL:1106if self.prob_name != None:1107return self.prob_name1108else:1109return ""1110else:1111self.prob_name = str(name)111211131114cpdef row_name(self, int index):1115r"""1116Returns the ``index`` th row name11171118INPUT:11191120- ``index`` (integer) -- the row's id11211122EXAMPLE::11231124sage: from sage.numerical.backends.generic_backend import get_solver1125sage: p = get_solver(solver = "Coin") # optional - Coin1126sage: p.add_linear_constraints(1, 2, None, names=['Empty constraint 1']) # optional - Coin1127sage: print p.row_name(0) # optional - Coin1128Empty constraint 11129"""1130if self.row_names != None:1131return self.row_names[index]1132else:1133return ""11341135cpdef col_name(self, int index):1136r"""1137Returns the ``index`` th col name11381139INPUT:11401141- ``index`` (integer) -- the col's id11421143EXAMPLE::11441145sage: from sage.numerical.backends.generic_backend import get_solver1146sage: p = get_solver(solver = "Coin") # optional - Coin1147sage: p.add_variable(name='I am a variable') # optional - Coin114801149sage: print p.col_name(0) # optional - Coin1150I am a variable1151"""1152if self.col_names != None:1153return self.col_names[index]1154else:1155return ""11561157cpdef CoinBackend copy(self):1158"""1159Returns a copy of self.11601161EXAMPLE::11621163sage: from sage.numerical.backends.generic_backend import get_solver1164sage: p = MixedIntegerLinearProgram(solver = "Coin") # optional - Coin1165sage: b = p.new_variable() # optional - Coin1166sage: p.add_constraint(b[1] + b[2] <= 6) # optional - Coin1167sage: p.set_objective(b[1] + b[2]) # optional - Coin1168sage: copy(p).solve() # optional - Coin11696.01170"""1171# create new backend1172cdef CoinBackend p = CoinBackend(maximization = (1 if self.is_maximization() else -1))11731174# replace solver with copy of self's solver1175del p.si1176p.si = self.si.clone(1)1177p.row_names = copy(self.row_names)1178p.col_names = copy(self.col_names)1179p.obj_constant_term = self.obj_constant_term1180# Maybe I should copy this, not sure -- seems complicated, though1181p.prob_name = self.prob_name11821183return p118411851186