Skip to content
This repository was archived by the owner on Jan 30, 2023. It is now read-only.

Commit 5bb7623

Browse files
Release Managervbraun
authored andcommitted
Trac #22559: Matchings in Bipartite Graphs
The Hopcroft-Karp and Eppstein algorithms as implemented in NetworkX compute matchings for bipartite graphs faster than the algorithms for general graphs. I've included an override of the matching method in the bipartite graph class, with parameters that allow for using the old algorithms if desired. URL: https://trac.sagemath.org/22559 Reported by: zgershkoff Ticket author(s): Zachary Gershkoff Reviewer(s): Julian Rüth, Travis Scrimshaw, David Coudert
2 parents 2b9da89 + 5924a34 commit 5bb7623

File tree

1 file changed

+134
-0
lines changed

1 file changed

+134
-0
lines changed

src/sage/graphs/bipartite_graph.py

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,11 @@
3333
#*****************************************************************************
3434
from __future__ import print_function
3535
from __future__ import absolute_import
36+
from six import iteritems
3637
from six.moves import range
3738

3839
from .graph import Graph
40+
from sage.rings.integer import Integer
3941

4042
class BipartiteGraph(Graph):
4143
r"""
@@ -1281,3 +1283,135 @@ def reduced_adjacency_matrix(self, sparse=True):
12811283
# now construct and return the matrix from the dictionary we created
12821284
from sage.matrix.constructor import matrix
12831285
return matrix(len(self.right), len(self.left), D, sparse=sparse)
1286+
1287+
def matching(self, value_only=False, algorithm=None,
1288+
use_edge_labels=False, solver=None, verbose=0):
1289+
r"""
1290+
Return a maximum matching of the graph represented by the list of its
1291+
edges.
1292+
1293+
Given a graph `G` such that each edge `e` has a weight `w_e`, a maximum
1294+
matching is a subset `S` of the edges of `G` of maximum weight such that
1295+
no two edges of `S` are incident with each other.
1296+
1297+
INPUT:
1298+
1299+
- ``value_only`` -- boolean (default: ``False``); when set to ``True``,
1300+
only the cardinal (or the weight) of the matching is returned
1301+
1302+
- ``algorithm`` -- string (default: ``"Hopcroft-Karp"`` if
1303+
``use_edge_labels==False``, otherwise ``"Edmonds"``)
1304+
1305+
- ``"Hopcroft-Karp"`` selects the default bipartite graph algorithm as
1306+
implemented in NetworkX
1307+
1308+
- ``"Eppstein"`` selects Eppstein's algorithm as implemented in
1309+
NetworkX
1310+
1311+
- ``"Edmonds"`` selects Edmonds' algorithm as implemented in NetworkX
1312+
1313+
- ``"LP"`` uses a Linear Program formulation of the matching problem
1314+
1315+
- ``use_edge_labels`` -- boolean (default: ``False``)
1316+
1317+
- when set to ``True``, computes a weighted matching where each edge
1318+
is weighted by its label (if an edge has no label, `1` is assumed);
1319+
only if ``algorithm`` is ``"Edmonds"``, ``"LP"``
1320+
1321+
- when set to ``False``, each edge has weight `1`
1322+
1323+
- ``solver`` -- (default: ``None``) a specific Linear Program (LP)
1324+
solver to be used
1325+
1326+
- ``verbose`` -- integer (default: ``0``); sets the level of verbosity:
1327+
set to 0 by default, which means quiet
1328+
1329+
.. SEEALSO::
1330+
1331+
- :wikipedia:`Matching_(graph_theory)`
1332+
- :meth:`~Graph.matching`
1333+
1334+
EXAMPLES:
1335+
1336+
Maximum matching in a cycle graph::
1337+
1338+
sage: G = BipartiteGraph(graphs.CycleGraph(10))
1339+
sage: G.matching()
1340+
[(0, 1, None), (2, 3, None), (4, 5, None), (6, 7, None), (8, 9, None)]
1341+
1342+
The size of a maximum matching in a complete bipartite graph using
1343+
Eppstein::
1344+
1345+
sage: G = BipartiteGraph(graphs.CompleteBipartiteGraph(4,5))
1346+
sage: G.matching(algorithm="Eppstein", value_only=True)
1347+
4
1348+
1349+
TESTS:
1350+
1351+
If ``algorithm`` is not set to one of the supported algorithms, an
1352+
exception is raised::
1353+
1354+
sage: G = BipartiteGraph(graphs.CompleteBipartiteGraph(4,5))
1355+
sage: G.matching(algorithm="somethingdifferent")
1356+
Traceback (most recent call last):
1357+
...
1358+
ValueError: algorithm must be "Hopcroft-Karp", "Eppstein", "Edmonds" or "LP"
1359+
1360+
Maximum matching in a weighted bipartite graph::
1361+
1362+
sage: G = graphs.CycleGraph(4)
1363+
sage: B = BipartiteGraph([(u,v,2) for u,v in G.edges(labels=0)])
1364+
sage: B.matching(use_edge_labels=True)
1365+
[(0, 3, 2), (1, 2, 2)]
1366+
sage: B.matching(use_edge_labels=True, value_only=True)
1367+
4
1368+
sage: B.matching(use_edge_labels=True, value_only=True, algorithm='Edmonds')
1369+
4
1370+
sage: B.matching(use_edge_labels=True, value_only=True, algorithm='LP')
1371+
4.0
1372+
sage: B.matching(use_edge_labels=True, value_only=True, algorithm='Eppstein')
1373+
Traceback (most recent call last):
1374+
...
1375+
ValueError: use_edge_labels can not be used with "Hopcroft-Karp" or "Eppstein"
1376+
sage: B.matching(use_edge_labels=True, value_only=True, algorithm='Hopcroft-Karp')
1377+
Traceback (most recent call last):
1378+
...
1379+
ValueError: use_edge_labels can not be used with "Hopcroft-Karp" or "Eppstein"
1380+
sage: B.matching(use_edge_labels=False, value_only=True, algorithm='Hopcroft-Karp')
1381+
2
1382+
sage: B.matching(use_edge_labels=False, value_only=True, algorithm='Eppstein')
1383+
2
1384+
sage: B.matching(use_edge_labels=False, value_only=True, algorithm='Edmonds')
1385+
2
1386+
sage: B.matching(use_edge_labels=False, value_only=True, algorithm='LP')
1387+
2
1388+
"""
1389+
self._scream_if_not_simple()
1390+
1391+
if algorithm is None:
1392+
algorithm = "Edmonds" if use_edge_labels else "Hopcroft-Karp"
1393+
1394+
if algorithm == "Hopcroft-Karp" or algorithm == "Eppstein":
1395+
if use_edge_labels:
1396+
raise ValueError('use_edge_labels can not be used with ' +
1397+
'"Hopcroft-Karp" or "Eppstein"')
1398+
import networkx
1399+
#this is necessary to call the methods for bipartite matchings
1400+
g = self.networkx_graph()
1401+
if algorithm == "Hopcroft-Karp":
1402+
d = networkx.bipartite.hopcroft_karp_matching(g)
1403+
else:
1404+
d = networkx.bipartite.eppstein_matching(g)
1405+
if value_only:
1406+
return Integer(len(d) // 2)
1407+
else:
1408+
return [(u, v, self.edge_label(u, v))
1409+
for u, v in iteritems(d) if u < v]
1410+
elif algorithm == "Edmonds" or algorithm == "LP":
1411+
return Graph.matching(self, value_only=value_only,
1412+
algorithm=algorithm,
1413+
use_edge_labels=use_edge_labels,
1414+
solver=solver, verbose=verbose)
1415+
else:
1416+
raise ValueError('algorithm must be "Hopcroft-Karp", ' +
1417+
'"Eppstein", "Edmonds" or "LP"')

0 commit comments

Comments
 (0)