Path: blob/master/sage/numerical/backends/coin_backend.pyx
4079 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 = []4142if maximization:43self.set_sense(+1)44else:45self.set_sense(-1)4647self.obj_constant_term = 0.04849def __dealloc__(self):50r"""51Destructor function52"""53del self.si5455cpdef int add_variable(self, lower_bound=0.0, upper_bound=None, binary=False, continuous=False, integer=False, obj=0.0, name=None) except -1:56r"""57Add a variable.5859This amounts to adding a new column to the matrix. By default,60the variable is both positive and real.6162INPUT:6364- ``lower_bound`` - the lower bound of the variable (default: 0)6566- ``upper_bound`` - the upper bound of the variable (default: ``None``)6768- ``binary`` - ``True`` if the variable is binary (default: ``False``).6970- ``continuous`` - ``True`` if the variable is binary (default: ``True``).7172- ``integer`` - ``True`` if the variable is binary (default: ``False``).7374- ``obj`` - (optional) coefficient of this variable in the objective function (default: 0.0)7576- ``name`` - an optional name for the newly added variable (default: ``None``).7778OUTPUT: The index of the newly created variable7980EXAMPLE::8182sage: from sage.numerical.backends.generic_backend import get_solver83sage: p = get_solver(solver = "Coin") # optional - Coin84sage: p.ncols() # optional - Coin85086sage: p.add_variable() # optional - Coin87088sage: p.ncols() # optional - Coin89190sage: p.add_variable(binary=True) # optional - Coin91192sage: p.add_variable(lower_bound=-2.0, integer=True) # optional - Coin93294sage: p.add_variable(continuous=True, integer=True) # optional - Coin95Traceback (most recent call last):96...97ValueError: ...98sage: p.add_variable(name='x',obj=1.0) # optional - Coin993100sage: p.col_name(3) # optional - Coin101'x'102sage: p.objective_coefficient(3) # optional - Coin1031.0104"""105106# for some reason, Cython is not accepting the line below, which appeare107#cdef int vtype = int(bool(binary)) + int(bool(continuous)) + int(bool(integer))108cdef int vtype = int(binary) + int(continuous) + int(integer)109if vtype == 0:110continuous = True111elif vtype != 1:112raise ValueError("Exactly one parameter of 'binary', 'integer' and 'continuous' must be 'True'.")113114self.si.addCol(0, NULL, NULL, 0, self.si.getInfinity(), 0)115116cdef int n117n = self.si.getNumCols() - 1118119if lower_bound != 0.0:120self.variable_lower_bound(n, lower_bound)121if upper_bound is not None:122self.variable_upper_bound(n, upper_bound)123124if binary:125self.set_variable_type(n,0)126elif integer:127self.set_variable_type(n,1)128129if name:130self.col_names.append(name)131else:132self.col_names.append("")133134if obj:135self.si.setObjCoeff(n, obj)136137return n138139cpdef 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:140"""141Add ``number`` new variables.142143This amounts to adding new columns to the matrix. By default,144the variables are both positive and real.145146INPUT:147148- ``n`` - the number of new variables (must be > 0)149150- ``lower_bound`` - the lower bound of the variable (default: 0)151152- ``upper_bound`` - the upper bound of the variable (default: ``None``)153154- ``binary`` - ``True`` if the variable is binary (default: ``False``).155156- ``continuous`` - ``True`` if the variable is binary (default: ``True``).157158- ``integer`` - ``True`` if the variable is binary (default: ``False``).159160- ``obj`` - (optional) coefficient of all variables in the objective function (default: 0.0)161162- ``names`` - optional list of names (default: ``None``)163164OUTPUT: The index of the variable created last.165166EXAMPLE::167168sage: from sage.numerical.backends.generic_backend import get_solver169sage: p = get_solver(solver = "Coin") # optional - Coin170sage: p.ncols() # optional - Coin1710172sage: p.add_variables(5) # optional - Coin1734174sage: p.ncols() # optional - Coin1755176sage: p.add_variables(2, lower_bound=-2.0, integer=True, names=['a','b']) # optional - Coin1776178sage: p.col_name(5) # optional - Coin179'a'180"""181#cdef int vtype = int(bool(binary)) + int(bool(continuous)) + int(bool(integer))182cdef int vtype = int(binary) + int(continuous) + int(integer)183if vtype == 0:184continuous = True185elif vtype != 1:186raise ValueError("Exactly one parameter of 'binary', 'integer' and 'continuous' must be 'True'.")187188cdef int n189n = self.si.getNumCols()190191cdef int i192193for 0<= i < number:194195self.si.addCol(0, NULL, NULL, 0, self.si.getInfinity(), 0)196197if lower_bound != 0.0:198self.variable_lower_bound(n + i, lower_bound)199if upper_bound is not None:200self.variable_upper_bound(n + i, upper_bound)201202if binary:203self.set_variable_type(n + i,0)204elif integer:205self.set_variable_type(n + i,1)206207if obj:208self.si.setObjCoeff(n + i, obj)209210if names != None:211for name in names:212self.col_names.append(name)213else:214self.col_names.extend(['' for i in range(number)])215216return n + number -1217218cpdef set_variable_type(self, int variable, int vtype):219r"""220Sets the type of a variable221222INPUT:223224- ``variable`` (integer) -- the variable's id225226- ``vtype`` (integer) :227228* 1 Integer229* 0 Binary230* -1 Real231232EXAMPLE::233234sage: from sage.numerical.backends.generic_backend import get_solver235sage: p = get_solver(solver = "Coin") # optional - Coin236sage: p.ncols() # optional - Coin2370238sage: p.add_variable() # optional - Coin2390240sage: p.set_variable_type(0,1) # optional - Coin241sage: p.is_variable_integer(0) # optional - Coin242True243"""244245if vtype == 1:246self.si.setInteger(variable)247elif vtype == 0:248self.si.setColLower(variable, 0)249self.si.setInteger(variable)250self.si.setColUpper(variable, 1)251else:252self.si.setContinuous(variable)253254cpdef set_sense(self, int sense):255r"""256Sets the direction (maximization/minimization).257258INPUT:259260- ``sense`` (integer) :261262* +1 => Maximization263* -1 => Minimization264265EXAMPLE::266267sage: from sage.numerical.backends.generic_backend import get_solver268sage: p = get_solver(solver = "Coin") # optional - Coin269sage: p.is_maximization() # optional - Coin270True271sage: p.set_sense(-1) # optional - Coin272sage: p.is_maximization() # optional - Coin273False274"""275self.si.setObjSense(-sense)276277cpdef objective_coefficient(self, int variable, coeff=None):278"""279Set or get the coefficient of a variable in the objective function280281INPUT:282283- ``variable`` (integer) -- the variable's id284285- ``coeff`` (double) -- its coefficient or ``None`` for286reading (default: ``None``)287288EXAMPLE::289290sage: from sage.numerical.backends.generic_backend import get_solver291sage: p = get_solver(solver = "Coin") # optional -- Coin292sage: p.add_variable() # optional -- Coin2930294sage: p.objective_coefficient(0) # optional -- Coin2950.0296sage: p.objective_coefficient(0,2) # optional -- Coin297sage: p.objective_coefficient(0) # optional -- Coin2982.0299"""300if coeff is not None:301self.si.setObjCoeff(variable, coeff)302else:303return self.si.getObjCoefficients()[variable]304305cpdef set_objective(self, list coeff, double d = 0.0):306r"""307Sets the objective function.308309INPUT:310311- ``coeff`` -- a list of real values, whose ith element is the312coefficient of the ith variable in the objective function.313314- ``d`` (double) -- the constant term in the linear function (set to `0` by default)315316EXAMPLE::317318sage: from sage.numerical.backends.generic_backend import get_solver319sage: p = get_solver(solver = "Coin") # optional - Coin320sage: p.add_variables(5) # optional - Coin3214322sage: p.set_objective([1, 1, 2, 1, 3]) # optional - Coin323sage: map(lambda x :p.objective_coefficient(x), range(5)) # optional - Coin324[1.0, 1.0, 2.0, 1.0, 3.0]325326Constants in the objective function are respected::327328sage: p = MixedIntegerLinearProgram(solver='Coin') # optional - Coin329sage: x,y = p[0], p[1] # optional - Coin330sage: p.add_constraint(2*x + 3*y, max = 6) # optional - Coin331sage: p.add_constraint(3*x + 2*y, max = 6) # optional - Coin332sage: p.set_objective(x + y + 7) # optional - Coin333sage: p.set_integer(x); p.set_integer(y) # optional - Coin334sage: p.solve() # optional - Coin3359.0336"""337338cdef int i339340for i,v in enumerate(coeff):341self.si.setObjCoeff(i, v)342343self.obj_constant_term = d344345cpdef set_verbosity(self, int level):346r"""347Sets the log (verbosity) level348349INPUT:350351- ``level`` (integer) -- From 0 (no verbosity) to 3.352353EXAMPLE::354355sage: from sage.numerical.backends.generic_backend import get_solver356sage: p = get_solver(solver = "Coin") # optional - Coin357sage: p.set_verbosity(2) # optional - Coin358359"""360361self.model.setLogLevel(level)362363cpdef remove_constraint(self, int i):364r"""365Remove a constraint from self.366367INPUT:368369- ``i`` -- index of the constraint to remove370371EXAMPLE::372373sage: p = MixedIntegerLinearProgram(solver='Coin') # optional - Coin374sage: x,y = p[0], p[1] # optional - Coin375sage: p.add_constraint(2*x + 3*y, max = 6) # optional - Coin376sage: p.add_constraint(3*x + 2*y, max = 6) # optional - Coin377sage: p.set_objective(x + y + 7) # optional - Coin378sage: p.set_integer(x); p.set_integer(y) # optional - Coin379sage: p.solve() # optional - Coin3809.0381sage: p.remove_constraint(0) # optional - Coin382sage: p.solve() # optional - Coin38310.0384sage: p.get_values([x,y]) # optional - Coin385[0.0, 3.0]386387TESTS:388389Removing fancy constraints does not make Sage crash::390391sage: MixedIntegerLinearProgram(solver='Coin').remove_constraint(-2) # optional - Coin392Traceback (most recent call last):393...394ValueError: The constraint's index i must satisfy 0 <= i < number_of_constraints395"""396cdef int rows [1]397398if i < 0 or i >= self.si.getNumRows():399raise ValueError("The constraint's index i must satisfy 0 <= i < number_of_constraints")400rows[0] = i401self.si.deleteRows(1,rows)402403cpdef remove_constraints(self, constraints):404r"""405Remove several constraints.406407INPUT:408409- ``constraints`` -- an interable containing the indices of the rows to remove410411EXAMPLE::412413sage: p = MixedIntegerLinearProgram(solver='Coin') # optional - Coin414sage: x,y = p[0], p[1] # optional - Coin415sage: p.add_constraint(2*x + 3*y, max = 6) # optional - Coin416sage: p.add_constraint(3*x + 2*y, max = 6) # optional - Coin417sage: p.set_objective(x + y + 7) # optional - Coin418sage: p.set_integer(x); p.set_integer(y) # optional - Coin419sage: p.solve() # optional - Coin4209.0421sage: p.get_values(x) # optional - Coin4222.0...423sage: p.get_values(y) # optional - Coin4240.0...425sage: p.remove_constraints([0]) # optional - Coin426sage: p.solve() # optional - Coin42710.0428sage: p.get_values([x,y]) # optional - Coin429[0.0, 3.0]430431TESTS:432433Removing fancy constraints do not make Sage crash::434435sage: MixedIntegerLinearProgram(solver='Coin').remove_constraints([0, -2]) # optional - Coin436Traceback (most recent call last):437...438ValueError: The constraint's index i must satisfy 0 <= i < number_of_constraints439"""440cdef int i, c441cdef int m = len(constraints)442cdef int * rows = <int *>sage_malloc(m * sizeof(int *))443cdef int nrows = self.si.getNumRows()444445for i in xrange(m):446447c = constraints[i]448if c < 0 or c >= nrows:449sage_free(rows)450raise ValueError("The constraint's index i must satisfy 0 <= i < number_of_constraints")451452rows[i] = c453454self.si.deleteRows(m,rows)455sage_free(rows)456457cpdef add_linear_constraints(self, int number, lower_bound, upper_bound, names = None):458"""459Add ``'number`` linear constraints.460461INPUT:462463- ``number`` (integer) -- the number of constraints to add.464465- ``lower_bound`` - a lower bound, either a real value or ``None``466467- ``upper_bound`` - an upper bound, either a real value or ``None``468469- ``names`` - an optional list of names (default: ``None``)470471EXAMPLE::472473sage: from sage.numerical.backends.generic_backend import get_solver474sage: p = get_solver(solver = "Coin") # optional - Coin475sage: p.add_variables(5) # optional - Coin4764477sage: p.add_linear_constraints(5, None, 2) # optional - Coin478sage: p.row(4) # optional - Coin479([], [])480sage: p.row_bounds(4) # optional - Coin481(None, 2.0)482sage: p.add_linear_constraints(2, None, 2, names=['foo','bar']) # optional - Coin483sage: p.row_name(6) # optional - Coin484'bar'485"""486487cdef int i488for 0<= i<number:489self.add_linear_constraint([],lower_bound, upper_bound, name = (names[i] if names else None))490491492cpdef add_linear_constraint(self, coefficients, lower_bound, upper_bound, name = None):493"""494Add a linear constraint.495496INPUT:497498- ``coefficients`` an iterable with ``(c,v)`` pairs where ``c``499is a variable index (integer) and ``v`` is a value (real500value).501502- ``lower_bound`` - a lower bound, either a real value or ``None``503504- ``upper_bound`` - an upper bound, either a real value or ``None``505506- ``name`` - an optional name for this row (default: ``None``)507508EXAMPLE::509510sage: from sage.numerical.backends.generic_backend import get_solver511sage: p = get_solver(solver = "Coin") # optional - Coin512sage: p.add_variables(5) # optional - Coin5134514sage: p.add_linear_constraint( zip(range(5), range(5)), 2.0, 2.0) # optional - Coin515sage: p.row(0) # optional - Coin516([0, 1, 2, 3, 4], [0.0, 1.0, 2.0, 3.0, 4.0])517sage: p.row_bounds(0) # optional - Coin518(2.0, 2.0)519sage: p.add_linear_constraint( zip(range(5), range(5)), 1.0, 1.0, name='foo') # optional - Coin520sage: p.row_name(1) # optional - Coin521'foo'522"""523if lower_bound is None and upper_bound is None:524raise ValueError("At least one of 'upper_bound' or 'lower_bound' must be set.")525526cdef int i527cdef double c528cdef CoinPackedVector* row529row = new_CoinPackedVector();530531532for i,c in coefficients:533row.insert(i, c)534535self.si.addRow (row[0],536lower_bound if lower_bound != None else -self.si.getInfinity(),537upper_bound if upper_bound != None else +self.si.getInfinity())538if name != None:539self.row_names.append(name)540else:541self.row_names.append("")542543cpdef row(self, int index):544r"""545Returns a row546547INPUT:548549- ``index`` (integer) -- the constraint's id.550551OUTPUT:552553A pair ``(indices, coeffs)`` where ``indices`` lists the554entries whose coefficient is nonzero, and to which ``coeffs``555associates their coefficient on the model of the556``add_linear_constraint`` method.557558EXAMPLE::559560sage: from sage.numerical.backends.generic_backend import get_solver561sage: p = get_solver(solver = "Coin") # optional - Coin562sage: p.add_variables(5) # optional - Coin5634564sage: p.add_linear_constraint(zip(range(5), range(5)), 2, 2) # optional - Coin565sage: p.row(0) # optional - Coin566([0, 1, 2, 3, 4], [0.0, 1.0, 2.0, 3.0, 4.0])567sage: p.row_bounds(0) # optional - Coin568(2.0, 2.0)569"""570571cdef list indices = []572cdef list values = []573cdef int * c_indices574cdef int i575cdef double * c_values576cdef CoinPackedMatrix * M = <CoinPackedMatrix *> self.si.getMatrixByRow()577cdef CoinShallowPackedVector V = <CoinShallowPackedVector> M.getVector(index)578cdef int n = V.getNumElements()579580c_indices = <int*> V.getIndices()581c_values = <double*> V.getElements()582583for 0<= i < n:584indices.append(c_indices[i])585values.append(c_values[i])586587return (indices, values)588589cpdef row_bounds(self, int i):590r"""591Returns the bounds of a specific constraint.592593INPUT:594595- ``index`` (integer) -- the constraint's id.596597OUTPUT:598599A pair ``(lower_bound, upper_bound)``. Each of them can be set600to ``None`` if the constraint is not bounded in the601corresponding direction, and is a real value otherwise.602603EXAMPLE::604605sage: from sage.numerical.backends.generic_backend import get_solver606sage: p = get_solver(solver = "Coin") # optional - Coin607sage: p.add_variables(5) # optional - Coin6084609sage: p.add_linear_constraint(zip(range(5), range(5)), 2, 2) # optional - Coin610sage: p.row(0) # optional - Coin611([0, 1, 2, 3, 4], [0.0, 1.0, 2.0, 3.0, 4.0])612sage: p.row_bounds(0) # optional - Coin613(2.0, 2.0)614"""615616cdef double * ub617cdef double * lb618619ub = <double*> self.si.getRowUpper()620lb = <double*> self.si.getRowLower()621622return (lb[i] if lb[i] != - self.si.getInfinity() else None,623ub[i] if ub[i] != + self.si.getInfinity() else None)624625cpdef col_bounds(self, int i):626r"""627Returns the bounds of a specific variable.628629INPUT:630631- ``index`` (integer) -- the variable's id.632633OUTPUT:634635A pair ``(lower_bound, upper_bound)``. Each of them can be set636to ``None`` if the variable is not bounded in the637corresponding direction, and is a real value otherwise.638639EXAMPLE::640641sage: from sage.numerical.backends.generic_backend import get_solver642sage: p = get_solver(solver = "Coin") # optional - Coin643sage: p.add_variable() # optional - Coin6440645sage: p.col_bounds(0) # optional - Coin646(0.0, None)647sage: p.variable_upper_bound(0, 5) # optional - Coin648sage: p.col_bounds(0) # optional - Coin649(0.0, 5.0)650"""651652cdef double * ub653cdef double * lb654655ub = <double*> self.si.getColUpper()656lb = <double*> self.si.getColLower()657658return (lb[i] if lb[i] != - self.si.getInfinity() else None,659ub[i] if ub[i] != + self.si.getInfinity() else None)660661cpdef add_col(self, list indices, list coeffs):662r"""663Adds a column.664665INPUT:666667- ``indices`` (list of integers) -- this list constains the668indices of the constraints in which the variable's669coefficient is nonzero670671- ``coeffs`` (list of real values) -- associates a coefficient672to the variable in each of the constraints in which it673appears. Namely, the ith entry of ``coeffs`` corresponds to674the coefficient of the variable in the constraint675represented by the ith entry in ``indices``.676677.. NOTE::678679``indices`` and ``coeffs`` are expected to be of the same680length.681682EXAMPLE::683684sage: from sage.numerical.backends.generic_backend import get_solver685sage: p = get_solver(solver = "Coin") # optional - Coin686sage: p.ncols() # optional - Coin6870688sage: p.nrows() # optional - Coin6890690sage: p.add_linear_constraints(5, 0, None) # optional - Coin691sage: p.add_col(range(5), range(5)) # optional - Coin692sage: p.nrows() # optional - Coin6935694"""695696cdef int n = len(indices)697cdef int * c_indices = <int*>sage_malloc(n*sizeof(int))698cdef double * c_values = <double*>sage_malloc(n*sizeof(double))699cdef int i700701for 0<= i< n:702c_indices[i] = indices[i]703c_values[i] = coeffs[i]704705self.si.addCol (1, c_indices, c_values, 0, self.si.getInfinity(), 0)706707cpdef int solve(self) except -1:708r"""709Solves the problem.710711.. NOTE::712713This method raises ``MIPSolverException`` exceptions when714the solution can not be computed for any reason (none715exists, or the LP solver was not able to find it, etc...)716717EXAMPLE::718719sage: from sage.numerical.backends.generic_backend import get_solver720sage: p = get_solver(solver = "Coin") # optional - Coin721sage: p.add_linear_constraints(5, 0, None) # optional - Coin722sage: p.add_col(range(5), [1,2,3,4,5]) # optional - Coin723sage: p.solve() # optional - Coin7240725726TESTS::727728sage: from sage.numerical.backends.generic_backend import get_solver729sage: p = get_solver(solver = "Coin") # optional - Coin730sage: p.add_variable() # optional - Coin7310732sage: p.add_linear_constraint([(0, 1)], None, 4) # optional - Coin733sage: p.add_linear_constraint([(0, 1)], 6, None) # optional - Coin734sage: p.objective_coefficient(0,1) # optional - Coin735sage: p.solve() # optional - Coin736Traceback (most recent call last):737...738MIPSolverException: ...739"""740741# set up the model742cdef OsiSolverInterface * si = self.si743744cdef CbcModel * model745model = new CbcModel(si[0])746model.setLogLevel(self.model.logLevel())747748# multithreading749import multiprocessing750model.setNumberThreads(multiprocessing.cpu_count())751752model.branchAndBound()753754if model.solver().isAbandoned():755raise MIPSolverException("CBC : The solver has abandoned!")756757elif model.solver().isProvenPrimalInfeasible() or model.solver().isProvenDualInfeasible():758raise MIPSolverException("CBC : The problem or its dual has been proven infeasible!")759760elif (model.solver().isPrimalObjectiveLimitReached() or model.solver().isDualObjectiveLimitReached()):761raise MIPSolverException("CBC : The objective limit has been reached for the problem or its dual!")762763elif model.solver().isIterationLimitReached():764raise MIPSolverException("CBC : The iteration limit has been reached!")765766elif not model.solver().isProvenOptimal():767raise MIPSolverException("CBC : Unknown error")768769del self.model770self.model = model771772cpdef double get_objective_value(self):773r"""774Returns the value of the objective function.775776.. NOTE::777778Has no meaning unless ``solve`` has been called before.779780EXAMPLE::781782sage: from sage.numerical.backends.generic_backend import get_solver783sage: p = get_solver(solver = "Coin") # optional - Coin784sage: p.add_variables(2) # optional - Coin7851786sage: p.add_linear_constraint([(0, 1), (1, 2)], None, 3) # optional - Coin787sage: p.set_objective([2, 5]) # optional - Coin788sage: p.solve() # optional - Coin7890790sage: p.get_objective_value() # optional - Coin7917.5792sage: p.get_variable_value(0) # optional - Coin7930.0794sage: p.get_variable_value(1) # optional - Coin7951.5796"""797return self.model.solver().getObjValue() + self.obj_constant_term798799cpdef double get_variable_value(self, int variable):800r"""801Returns the value of a variable given by the solver.802803.. NOTE::804805Has no meaning unless ``solve`` has been called before.806807EXAMPLE::808809sage: from sage.numerical.backends.generic_backend import get_solver810sage: p = get_solver(solver = "Coin") # optional - Coin811sage: p.add_variables(2) # optional - Coin8121813sage: p.add_linear_constraint([(0, 1), (1, 2)], None, 3) # optional - Coin814sage: p.set_objective([2, 5]) # optional - Coin815sage: p.solve() # optional - Coin8160817sage: p.get_objective_value() # optional - Coin8187.5819sage: p.get_variable_value(0) # optional - Coin8200.0821sage: p.get_variable_value(1) # optional - Coin8221.5823"""824825cdef double * solution826solution = <double*> self.model.solver().getColSolution()827return solution[variable]828829cpdef int ncols(self):830r"""831Returns the number of columns/variables.832833EXAMPLE::834835sage: from sage.numerical.backends.generic_backend import get_solver836sage: p = get_solver(solver = "Coin") # optional - Coin837sage: p.ncols() # optional - Coin8380839sage: p.add_variables(2) # optional - Coin8401841sage: p.ncols() # optional - Coin8422843"""844845return self.si.getNumCols()846847cpdef int nrows(self):848r"""849Returns the number of rows/constraints.850851EXAMPLE::852853sage: from sage.numerical.backends.generic_backend import get_solver854sage: p = get_solver(solver = "Coin") # optional - Coin855sage: p.nrows() # optional - Coin8560857sage: p.add_linear_constraints(2, 2, None) # optional - Coin858sage: p.nrows() # optional - Coin8592860"""861return self.si.getNumRows()862863864cpdef bint is_variable_binary(self, int index):865r"""866Tests whether the given variable is of binary type.867868INPUT:869870- ``index`` (integer) -- the variable's id871872EXAMPLE::873874sage: from sage.numerical.backends.generic_backend import get_solver875sage: p = get_solver(solver = "Coin") # optional - Coin876sage: p.ncols() # optional - Coin8770878sage: p.add_variable() # optional - Coin8790880sage: p.set_variable_type(0,0) # optional - Coin881sage: p.is_variable_binary(0) # optional - Coin882True883884"""885886return (0 == self.si.isContinuous(index) and887self.variable_lower_bound(index) == 0 and888self.variable_upper_bound(index) == 1)889890cpdef bint is_variable_integer(self, int index):891r"""892Tests whether the given variable is of integer type.893894INPUT:895896- ``index`` (integer) -- the variable's id897898EXAMPLE::899900sage: from sage.numerical.backends.generic_backend import get_solver901sage: p = get_solver(solver = "Coin") # optional - Coin902sage: p.ncols() # optional - Coin9030904sage: p.add_variable() # optional - Coin9050906sage: p.set_variable_type(0,1) # optional - Coin907sage: p.is_variable_integer(0) # optional - Coin908True909"""910return (0 == self.si.isContinuous(index) and911(self.variable_lower_bound(index) != 0 or912self.variable_upper_bound(index) != 1))913914cpdef bint is_variable_continuous(self, int index):915r"""916Tests whether the given variable is of continuous/real type.917918INPUT:919920- ``index`` (integer) -- the variable's id921922EXAMPLE::923924sage: from sage.numerical.backends.generic_backend import get_solver925sage: p = get_solver(solver = "Coin") # optional - Coin926sage: p.ncols() # optional - Coin9270928sage: p.add_variable() # optional - Coin9290930sage: p.is_variable_continuous(0) # optional - Coin931True932sage: p.set_variable_type(0,1) # optional - Coin933sage: p.is_variable_continuous(0) # optional - Coin934False935936"""937return 1 == self.si.isContinuous(index)938939940cpdef bint is_maximization(self):941r"""942Tests whether the problem is a maximization943944EXAMPLE::945946sage: from sage.numerical.backends.generic_backend import get_solver947sage: p = get_solver(solver = "Coin") # optional - Coin948sage: p.is_maximization() # optional - Coin949True950sage: p.set_sense(-1) # optional - Coin951sage: p.is_maximization() # optional - Coin952False953"""954955return self.si.getObjSense() == -1956957cpdef variable_upper_bound(self, int index, value = False):958r"""959Returns or defines the upper bound on a variable960961INPUT:962963- ``index`` (integer) -- the variable's id964965- ``value`` -- real value, or ``None`` to mean that the966variable has not upper bound. When set to ``False``967(default), the method returns the current value.968969EXAMPLE::970971sage: from sage.numerical.backends.generic_backend import get_solver972sage: p = get_solver(solver = "Coin") # optional - Coin973sage: p.add_variable() # optional - Coin9740975sage: p.col_bounds(0) # optional - Coin976(0.0, None)977sage: p.variable_upper_bound(0, 5) # optional - Coin978sage: p.col_bounds(0) # optional - Coin979(0.0, 5.0)980"""981cdef double * ub982983if value == False:984ub = <double*> self.si.getColUpper()985return ub[index] if ub[index] != + self.si.getInfinity() else None986else:987self.si.setColUpper(index, value if value is not None else +self.si.getInfinity())988989cpdef variable_lower_bound(self, int index, value = False):990r"""991Returns or defines the lower bound on a variable992993INPUT:994995- ``index`` (integer) -- the variable's id996997- ``value`` -- real value, or ``None`` to mean that the998variable has not lower bound. When set to ``False``999(default), the method returns the current value.10001001EXAMPLE::10021003sage: from sage.numerical.backends.generic_backend import get_solver1004sage: p = get_solver(solver = "Coin") # optional - Coin1005sage: p.add_variable() # optional - Coin100601007sage: p.col_bounds(0) # optional - Coin1008(0.0, None)1009sage: p.variable_lower_bound(0, 5) # optional - Coin1010sage: p.col_bounds(0) # optional - Coin1011(5.0, None)1012"""1013cdef double * lb10141015if value == False:1016lb = <double*> self.si.getColLower()1017return lb[index] if lb[index] != - self.si.getInfinity() else None1018else:1019self.si.setColLower(index, value if value is not None else -self.si.getInfinity())10201021cpdef write_mps(self, char * filename, int modern):1022r"""1023Writes the problem to a .mps file10241025INPUT:10261027- ``filename`` (string)10281029EXAMPLE::10301031sage: from sage.numerical.backends.generic_backend import get_solver1032sage: p = get_solver(solver = "Coin") # optional - Coin1033sage: p.add_variables(2) # optional - Coin103411035sage: p.add_linear_constraint([(0, 1), (1, 2)], None, 3) # optional - Coin1036sage: p.set_objective([2, 5]) # optional - Coin1037sage: p.write_mps(SAGE_TMP+"/lp_problem.mps", 0) # optional - Coin1038"""10391040cdef char * mps = "mps"1041self.si.writeMps(filename, mps, -1 if self.is_maximization() else 1)10421043cpdef write_lp(self, char * filename):1044r"""1045Writes the problem to a .lp file10461047INPUT:10481049- ``filename`` (string)10501051EXAMPLE::10521053sage: from sage.numerical.backends.generic_backend import get_solver1054sage: p = get_solver(solver = "Coin") # optional - Coin1055sage: p.add_variables(2) # optional - Coin105611057sage: p.add_linear_constraint([(0, 1), (1, 2)], None, 3) # optional - Coin1058sage: p.set_objective([2, 5]) # optional - Coin1059sage: p.write_lp(SAGE_TMP+"/lp_problem.lp") # optional - Coin1060"""10611062cdef char * lp = "lp"1063self.si.writeLp(filename, lp, 0.00001, 10, 5, -1 if self.is_maximization() else 1, 1)10641065cpdef problem_name(self, char * name = NULL):1066r"""1067Returns or defines the problem's name10681069INPUT:10701071- ``name`` (``char *``) -- the problem's name. When set to1072``NULL`` (default), the method returns the problem's name.10731074EXAMPLE::10751076sage: from sage.numerical.backends.generic_backend import get_solver1077sage: p = get_solver(solver = "Coin") # optional - Coin1078sage: p.problem_name("There once was a french fry") # optional - Coin1079sage: print p.problem_name() # optional - Coin1080There once was a french fry1081"""1082if name == NULL:1083if self.prob_name != None:1084return self.prob_name1085else:1086return ""1087else:1088self.prob_name = name108910901091cpdef row_name(self, int index):1092r"""1093Returns the ``index`` th row name10941095INPUT:10961097- ``index`` (integer) -- the row's id10981099EXAMPLE::11001101sage: from sage.numerical.backends.generic_backend import get_solver1102sage: p = get_solver(solver = "Coin") # optional - Coin1103sage: p.add_linear_constraints(1, 2, None, names=['Empty constraint 1']) # optional - Coin1104sage: print p.row_name(0) # optional - Coin1105Empty constraint 11106"""1107if self.row_names != None:1108return self.row_names[index]1109else:1110return ""11111112cpdef col_name(self, int index):1113r"""1114Returns the ``index`` th col name11151116INPUT:11171118- ``index`` (integer) -- the col's id11191120EXAMPLE::11211122sage: from sage.numerical.backends.generic_backend import get_solver1123sage: p = get_solver(solver = "Coin") # optional - Coin1124sage: p.add_variable(name='I am a variable') # optional - Coin112501126sage: print p.col_name(0) # optional - Coin1127I am a variable1128"""1129if self.col_names != None:1130return self.col_names[index]1131else:1132return ""11331134cpdef CoinBackend copy(self):1135"""1136Returns a copy of self.11371138EXAMPLE::11391140sage: from sage.numerical.backends.generic_backend import get_solver1141sage: p = MixedIntegerLinearProgram(solver = "Coin") # optional - Coin1142sage: b = p.new_variable() # optional - Coin1143sage: p.add_constraint(b[1] + b[2] <= 6) # optional - Coin1144sage: p.set_objective(b[1] + b[2]) # optional - Coin1145sage: copy(p).solve() # optional - Coin11466.01147"""1148# create new backend1149cdef CoinBackend p = CoinBackend(maximization = (1 if self.is_maximization() else -1))11501151# replace solver with copy of self's solver1152del p.si1153p.si = self.si.clone(1)1154p.row_names = copy(self.row_names)1155p.col_names = copy(self.col_names)1156p.obj_constant_term = self.obj_constant_term1157# Maybe I should copy this, not sure -- seems complicated, though1158p.prob_name = self.prob_name11591160return p116111621163