Skip to content

Commit

Permalink
Trac #23658: Fractional Chromatic Index Infinite Loop
Browse files Browse the repository at this point in the history
The implementation contains an infinite loop that is broken when a
quantity is less than or equal to 1, however the loop never ends on the
following input:

{{{
sage: g=graphs.PetersenGraph()
sage: g.fractional_chromatic_index(solver="GLPK")
}}}

The problem seems to depend on the LP solver, using PPL seems to work
just fine. Issue seems to be GLPK and CBC/Coin.

Relevant sage-devel thread at [1].

Relevant ask.sagemath thread at [2].

[1]: https://groups.google.com/forum/#!topic/sage-devel/hsKxANYAeQI

[2]: https://ask.sagemath.org/question/38543/fractional_chromatic_index-
in-sage-76-gets-stuck-in-loop-adding-constraints/

URL: https://trac.sagemath.org/23658
Reported by: fidelbarrera
Ticket author(s): David Coudert
Reviewer(s): Dima Pasechnik
  • Loading branch information
Release Manager authored and vbraun committed Sep 3, 2017
2 parents 34f6c76 + ceb0eb8 commit d996058
Showing 1 changed file with 38 additions and 26 deletions.
64 changes: 38 additions & 26 deletions src/sage/graphs/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -4695,8 +4695,7 @@ def fractional_chromatic_index(self, solver = None, verbose_constraints = 0, ver
\forall e \in E(G), \sum_{e \in M_i} \alpha_i \geq 1
For more information, see the `Wikipedia article on fractional coloring
<http://en.wikipedia.org/wiki/Fractional_coloring>`_.
For more information, see :wikipedia:`Fractional_coloring`.
ALGORITHM:
Expand Down Expand Up @@ -4729,15 +4728,6 @@ def fractional_chromatic_index(self, solver = None, verbose_constraints = 0, ver
- ``verbose`` -- level of verbosity required from the LP solver
.. NOTE::
This implementation can be improved by computing matchings through a
LP formulation, and not using the Python implementation of Edmonds'
algorithm (which requires to copy the graph, etc). It may be more
efficient to write the matching problem as a LP, as we would then
just have to update the weights on the edges between each call to
``solve`` (and so avoiding the generation of all the constraints).
EXAMPLES:
The fractional chromatic index of a `C_5` is `5/2`::
Expand All @@ -4750,49 +4740,71 @@ def fractional_chromatic_index(self, solver = None, verbose_constraints = 0, ver
sage: g.fractional_chromatic_index(solver="PPL")
5/2
TESTS:
Ticket :trac:`23658` is fixed::
sage: g = graphs.PetersenGraph()
sage: g.fractional_chromatic_index(solver='GLPK')
3.0
sage: g.fractional_chromatic_index(solver='PPL')
3
"""
self._scream_if_not_simple()

if not self.order():
return 0
if not self.size():
return 1

from sage.numerical.mip import MixedIntegerLinearProgram

g = copy(self)
#
# Initialize LP for maximum weigth matching
M = MixedIntegerLinearProgram(solver=solver, constraint_generation=True)

# One variable per edge
b = M.new_variable(binary=True, nonnegative=True)
B = lambda x,y : b[x,y] if x<y else b[y,x]

# We want to select at most one incident edge per vertex (matching)
for u in self.vertex_iterator():
M.add_constraint( M.sum( B(x,y) for x,y in self.edges_incident(u, labels=0) ) <= 1 )

#
# Initialize LP for fractional chromatic number
p = MixedIntegerLinearProgram(solver=solver, constraint_generation = True)

# One variable per edge
r = p.new_variable(nonnegative=True)
R = lambda x,y : r[x,y] if x<y else r[y,x]

# We want to maximize the sum of weights on the edges
p.set_objective( p.sum( R(u,v) for u,v in g.edges(labels = False)))
p.set_objective( p.sum( R(u,v) for u,v in self.edges(labels = False)))

# Each edge being by itself a matching, its weight can not be more than
# 1

for u,v in g.edges(labels = False):
for u,v in self.edges(labels = False):
p.add_constraint( R(u,v), max = 1)

obj = p.solve(log = verbose)

while True:

# Updating the value on the edges of g
for u,v in g.edges(labels = False):
g.set_edge_label(u,v,p.get_values(R(u,v)))

# Computing a matching of maximum weight...

matching = g.matching(use_edge_labels=True)
# Update the weights of edges for the matching problem
M.set_objective( M.sum( p.get_values(R(u,v)) * B(u,v) for u,v in self.edge_iterator(labels=0) ) )

# If the maximum matching has weight at most 1, we are done !
if sum((x[2] for x in matching)) <= 1:
if M.solve(log = verbose) <= 1:
break

# Otherwise, we add a new constraint

matching = [(u,v) for u,v in self.edge_iterator(labels=0) if M.get_values(B(u,v)) == 1]
p.add_constraint( p.sum( R(u,v) for u,v in matching), max = 1)
if verbose_constraints:
print("Adding a constraint on matching : {}".format(matching))

p.add_constraint( p.sum( R(u,v) for u,v,_ in matching), max = 1)

# And solve again
obj = p.solve(log = verbose)

Expand Down

0 comments on commit d996058

Please sign in to comment.