Skip to content

Commit

Permalink
Add max weight matching function
Browse files Browse the repository at this point in the history
This commit adds a new python function max_weight_function() for
computing the maximum-weighted matching of a PyGraph object. The
implementation of this function is based on “Efficient Algorithms for
Finding Maximum Matching in Graphs”, Zvi Galil, ACM Computing Surveys,
1986. [1] It is basically a porting of the networkx implementation of
the algorithm [2] with some additional inspiration from the prototype for
the networkx version (it's basically the same code but some aspects were
a bit clearer to figure out from the prototype rather than networkx's
copy of it). [3][4]

Fixes #216

[1] https://dl.acm.org/doi/10.1145/6462.6502
[2] https://github.com/networkx/networkx/blob/3351206a3ce5b3a39bb2fc451e93ef545b96c95b/networkx/algorithms/matching.py
[3] http://jorisvr.nl/article/maximum-matching
[4] http://jorisvr.nl/files/graphmatching/20130407/mwmatching.py
  • Loading branch information
mtreinish committed Jan 17, 2021
1 parent 38938a1 commit 98fa26e
Show file tree
Hide file tree
Showing 6 changed files with 1,780 additions and 4 deletions.
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions docs/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ Algorithm Functions
retworkx.digraph_dfs_edges
retworkx.digraph_find_cycle
retworkx.digraph_union
retworkx.max_weight_matching

Exceptions
----------
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
features:
- |
Add a new function, :func:`~retworkx.max_weight_matching` for computing the
maximum-weighted matching for a :class:`~retworkx.PyGraph` object.
57 changes: 57 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ mod generators;
mod graph;
mod iterators;
mod k_shortest_path;
mod max_weight_matching;
mod union;

use std::cmp::{Ordering, Reverse};
Expand Down Expand Up @@ -2475,6 +2476,61 @@ pub fn cycle_basis(
cycles
}

/// Compute a maximum-weighted matching for a :class:`~retworkx.PyGraph`
///
/// A matching is a subset of edges in which no node occurs more than once.
/// The weight of a matching is the sum of the weights of its edges.
/// A maximal matching cannot add more edges and still be a matching.
/// The cardinality of a matching is the number of matched edges.
///
/// This function takes time :math:`O(n^3)` where ``n` is the number of nodes
/// in the graph.
///
/// This method is based on the "blossom" method for finding augmenting
/// paths and the "primal-dual" method for finding a matching of maximum
/// weight, both methods invented by Jack Edmonds [1]_.
///
/// :param PyGraph graph: The undirected graph to compute the max weight
/// matching for. Expects to have no parallel edges (multigraphs are
/// untested currently).
/// :param max_cardinality bool: If True, compute the maximum-cardinality
/// matching with maximum weight among all maximum-cardinality matchings.
/// Defaults False.
/// :param callable weight_fn: An optional callable that will be passed a
/// single argument the edge object for each edge in the graph. It is
/// expected to return an ``int`` weight for that edge. For example,
/// if the weights are all integers you can use: ``lambda x: x``. If not
/// specified the value for ``default_weight`` will be used for all
/// edge weights.
/// :param int default_weight: The ``int`` value to use for all edge weights
/// in the graph if ``weight_fn`` is not specified. Defaults to ``1``.
///
/// :returns: A dictionary of matching, the key is the node index and the
/// value is it's matched node index. Note that both directions will be
/// listed in the output, for example: ``{0: 1, 1: 0}``.
/// :rtype: dict
///
/// .. [1] "Efficient Algorithms for Finding Maximum Matching in Graphs",
/// Zvi Galil, ACM Computing Surveys, 1986.
///
#[pyfunction(max_cardinality = "false", default_weight = 1)]
#[text_signature = "(graph, /, max_cardinality=False, weight_fn=None, default_weight=1)"]
pub fn max_weight_matching(
py: Python,
graph: &graph::PyGraph,
max_cardinality: bool,
weight_fn: Option<PyObject>,
default_weight: i128,
) -> PyResult<HashMap<usize, usize>> {
max_weight_matching::max_weight_matching(
py,
graph,
max_cardinality,
weight_fn,
default_weight,
)
}

/// Compute the strongly connected components for a directed graph
///
/// This function is implemented using Kosaraju's algorithm
Expand Down Expand Up @@ -2644,6 +2700,7 @@ fn retworkx(py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_wrapped(wrap_pyfunction!(digraph_find_cycle))?;
m.add_wrapped(wrap_pyfunction!(digraph_k_shortest_path_lengths))?;
m.add_wrapped(wrap_pyfunction!(graph_k_shortest_path_lengths))?;
m.add_wrapped(wrap_pyfunction!(max_weight_matching))?;
m.add_class::<digraph::PyDiGraph>()?;
m.add_class::<graph::PyGraph>()?;
m.add_class::<iterators::BFSSuccessors>()?;
Expand Down
Loading

0 comments on commit 98fa26e

Please sign in to comment.