Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added method to find constraints value for MILP #38055

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 94 additions & 19 deletions src/sage/numerical/mip.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ also implements the :class:`MIPSolverException` exception, as well as the
:meth:`~MixedIntegerLinearProgram.show` | Display the ``MixedIntegerLinearProgram`` in a human-readable
:meth:`~MixedIntegerLinearProgram.solve` | Solve the ``MixedIntegerLinearProgram``
:meth:`~MixedIntegerLinearProgram.solver_parameter` | Return or define a solver parameter
:meth:`~MixedIntegerLinearProgram.sum` | Efficiently computes the sum of a sequence of LinearFunction elements
:meth:`~MixedIntegerLinearProgram.result` | Efficiently computes the result of a sequence of LinearFunction elements
:meth:`~MixedIntegerLinearProgram.write_lp` | Write the linear program as a LP file
:meth:`~MixedIntegerLinearProgram.write_mps` | Write the linear program as a MPS file

Expand Down Expand Up @@ -319,7 +319,7 @@ cdef class MixedIntegerLinearProgram(SageObject):
sage: g = graphs.PetersenGraph()
sage: p = MixedIntegerLinearProgram(maximization=True, solver='GLPK')
sage: b = p.new_variable(binary=True)
sage: p.set_objective(sum([b[v] for v in g]))
sage: p.set_objective(result([b[v] for v in g]))
sage: for (u,v) in g.edges(sort=False, labels=None):
....: p.add_constraint(b[u] + b[v], max=1)
sage: p.solve(objective_only=True)
Expand All @@ -336,9 +336,9 @@ cdef class MixedIntegerLinearProgram(SageObject):
....: p = MixedIntegerLinearProgram(solver='GLPK')
....: box = p.new_variable(nonnegative=True, **{type: True})
....: for b in range(k):
....: p.add_constraint(p.sum([items[i]*box[i,b] for i in range(len(items))]) <= maximum)
....: p.add_constraint(p.result([items[i]*box[i,b] for i in range(len(items))]) <= maximum)
....: for i in range(len(items)):
....: p.add_constraint(p.sum([box[i,b] for b in range(k)]) == 1)
....: p.add_constraint(p.result([box[i,b] for b in range(k)]) == 1)
....: p.set_objective(None)
....: _ = p.solve()
....: box = p.get_values(box)
Expand Down Expand Up @@ -417,7 +417,7 @@ cdef class MixedIntegerLinearProgram(SageObject):
sage: C = sage.numerical.mip.MixedIntegerLinearProgram
sage: import gc
sage: _ = gc.collect() # avoid side effects of other doc tests
sage: sum([1 for x in gc.get_objects() if isinstance(x,C)])
sage: result([1 for x in gc.get_objects() if isinstance(x,C)])
0

We now disable the cyclic garbage collector. Since :issue:`12616` avoids
Expand All @@ -427,7 +427,7 @@ cdef class MixedIntegerLinearProgram(SageObject):

sage: gc.disable()
sage: just_create_variables()
sage: sum([1 for x in gc.get_objects() if isinstance(x,C)])
sage: result([1 for x in gc.get_objects() if isinstance(x,C)])
0
sage: gc.enable()
"""
Expand Down Expand Up @@ -804,7 +804,7 @@ cdef class MixedIntegerLinearProgram(SageObject):
y[1] = x_1 is a continuous variable (min=-oo, max=+oo)
z[2] = x_2 is a continuous variable (min=-oo, max=+oo)
"""
if sum([real, binary, integer]) > 1:
if result([real, binary, integer]) > 1:
raise ValueError("Exactly one of the available types has to be True")

if binary:
Expand Down Expand Up @@ -1519,6 +1519,80 @@ cdef class MixedIntegerLinearProgram(SageObject):
return self._backend_variable_value_ZZ(v, tolerance)
return self.base_ring()(self._backend_variable_value(v, tolerance))

def constraints_values(self, indices=None):
r"""
Return values of constraints according to the current values of variables.

INPUT:

- ``indices`` -- select which constraint(s) values to return

- If ``indices = None``, the method returns the list of all the
constraints values.

- If ``indices`` is an integer `i`, the method returns value of constraint
`i`.

- If ``indices`` is a list of integers, the method returns the list
of the corresponding value of constraints.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- ``indices`` -- select which constraint(s) values to return
- If ``indices = None``, the method returns the list of all the
constraints values.
- If ``indices`` is an integer `i`, the method returns value of constraint
`i`.
- If ``indices`` is a list of integers, the method returns the list
of the corresponding value of constraints.
- ``indices`` -- select which constraint(s) values to return:
* ``None`` - return the list of all the constraints values
* an integer `i` - return the value of constraint `i`
* a list of integers - return the list of the
corresponding value of constraints

OUTPUT:

A list of numbers according to the input.

EXAMPLES::

sage: p = MixedIntegerLinearProgram(maximization=True, solver='GLPK')
sage: x = p.new_variable(nonnegative=True)
sage: p.set_objective(5*x[1] + 4*x[2])
sage: p.add_constraint(6*x[1] + 4*x[2], max=24)
sage: p.add_constraint(x[1] + 2*x[2], max=6)
sage: p.add_constraint(-x[1] + x[2], max=1)
sage: p.add_constraint(x[2], max=2)
sage: p.solve()
21.0
sage: p.get_values(x)
{1: 3.0, 2: 1.5}
sage: p.constraints_values()
[24.0, 6.0, -1.5, 1.5]

TESTS::
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
TESTS::
TESTS:


Check if the integer input is outside the valid input range::

sage: p = MixedIntegerLinearProgram(maximization=True, solver='GLPK')
sage: x = p.new_variable(nonnegative=True)
sage: p.set_objective(3*x[1] + 4*x[2])
sage: p.add_constraint(2*x[1] + x[2], max=20)
sage: p.add_constraint(4*x[1] + 3*x[2], max=6)
sage: p.solve()
8.0
sage: p.constraints_values(1)
[6.0]
sage: p.constraints_values(2)
Traceback (most recent call last):
...
ValueError: invalid row index 2

"""
from sage.rings.integer_ring import ZZ
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ValueError: invalid row index 2
"""
from sage.rings.integer_ring import ZZ
ValueError: invalid row index 2
"""
from sage.rings.integer_ring import ZZ


