Skip to content
This repository has been archived by the owner on Jan 30, 2023. It is now read-only.

Commit

Permalink
PPLBackend: Add support for integer variables
Browse files Browse the repository at this point in the history
  • Loading branch information
Matthias Koeppe committed Apr 5, 2016
1 parent b0179c2 commit 144a970
Showing 1 changed file with 85 additions and 16 deletions.
101 changes: 85 additions & 16 deletions src/sage/numerical/backends/ppl_backend.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ AUTHORS:
#*****************************************************************************

from sage.numerical.mip import MIPSolverException
from sage.libs.ppl import MIP_Problem, Variable, Linear_Expression, Constraint, Generator
from sage.libs.ppl import MIP_Problem, Variable, Variables_Set, Linear_Expression, Constraint, Generator
from sage.rings.integer cimport Integer
from sage.rings.rational cimport Rational

Expand All @@ -36,6 +36,8 @@ cdef class PPLBackend(GenericBackend):
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

Expand All @@ -59,6 +61,7 @@ cdef class PPLBackend(GenericBackend):
self.name = ''
self.obj_constant_term = Rational(0)
self.obj_denominator = Integer(1)
self.integer_variables = set()

if maximization:
self.set_sense(+1)
Expand Down Expand Up @@ -94,6 +97,13 @@ cdef class PPLBackend(GenericBackend):

self.mip.add_space_dimensions_and_embed(len(self.objective_function))

# Integrality

ivar = Variables_Set()
for i in self.integer_variables:
ivar.insert(Variable(i))
self.mip.add_to_integer_space_dimensions(ivar)

# Objective function
mip_obj = Linear_Expression(0)
denom = Integer(1)
Expand Down Expand Up @@ -133,7 +143,7 @@ cdef class PPLBackend(GenericBackend):
else:
self.mip.set_optimization_mode('minimization')

cpdef int add_variable(self, lower_bound=0, upper_bound=None, binary=False, continuous=True, integer=False, obj=0, name=None) except -1:
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.
Expand Down Expand Up @@ -179,19 +189,28 @@ cdef class PPLBackend(GenericBackend):
sage: p.objective_coefficient(2)
2/3
sage: p.add_variable(integer=True)
Traceback (most recent call last):
...
NotImplementedError: The PPL backend in Sage only supports continuous variables
3
"""
if binary or integer:
raise NotImplementedError("The PPL backend in Sage only supports continuous variables")
cdef int vtype = int(bool(binary)) + int(bool(continuous)) + int(bool(integer))
if vtype == 0:
continuous = True
elif vtype != 1:
raise ValueError("Exactly one parameter of 'binary', 'integer' and 'continuous' must be 'True'.")

for i in range(len(self.Matrix)):
self.Matrix[i].append(0)
self.col_lower_bound.append(lower_bound)
self.col_upper_bound.append(upper_bound)
self.objective_function.append(obj)
self.col_name_var.append(name)
return len(self.objective_function) - 1

n = len(self.objective_function) - 1
if binary:
self.set_variable_type(n,0)
elif integer:
self.set_variable_type(n,1)

return n

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:
"""
Expand Down Expand Up @@ -264,20 +283,54 @@ cdef class PPLBackend(GenericBackend):
"""
Set the type of a variable.
INPUT:
- ``variable`` (integer) -- the variable's id
- ``vtype`` (integer) :
* 1 Integer
* 0 Binary
* -1 Continuous
EXAMPLE::
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):
...
Exception: ...
ValueError: ...
"""
if vtype != -1:
raise Exception('This backend does not handle integer variables ! Read the doc !')
if vtype == -1:
if variable in self.integer_variables:
self.integer_variables.remove(variable)
elif vtype == 0:
self.integer_variables.add(variable)
self.variable_lower_bound(variable, 0)
self.variable_upper_bound(variable, 1)
elif vtype == 1:
self.integer_variables.add(variable)
else:
raise ValueError("Invalid variable type: {}".format(vtype))

cpdef set_sense(self, int sense):
"""
Expand Down Expand Up @@ -524,28 +577,44 @@ cdef class PPLBackend(GenericBackend):
self.row_name_var.append(None)

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 LP solver was not able to find it, etc...)
exists, or the solver was not able to find it, etc...)
EXAMPLE::
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(range(5), 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
"""
self.init_mip()

Expand Down Expand Up @@ -809,7 +878,7 @@ cdef class PPLBackend(GenericBackend):
sage: p.is_variable_binary(0)
False
"""
return False
return index in self.integer_variables and self.col_bounds(index) == (0, 1)

cpdef bint is_variable_integer(self, int index):
"""
Expand All @@ -830,7 +899,7 @@ cdef class PPLBackend(GenericBackend):
sage: p.is_variable_integer(0)
False
"""
return False
return index in self.integer_variables and self.col_bounds(index) != (0, 1)

cpdef bint is_variable_continuous(self, int index):
"""
Expand All @@ -851,7 +920,7 @@ cdef class PPLBackend(GenericBackend):
sage: p.is_variable_continuous(0)
True
"""
return True
return index not in self.integer_variables

cpdef row_name(self, int index):
"""
Expand Down

0 comments on commit 144a970

Please sign in to comment.