Solving (undirected) graph coloring as a Mixed Integer Programming problem
Implements a class
GraphColoringMIP
,
with the following methods:
-
GraphColoringMIP.__init__(self, graph=None, n=None)
:graph
: an adjacency matrix representing the graph as a 2D numpy array of dimensions|V|
by|V|
where the value at row i and column j set to 0 indicates the absence of an edge and 1 indicates the presence of an edge, between the vertices indexed by i and j. Note that only the upper diagonal of this matrix will be considered for the representation of the graph, also, the MIP algorithm has non-polynomial nature, therefore, using adjacency list of lists doesn't accomplish meaningful results;
-
GraphColoringMIP._lpsolution2coloring(self)
: decodes the linear programming optimal solution vectorX*
into a human friendly format consisting of an array of|V|
elements each representing a vertex ofG
with a color (integer number) assigned to it; -
GraphColoringMIP._update_heuristic_min_chrom(self)
: updates an internal representation of the minimum chromatic number and it's certificate using a heuristic (algorithm which doesn't offer any guarantees of finding the optimum solution) consisting of a greedy algorithm. Returns the chromatic number found by the heuristic; -
GraphColoringMIP.model(self, ascopy=True, dtype=np.float64)
: instantiates internal arrays to be used for the linear programming and fill them up. The heuristic from the methodGraphColoringMIP._update_heuristic_min_chrom(self)
is used to find an upper limit on the amount of colors that may be used, this simple technique reduces a the number of required variables in the final MIP model, therefore, a solution may be found faster. Returns the LP data arrays;ascopy
: return copies of the LP data structures which may be safely modified outside the scope of this object;dtype
: sets the data type for the LP data arrays;
-
GraphColoringMIP.solve(self)
: solves the LP built byGraphColoringMIP.model(self, ascopy=True, dtype=np.float64)
. Returns a 2-tuple containing the (guaranteed) minimum chromatic number and the optimum solution vectorX*
in a decoded (human-friendly) format;
The input graph is:
[[0 1 0 0 0 1 0 1 1 0 0 1 1 0 1 0 1 0 1 1]
[1 0 0 1 1 0 0 1 1 1 0 0 0 0 0 1 1 0 0 0]
[0 0 0 1 1 1 1 1 1 1 1 0 0 1 1 0 0 1 1 0]
[0 1 1 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 1]
[0 1 1 0 0 1 0 1 0 1 1 1 0 0 0 0 1 0 1 0]
[1 0 1 0 1 0 0 0 0 0 1 0 0 1 1 0 1 1 1 1]
[0 0 1 0 0 0 0 1 0 1 0 0 0 1 0 0 1 1 0 1]
[1 1 1 0 1 0 1 0 1 0 0 0 0 1 0 0 1 1 0 1]
[1 1 1 0 0 0 0 1 0 1 1 1 1 1 0 0 0 1 1 0]
[0 1 1 0 1 0 1 0 1 0 0 0 1 1 0 1 0 1 0 0]
[0 0 1 0 1 1 0 0 1 0 0 1 1 0 0 1 1 0 0 0]
[1 0 0 0 1 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0]
[1 0 0 0 0 0 0 0 1 1 1 0 0 0 1 0 0 0 1 1]
[0 0 1 1 0 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1]
[1 0 1 1 0 1 0 0 0 0 0 0 1 0 0 1 0 0 0 0]
[0 1 0 0 0 0 0 0 0 1 1 0 0 0 1 0 0 0 0 0]
[1 1 0 0 1 1 1 1 0 0 1 0 0 0 0 0 0 0 0 0]
[0 0 1 1 0 1 1 1 1 1 0 0 0 0 0 0 0 0 1 0]
[1 0 1 0 1 1 0 0 1 0 0 0 1 1 0 0 0 1 0 1]
[1 0 0 1 0 1 1 1 0 0 0 0 1 1 0 0 0 0 1 0]]
It contains 20 vertices and 81 edges.
HEURISTIC MIN CHROM = 6
MIP optimization time (milliseconds): 82
The minimum chromatic number for the given input is: 5
The color to assign to vertex k matches the k-th one in the following array:
[0 1 0 2 3 1 1 2 4 2 2 1 1 3 4 0 4 3 2 4]
Further reference on the problem formulation and solution may be found here: https://gmmoliveira.github.io/blog/graph_coloring.html.