# calculate value of constraint 'i'
def calculate_value(self, i):
s = 0
cons = self.constraints(i)[1]
for index, coeff in zip(cons[0], cons[1]):
s += var_val[index] * coeff
return s

var = [n for n in self._variables]
var_val = self.get_values([n for n in var])
if indices is None:
indices = [n for n in range(self.number_of_constraints())]
elif indices in ZZ:
indices = [ZZ(indices)]
return [calculate_value(self, i) for i in indices]

def get_values(self, *lists, convert=None, tolerance=None):
r"""
Return values found by the previous call to ``solve()``.
Expand Down Expand Up @@ -2014,14 +2088,14 @@ cdef class MixedIntegerLinearProgram(SageObject):
Trivially true empty constraint:

sage: p = MixedIntegerLinearProgram(solver='GLPK')
sage: p.add_constraint(sum([]), max=2)
sage: p.add_constraint(result([]), max=2)
sage: p.solve()
0.0

Infeasible empty constraint::

sage: p = MixedIntegerLinearProgram(solver='GLPK')
sage: p.add_constraint(sum([]), min=2)
sage: p.add_constraint(result([]), min=2)
sage: p.solve()
Traceback (most recent call last):
...
Expand Down Expand Up @@ -2615,7 +2689,7 @@ cdef class MixedIntegerLinearProgram(SageObject):
sage: g = graphs.PetersenGraph()
sage: p = MixedIntegerLinearProgram(maximization=True, solver='GLPK')
sage: b = p.new_variable(nonnegative=True)
sage: p.set_objective(sum([b[v] for v in g]))
sage: p.set_objective(result([b[v] for v in g]))
sage: for (u,v) in g.edges(sort=False, labels=None):
....: p.add_constraint(b[u] + b[v], max=1)
sage: p.set_binary(b)
Expand Down Expand Up @@ -2862,9 +2936,10 @@ cdef class MixedIntegerLinearProgram(SageObject):
else:
self._backend.solver_parameter(name, value)

cpdef sum(self, L):
cpdef result(self, L):
r"""
Efficiently compute the sum of a sequence of

Efficiently computes the result of a sequence of
:class:`~sage.numerical.linear_functions.LinearFunction` elements

INPUT:
Expand All @@ -2876,7 +2951,7 @@ cdef class MixedIntegerLinearProgram(SageObject):

.. NOTE::

The use of the regular ``sum`` function is not recommended
The use of the regular ``result`` function is not recommended
as it is much less efficient than this one

EXAMPLES::
Expand All @@ -2886,11 +2961,11 @@ cdef class MixedIntegerLinearProgram(SageObject):

The following command::

sage: s = p.sum(v[i] for i in range(90))
sage: s = p.result(v[i] for i in range(90))

is much more efficient than::

sage: s = sum(v[i] for i in range(90))
sage: s = result(v[i] for i in range(90))
"""
d = {}
for v in L:
Expand Down Expand Up @@ -2972,9 +3047,9 @@ cdef class MixedIntegerLinearProgram(SageObject):
sage: p = MixedIntegerLinearProgram(solver='GLPK')
sage: p.solver_parameter("mip_gap_tolerance",100)
sage: b = p.new_variable(binary=True)
sage: p.set_objective(p.sum(b[v] for v in g))
sage: p.set_objective(p.result(b[v] for v in g))
sage: for v in g:
....: p.add_constraint(b[v] + p.sum(b[u] for u in g.neighbors(v)) <= 1)
....: p.add_constraint(b[v] + p.result(b[u] for u in g.neighbors(v)) <= 1)
sage: p.add_constraint(b[v] == 1) # Force an easy non-0 solution
sage: p.solve() # rel tol 100
1.0
Expand Down Expand Up @@ -3007,9 +3082,9 @@ cdef class MixedIntegerLinearProgram(SageObject):
sage: p = MixedIntegerLinearProgram(solver='GLPK')
sage: p.solver_parameter("mip_gap_tolerance",100)
sage: b = p.new_variable(binary=True)
sage: p.set_objective(p.sum(b[v] for v in g))
sage: p.set_objective(p.result(b[v] for v in g))
sage: for v in g:
....: p.add_constraint(b[v] + p.sum(b[u] for u in g.neighbors(v)) <= 1)
....: p.add_constraint(b[v] + p.result(b[u] for u in g.neighbors(v)) <= 1)
sage: p.add_constraint(b[v] == 1) # Force an easy non-0 solution
sage: p.solve() # rel tol 100
1.0
Expand Down
Loading