Skip to content

Commit dcece27

Browse files
Adding edge coloring algorithm for bipartite graphs (#1026)
### Summary This PR adds a graph edge-coloring algorithm ``graph_bipartite_edge_color`` for **bipartite** graphs based on the paper "A simple algorithm for edge-coloring bipartite multigraphs" by Noga Alon, 2003. The above function first checks whether the graph is indeed bipartite, and raises an exception of type ``GraphNotBipartite`` if this is not the case. Otherwise, the function edge-colors the graph and returns a dictionary with key being the edge index and value being the assigned color. This is the same output format as produced by other recently added edge-coloring functions ``graph_greedy_edge_color`` and ``graph_misra_gries_edge_color``. The algorithm uses exactly `d` colors when `d` is the maximum degree of a node in the graph. Usage example: ``` import rustworkx as rx graph = rx.generators.heavy_hex_graph(9) edge_colors = rx.graph_bipartite_edge_color(graph) num_colors = max(edge_colors.values()) + 1 assert num_colors == 3 ``` The algorithm has a runtime of `O(m log m)` where `m` is the number of edges in the graph. ### A few technical details: Internally this works with an undirected regular bipartite multigraph that - keeps an explicit partition of its nodes into "left nodes" and "right nodes" - only the edges connecting a left node to a right node are allowed (this is what _bipartite_ means) - each node in the graph has the same degree `r` (this is what _regular_ means) - each edge keeps additional data representing its _multiplicity_ (aka _weight_) and whether or not the edge is _bad_ (it is important that multiple edges connecting the same pairs of nodes are grouped into a single edges with multiplicity) The internal data structure for the above is called `RegularBipartiteMultiGraph`. I don't foresee it being used anywhere outside of this PR, and so it's not marked as ``pub``. There is one possible optimization that is mentioned in the paper but which I have not implemented. Let `r` be the maximum degree of a node in the original graph. If the original bipartite graph is not regular (i.e. some of its nodes have degree less than `r`), then extra vertices and edges are inserted to make it regular (and of degree `r`) with the final edge-coloring is obtained by restricting the edge-coloring of the multi-graph to original edges. In addition (this is the mentioned possible optimization), one could group multiple "left" vertices with total degree not exceeding `r` into a _single_ node in the multigraph; the same applies for subsets of right vertices. The implementation also contains a function ``euler_cycles`` that might be useful in general, however the flavor needed here is somewhat non-standard, and so I did not expose it. The standard definition assumes that the graph is connected and an _Euler cycle_ is a path that visits each edge exactly once and comes back to the starting vertex. In our case, however, the graph may be disconnected and the function ``euler_cycles`` returns a list of Euler cycles that visit each edge exactly once (however this is not an Euler cycle for the standard definition). * very messy initial implementation * adding random_bipartite_graph and some tests * tests * fix correctness checking functions * imports in test module * starting cleanup * minor * cleaning pass * fmt * fix for empty graphs * python tests + fmt * release notes * first round of clippy * clippy * minor * pylint * fmt * renaming * more renaming * api docs * release notes fix * correct release notes fix * yet another fix * Add Python type annotation to stub files * Fix stubs * adding python interface for creating random bipartite graphs, both directed and undirected * stubs * updating runtime complexity to include the number of nodes * derive(Clone) * panic! -> unreachable! * making bipartite_edge_color_with_partitions non-public, cleaning and polishing tests
1 parent e39ecc6 commit dcece27

File tree

14 files changed

+1573
-1
lines changed

14 files changed

+1573
-1
lines changed

docs/source/api/algorithm_functions/coloring.rst

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

99
rustworkx.graph_greedy_color
10+
rustworkx.graph_bipartite_edge_color
1011
rustworkx.graph_greedy_edge_color
1112
rustworkx.graph_misra_gries_edge_color

docs/source/api/random_graph_generator_functions.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@ Random Graph Generator Functions
1313
rustworkx.random_geometric_graph
1414
rustworkx.barabasi_albert_graph
1515
rustworkx.directed_barabasi_albert_graph
16+
rustworkx.directed_random_bipartite_graph
17+
rustworkx.undirected_random_bipartite_graph
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
features:
3+
- |
4+
Added a new exception class :class:`~.GraphNotBipartite` which is raised when a
5+
graph is not bipartite. The sole user of this exception is the :func:`~.graph_bipartite_edge_color`
6+
which will raise it when the user provided graph is not bipartite.
7+
- |
8+
Added a new function, :func:`~.graph_bipartite_edge_color` to color edges
9+
of a :class:`~.PyGraph` object. The function first checks whether a graph is
10+
bipartite, raising exception of type :class:`~.GraphNotBipartite` if this is not the case.
11+
Otherwise, the function calls the algorithm for edge-coloring bipartite graphs,
12+
and returns a dictionary with key being the edge index and value being the assigned
13+
color.
14+
15+
The implemented algorithm is based on the paper "A simple algorithm for edge-coloring
16+
bipartite multigraphs" by Noga Alon, 2003.
17+
18+
The coloring produces at most d colors where d is the maximum degree of a node in the graph.
19+
The algorithm runs in time ``O (n + m log m)``, where ``n`` is the number of vertices and
20+
``m`` is the number of edges in the graph.
21+
22+
.. jupyter-execute::
23+
24+
import rustworkx as rx
25+
from rustworkx.visualization import mpl_draw
26+
27+
graph = rx.generators.cycle_graph(8)
28+
edge_colors = rx.graph_bipartite_edge_color(graph)
29+
assert edge_colors == {0: 0, 1: 1, 2: 0, 3: 1, 4: 0, 5: 1, 6: 0, 7: 1}
30+
mpl_draw(graph, edge_color=[edge_colors[i] for i in range(graph.num_edges())])
31+
- |
32+
Added two new random graph generator functions,
33+
:func:`.directed_random_bipartite_graph` and :func:`.undirected_random_bipartite_graph`,
34+
to generate a random bipartite graph. For example:
35+
36+
.. jupyter-execute::
37+
38+
import rustworkx as rx
39+
from rustworkx.visualization import mpl_draw
40+
41+
random_graph = rx.undirected_random_bipartite_graph(10, 5, 0.5, seed=20)
42+
mpl_draw(random_graph)

0 commit comments

Comments
 (0)