Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
""" PPL Backend
AUTHORS:
- Risan (2012-02): initial implementation
- Jeroen Demeyer (2014-08-04) allow rational coefficients for constraints and objective function (:trac:`16755`) """
#***************************************************************************** # Copyright (C) 2010 Risan <ptrrsn.1@gmail.com> # Copyright (C) 2014 Jeroen Demeyer <jdemeyer@cage.ugent.be> # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function
from sage.rings.integer cimport Integer from sage.rings.rational cimport Rational from .generic_backend cimport GenericBackend
cdef class PPLBackend(GenericBackend):
""" MIP Backend that uses the exact MIP solver from the Parma Polyhedra Library.
General backend testsuite::
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: TestSuite(p).run(skip="_test_pickling") """
cdef object mip cdef list Matrix cdef list row_lower_bound cdef list row_upper_bound cdef list col_lower_bound cdef list col_upper_bound cdef list objective_function cdef list row_name_var cdef list col_name_var cdef int is_maximize cdef str name cdef object integer_variables
# Common denominator for objective function in self.mip (not for the constant term) cdef Integer obj_denominator
def __cinit__(self, maximization = True, base_ring = None): """ Constructor
EXAMPLES::
sage: p = MixedIntegerLinearProgram(solver = "PPL")
TESTS:
Raise an error if a ``base_ring`` is requested that is not supported::
sage: p = MixedIntegerLinearProgram(solver = "PPL", base_ring=AA) Traceback (most recent call last): ... TypeError: The PPL backend only supports rational data. """
else: self.set_sense(-1)
cpdef base_ring(self):
cpdef zero(self):
cpdef __copy__(self): """ Returns a copy of self.
EXAMPLES::
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = MixedIntegerLinearProgram(solver = "PPL") sage: b = p.new_variable() sage: p.add_constraint(b[1] + b[2] <= 6) sage: p.set_objective(b[1] + b[2]) sage: cp = copy(p.get_backend()) sage: cp.solve() 0 sage: cp.get_objective_value() 6 """
def init_mip(self): """ Converting the matrix form of the MIP Problem to PPL MIP_Problem.
EXAMPLES::
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver="PPL") sage: p.base_ring() Rational Field sage: type(p.zero()) <type 'sage.rings.rational.Rational'> sage: p.init_mip() """
# Common denominator (for objective function and every constraint) cdef Integer denom, newdenom
# Integrality
# Objective function
# Constraints
else:
cpdef int add_variable(self, lower_bound=0, upper_bound=None, binary=False, continuous=False, integer=False, obj=0, name=None) except -1: """ Add a variable.
This amounts to adding a new column to the matrix. By default, the variable is both positive and real.
It has not been implemented for selecting the variable type yet.
INPUT:
- ``lower_bound`` -- the lower bound of the variable (default: 0)
- ``upper_bound`` -- the upper bound of the variable (default: ``None``)
- ``binary`` -- ``True`` if the variable is binary (default: ``False``).
- ``continuous`` -- ``True`` if the variable is binary (default: ``True``).
- ``integer`` -- ``True`` if the variable is binary (default: ``False``).
- ``obj`` -- (optional) coefficient of this variable in the objective function (default: 0)
- ``name`` -- an optional name for the newly added variable (default: ``None``).
OUTPUT: The index of the newly created variable
EXAMPLES::
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: p.ncols() 0 sage: p.add_variable() 0 sage: p.ncols() 1 sage: p.add_variable(lower_bound=-2) 1 sage: p.add_variable(name='x',obj=2/3) 2 sage: p.col_name(2) 'x' sage: p.objective_coefficient(2) 2/3 sage: p.add_variable(integer=True) 3 """ raise ValueError("Exactly one parameter of 'binary', 'integer' and 'continuous' must be 'True'.")
self.set_variable_type(n,0)
cpdef int add_variables(self, int n, lower_bound=0, upper_bound=None, binary=False, continuous=True, integer=False, obj=0, names=None) except -1: """ Add ``n`` variables.
This amounts to adding new columns to the matrix. By default, the variables are both positive and real.
It has not been implemented for selecting the variable type yet.
INPUT:
- ``n`` -- the number of new variables (must be > 0)
- ``lower_bound`` -- the lower bound of the variable (default: 0)
- ``upper_bound`` -- the upper bound of the variable (default: ``None``)
- ``binary`` -- ``True`` if the variable is binary (default: ``False``).
- ``continuous`` -- ``True`` if the variable is binary (default: ``True``).
- ``integer`` -- ``True`` if the variable is binary (default: ``False``).
- ``obj`` -- (optional) coefficient of all variables in the objective function (default: 0)
- ``names`` -- optional list of names (default: ``None``)
OUTPUT: The index of the variable created last.
EXAMPLES::
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: p.ncols() 0 sage: p.add_variables(5) 4 sage: p.ncols() 5 sage: p.add_variables(2, lower_bound=-2.0, obj=42.0, names=['a','b']) 6
TESTS:
Check that arguments are used::
sage: p.col_bounds(5) # tol 1e-8 (-2.0, None) sage: p.col_name(5) 'a' sage: p.objective_coefficient(5) # tol 1e-8 42.0 """ raise NotImplementedError("The PPL backend in Sage only supports continuous variables") self.Matrix[i].append(0) else:
cpdef set_variable_type(self, int variable, int vtype): """ Set the type of a variable.
INPUT:
- ``variable`` (integer) -- the variable's id
- ``vtype`` (integer) :
* 1 Integer * 0 Binary * -1 Continuous
EXAMPLES::
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: p.add_variables(5) 4 sage: p.set_variable_type(0,1) sage: p.is_variable_integer(0) True sage: p.set_variable_type(3,0) sage: p.is_variable_integer(3) or p.is_variable_binary(3) True sage: p.col_bounds(3) # tol 1e-6 (0, 1) sage: p.set_variable_type(3, -1) sage: p.is_variable_continuous(3) True
TESTS:
Test that an exception is raised when an invalid type is passed::
sage: p.set_variable_type(3, -2) Traceback (most recent call last): ... ValueError: ... """ elif vtype == 0: elif vtype == 1: else:
cpdef set_sense(self, int sense): """ Set the direction (maximization/minimization).
INPUT:
- ``sense`` (integer) :
* +1 => Maximization * -1 => Minimization
EXAMPLES::
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: p.is_maximization() True sage: p.set_sense(-1) sage: p.is_maximization() False """ else:
cpdef objective_coefficient(self, int variable, coeff=None): """ Set or get the coefficient of a variable in the objective function
INPUT:
- ``variable`` (integer) -- the variable's id
- ``coeff`` (integer) -- its coefficient
EXAMPLES::
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: p.add_variable() 0 sage: p.objective_coefficient(0) 0 sage: p.objective_coefficient(0,2) sage: p.objective_coefficient(0) 2 """ else:
cpdef set_objective(self, list coeff, d=0): """ Set the objective function.
INPUT:
- ``coeff`` -- a list of real values, whose ith element is the coefficient of the ith variable in the objective function.
EXAMPLES::
sage: p = MixedIntegerLinearProgram(solver="PPL") sage: x = p.new_variable(nonnegative=True) sage: p.add_constraint(x[0]*5 + x[1]/11 <= 6) sage: p.set_objective(x[0]) sage: p.solve() 6/5 sage: p.set_objective(x[0]/2 + 1) sage: p.show() Maximization: 1/2 x_0 + 1 <BLANKLINE> Constraints: constraint_0: 5 x_0 + 1/11 x_1 <= 6 Variables: x_0 is a continuous variable (min=0, max=+oo) x_1 is a continuous variable (min=0, max=+oo) sage: p.solve() 8/5
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: p.add_variables(5) 4 sage: p.set_objective([1, 1, 2, 1, 3]) sage: [p.objective_coefficient(x) for x in range(5)] [1, 1, 2, 1, 3] """
cpdef set_verbosity(self, int level): Set the log (verbosity) level. Not Implemented.
EXAMPLES::
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: p.set_verbosity(0) """
cpdef add_linear_constraint(self, coefficients, lower_bound, upper_bound, name=None): """ Add a linear constraint.
INPUT:
- ``coefficients`` -- an iterable with ``(c,v)`` pairs where ``c`` is a variable index (integer) and ``v`` is a value (real value).
- ``lower_bound`` -- a lower bound, either a real value or ``None``
- ``upper_bound`` -- an upper bound, either a real value or ``None``
- ``name`` -- an optional name for this row (default: ``None``)
EXAMPLES::
sage: p = MixedIntegerLinearProgram(solver="PPL") sage: x = p.new_variable(nonnegative=True) sage: p.add_constraint(x[0]/2 + x[1]/3 <= 2/5) sage: p.set_objective(x[1]) sage: p.solve() 6/5 sage: p.add_constraint(x[0] - x[1] >= 1/10) sage: p.solve() 21/50 sage: p.set_max(x[0], 1/2) sage: p.set_min(x[1], 3/8) sage: p.solve() 2/5
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: p.add_variables(5) 4 sage: p.add_linear_constraint(zip(range(5), range(5)), 2.0, 2.0) sage: p.row(0) ([1, 2, 3, 4], [1, 2, 3, 4]) sage: p.row_bounds(0) (2.00000000000000, 2.00000000000000) sage: p.add_linear_constraint( zip(range(5), range(5)), 1.0, 1.0, name='foo') sage: p.row_name(-1) 'foo' """
cpdef add_col(self, list indices, list coeffs): """ Add a column.
INPUT:
- ``indices`` (list of integers) -- this list contains the indices of the constraints in which the variable's coefficient is nonzero
- ``coeffs`` (list of real values) -- associates a coefficient to the variable in each of the constraints in which it appears. Namely, the ith entry of ``coeffs`` corresponds to the coefficient of the variable in the constraint represented by the ith entry in ``indices``.
.. NOTE::
``indices`` and ``coeffs`` are expected to be of the same length.
EXAMPLES::
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: p.ncols() 0 sage: p.nrows() 0 sage: p.add_linear_constraints(5, 0, None) sage: p.add_col(list(range(5)), list(range(5))) sage: p.nrows() 5 """
cpdef add_linear_constraints(self, int number, lower_bound, upper_bound, names=None): """ Add constraints.
INPUT:
- ``number`` (integer) -- the number of constraints to add.
- ``lower_bound`` -- a lower bound, either a real value or ``None``
- ``upper_bound`` -- an upper bound, either a real value or ``None``
- ``names`` -- an optional list of names (default: ``None``)
EXAMPLES::
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: p.add_variables(5) 4 sage: p.add_linear_constraints(5, None, 2) sage: p.row(4) ([], []) sage: p.row_bounds(4) (None, 2) """ else:
cpdef int solve(self) except -1: # integer example copied from cplex_backend.pyx """ Solve the problem.
.. NOTE::
This method raises ``MIPSolverException`` exceptions when the solution can not be computed for any reason (none exists, or the solver was not able to find it, etc...)
EXAMPLES:
A linear optimization problem::
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: p.add_linear_constraints(5, 0, None) sage: p.add_col(list(range(5)), list(range(5))) sage: p.solve() 0
An unbounded problem::
sage: p.objective_coefficient(0,1) sage: p.solve() Traceback (most recent call last): ... MIPSolverException: ...
An integer optimization problem::
sage: p = MixedIntegerLinearProgram(solver='PPL') sage: x = p.new_variable(integer=True, nonnegative=True) sage: p.add_constraint(2*x[0] + 3*x[1], max = 6) sage: p.add_constraint(3*x[0] + 2*x[1], max = 6) sage: p.set_objective(x[0] + x[1] + 7) sage: p.solve() 9 """
pass
cpdef get_objective_value(self): """ Return the exact value of the objective function.
.. NOTE::
Behaviour is undefined unless ``solve`` has been called before.
EXAMPLES::
sage: p = MixedIntegerLinearProgram(solver="PPL") sage: x = p.new_variable(nonnegative=True) sage: p.add_constraint(5/13*x[0] + x[1]/2 == 8/7) sage: p.set_objective(5/13*x[0] + x[1]/2) sage: p.solve() 8/7
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: p.add_variables(2) 1 sage: p.add_linear_constraint([(0,1), (1,2)], None, 3) sage: p.set_objective([2, 5]) sage: p.solve() 0 sage: p.get_objective_value() 15/2 sage: p.get_variable_value(0) 0 sage: p.get_variable_value(1) 3/2 """
cpdef get_variable_value(self, int variable): """ Return the value of a variable given by the solver.
.. NOTE::
Behaviour is undefined unless ``solve`` has been called before.
EXAMPLES::
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: p.add_variables(2) 1 sage: p.add_linear_constraint([(0,1), (1, 2)], None, 3) sage: p.set_objective([2, 5]) sage: p.solve() 0 sage: p.get_objective_value() 15/2 sage: p.get_variable_value(0) 0 sage: p.get_variable_value(1) 3/2 """
cpdef int ncols(self): """ Return the number of columns/variables.
EXAMPLES::
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: p.ncols() 0 sage: p.add_variables(2) 1 sage: p.ncols() 2 """
cpdef int nrows(self): """ Return the number of rows/constraints.
EXAMPLES::
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: p.nrows() 0 sage: p.add_linear_constraints(2, 2.0, None) sage: p.nrows() 2 """
cpdef bint is_maximization(self): """ Test whether the problem is a maximization
EXAMPLES::
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: p.is_maximization() True sage: p.set_sense(-1) sage: p.is_maximization() False """ else:
cpdef problem_name(self, char * name = NULL): """ Return or define the problem's name
INPUT:
- ``name`` (``char *``) -- the problem's name. When set to ``NULL`` (default), the method returns the problem's name.
EXAMPLES::
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: p.problem_name("There once was a french fry") sage: print(p.problem_name()) There once was a french fry """
cpdef row(self, int i): """ Return a row
INPUT:
- ``index`` (integer) -- the constraint's id.
OUTPUT:
A pair ``(indices, coeffs)`` where ``indices`` lists the entries whose coefficient is nonzero, and to which ``coeffs`` associates their coefficient on the model of the ``add_linear_constraint`` method.
EXAMPLES::
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: p.add_variables(5) 4 sage: p.add_linear_constraint(zip(range(5), range(5)), 2, 2) sage: p.row(0) ([1, 2, 3, 4], [1, 2, 3, 4]) sage: p.row_bounds(0) (2, 2) """
cpdef row_bounds(self, int index): """ Return the bounds of a specific constraint.
INPUT:
- ``index`` (integer) -- the constraint's id.
OUTPUT:
A pair ``(lower_bound, upper_bound)``. Each of them can be set to ``None`` if the constraint is not bounded in the corresponding direction, and is a real value otherwise.
EXAMPLES::
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: p.add_variables(5) 4 sage: p.add_linear_constraint(zip(range(5), range(5)), 2, 2) sage: p.row(0) ([1, 2, 3, 4], [1, 2, 3, 4]) sage: p.row_bounds(0) (2, 2) """
cpdef col_bounds(self, int index): """ Return the bounds of a specific variable.
INPUT:
- ``index`` (integer) -- the variable's id.
OUTPUT:
A pair ``(lower_bound, upper_bound)``. Each of them can be set to ``None`` if the variable is not bounded in the corresponding direction, and is a real value otherwise.
EXAMPLES::
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: p.add_variable() 0 sage: p.col_bounds(0) (0, None) sage: p.variable_upper_bound(0, 5) sage: p.col_bounds(0) (0, 5) """
cpdef bint is_variable_binary(self, int index): """ Test whether the given variable is of binary type.
INPUT:
- ``index`` (integer) -- the variable's id
EXAMPLES::
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: p.ncols() 0 sage: p.add_variable() 0 sage: p.is_variable_binary(0) False """
cpdef bint is_variable_integer(self, int index): """ Test whether the given variable is of integer type.
INPUT:
- ``index`` (integer) -- the variable's id
EXAMPLES::
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: p.ncols() 0 sage: p.add_variable() 0 sage: p.is_variable_integer(0) False """
cpdef bint is_variable_continuous(self, int index): """ Test whether the given variable is of continuous/real type.
INPUT:
- ``index`` (integer) -- the variable's id
EXAMPLES::
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: p.ncols() 0 sage: p.add_variable() 0 sage: p.is_variable_continuous(0) True """
cpdef row_name(self, int index): """ Return the ``index`` th row name
INPUT:
- ``index`` (integer) -- the row's id
EXAMPLES::
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: p.add_linear_constraints(1, 2, None, names=["Empty constraint 1"]) sage: p.row_name(0) 'Empty constraint 1' """
cpdef col_name(self, int index): """ Return the ``index`` th col name
INPUT:
- ``index`` (integer) -- the col's id
- ``name`` (``char *``) -- its name. When set to ``NULL`` (default), the method returns the current name.
EXAMPLES::
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: p.add_variable(name="I am a variable") 0 sage: p.col_name(0) 'I am a variable' """
cpdef variable_upper_bound(self, int index, value = False): """ Return or define the upper bound on a variable
INPUT:
- ``index`` (integer) -- the variable's id
- ``value`` -- real value, or ``None`` to mean that the variable has not upper bound. When set to ``None`` (default), the method returns the current value.
EXAMPLES::
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: p.add_variable() 0 sage: p.col_bounds(0) (0, None) sage: p.variable_upper_bound(0, 5) sage: p.col_bounds(0) (0, 5) sage: p.variable_upper_bound(0, None) sage: p.col_bounds(0) (0, None) """ else: return self.col_upper_bound[index]
cpdef variable_lower_bound(self, int index, value = False): """ Return or define the lower bound on a variable
INPUT:
- ``index`` (integer) -- the variable's id
- ``value`` -- real value, or ``None`` to mean that the variable has not lower bound. When set to ``None`` (default), the method returns the current value.
EXAMPLES::
sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: p.add_variable() 0 sage: p.col_bounds(0) (0, None) sage: p.variable_lower_bound(0, 5) sage: p.col_bounds(0) (5, None) sage: p.variable_lower_bound(0, None) sage: p.col_bounds(0) (None, None) """ else: return self.col_lower_bound[index] |