Skip to content

Commit 8af19f0

Browse files
Add "saturation first" and "independent set" greedy node coloring strategies (#1138)
This PR adds two more standard coloring strategies to greedy node/edge coloring functions graph_greedy_color and graph_greedy_edge_color. In the literature the first strategy is known as "saturation", "DSATUR" or "SLF". We dynamically choose the vertex that has the largest number of different colors already assigned to its neighbors, and, in case of a tie, the vertex that has the largest number of uncolored neighbors. The second strategy is known as "greedy independent sets" or "GIS". We greedily find independent subsets of the graph, and assign a different color to each of these subsets. This addresses #1137, modulo the fact that there are quadrillions of different greedy node coloring algorithms, and each comes with trillion variations. Both new strategies can be combined with preset_color_fn (for node coloring). There is also a new Rust enum GreedyStrategy exposed as a python class that allows to specify which of the three currently supported greedy strategies is used. This is, calling the functions from the Python code would be as follows: import rustworkx as rx graph = rx.generators.generalized_petersen_graph(5, 2) coloring = rx.graph_greedy_color(graph, greedy_strategy=rx.GreedyStrategy.Degree) coloring = rx.graph_greedy_color(graph, greedy_strategy=rx.GreedyStrategy.Saturation) coloring = rx.graph_greedy_color(graph, greedy_strategy=rx.GreedyStrategy.IndependentSet) coloring = rx.graph_greedy_color(graph) coloring = rx.graph_greedy_edge_color(graph) coloring = rx.graph_greedy_edge_color(graph, greedy_strategy=rx.GreedyStrategy.Degree) coloring = rx.graph_greedy_edge_color(graph, greedy_strategy=rx.GreedyStrategy.Saturation) coloring = rx.graph_greedy_edge_color(graph, greedy_strategy=rx.GreedyStrategy.IndependentSet) The greedy_graph_edge_color function has been also extended to support the preset_color_fn argument (though in this case it's an optional map from an edge index to either a color or to None) * initial commit * fix and test * python formatting * creating enums * also passing greedy strategy to edge coloring functions; improving docstrings * release notes' * python test * reno fix * reno fix * minor cleanup * small rust cleanup * adding greedy node coloring using maximal independent set + reworked tests * updating top-level source, tests and docs * docs * applying fix from code review * adding IndependentSet to pyi as well * Adding a table that describes greedy strategies available * converting table to sphinx-style * fix reference to footnotes * docs improvements * another attempt * attempt to extend API with preset_color_fn for edges * minor * finally compiling * fixing pyi API and edding python tests * adding rustworkx-core tests * adding arguments to docstrings * expanding release note * another docs fix * Update releasenotes/notes/saturation-greedy-color-109d40f189590d3a.yaml Co-authored-by: Matthew Treinish <[email protected]> * a round of renaming * restoring greedy_node_coloring API for backwards compatibility * restoring greedy_edge_coloring API for backwards compatibility * fix to rustworkx coloring * changing return type of new rustworkx-core functions to be Result * black * removing NodeIndexable in node coloring functions * and from edge coloring functions * suggestions from code review * suggestions from code review * release note update --------- Co-authored-by: Matthew Treinish <[email protected]>
1 parent e4dae8c commit 8af19f0

File tree

8 files changed

+1216
-27
lines changed

8 files changed

+1216
-27
lines changed

docs/source/api/algorithm_functions/coloring.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Coloring
66
.. autosummary::
77
:toctree: ../../apiref
88

9+
rustworkx.ColoringStrategy
910
rustworkx.graph_greedy_color
1011
rustworkx.graph_bipartite_edge_color
1112
rustworkx.graph_greedy_edge_color
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
---
2+
features:
3+
- Added a new class :class:`~.ColoringStrategy` used to specify the strategy used by
4+
the greedy node and edge coloring algorithms.
5+
The ``Degree`` strategy colors the nodes with higher degree first.
6+
The ``Saturation`` strategy dynamically chooses the vertex that has the largest
7+
number of different colors already assigned to its neighbors, and, in case of a tie,
8+
the vertex that has the largest number of uncolored neighbors.
9+
The ``IndependentSet`` strategy finds independent subsets of the graph, and assigns
10+
a different color to each of these subsets.
11+
- |
12+
The rustworkx-core ``coloring`` module has 2 new functions,
13+
``greedy_node_color_with_coloring_strategy`` and
14+
``greedy_edge_color_with_coloring_strategy``.
15+
These functions color respectively the nodes or the edges of the graph
16+
using the specified coloring strategy and handling the preset colors
17+
when provided.
18+
- |
19+
Added a new keyword argument, ``strategy``, to :func:`.graph_greedy_color`
20+
and to :func:`.graph_greedy_edge_color` to specify the greedy coloring strategy.
21+
22+
For example:
23+
24+
.. jupyter-execute::
25+
26+
import rustworkx as rx
27+
from rustworkx.visualization import mpl_draw
28+
29+
graph = rx.generators.generalized_petersen_graph(5, 2)
30+
31+
coloring = rx.graph_greedy_color(graph, strategy=rx.ColoringStrategy.Saturation)
32+
colors = [coloring[node] for node in graph.node_indices()]
33+
34+
layout = rx.shell_layout(graph, nlist=[[0, 1, 2, 3, 4],[6, 7, 8, 9, 5]])
35+
mpl_draw(graph, node_color=colors, pos=layout)
36+
- |
37+
Added a new keyword argument, ``preset_color_fn``, to :func:`.graph_greedy_edge_color`
38+
which is used to provide preset colors for specific edges when computing the graph
39+
coloring. You can optionally pass a callable to that argument which will
40+
be passed edge index from the graph and is either expected to return an
41+
integer color to use for that edge, or `None` to indicate there is no
42+
preset color for that edge. For example:
43+
44+
.. jupyter-execute::
45+
46+
import rustworkx as rx
47+
from rustworkx.visualization import mpl_draw
48+
49+
graph = rx.generators.generalized_petersen_graph(5, 2)
50+
51+
def preset_colors(edge_index):
52+
if edge_index == 0:
53+
return 3
54+
55+
coloring = rx.graph_greedy_edge_color(graph, preset_color_fn=preset_colors)
56+
colors = [coloring[edge] for edge in graph.edge_indices()]
57+
58+
layout = rx.shell_layout(graph, nlist=[[0, 1, 2, 3, 4], [6, 7, 8, 9, 5]])
59+
mpl_draw(graph, edge_color=colors, pos=layout)

0 commit comments

Comments
 (0)