Skip to content

Commit

Permalink
Merge pull request #1735 from aureooms/weight-parameter-for-max-weigh…
Browse files Browse the repository at this point in the history
…t-matching

Add weight parameter for max_weight_matching
  • Loading branch information
dschult committed Sep 1, 2015
2 parents 8f1cbb8 + 04d01c8 commit 94e4b23
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 42 deletions.
17 changes: 11 additions & 6 deletions networkx/algorithms/matching.py
Expand Up @@ -53,7 +53,7 @@ def maximal_matching(G):
return matching


def max_weight_matching(G, maxcardinality=False):
def max_weight_matching(G, maxcardinality=False, weight='weight'):
"""Compute a maximum-weighted matching of G.
A matching is a subset of edges in which no node occurs more than once.
Expand All @@ -65,10 +65,15 @@ def max_weight_matching(G, maxcardinality=False):
G : NetworkX graph
Undirected graph
maxcardinality: bool, optional
maxcardinality: bool, optional (default=False)
If maxcardinality is True, compute the maximum-cardinality matching
with maximum weight among all maximum-cardinality matchings.
weight: string, optional (default='weight')
Edge data key corresponding to the edge weight.
If key not found, uses 1 as weight.
Returns
-------
mate : dictionary
Expand All @@ -79,7 +84,7 @@ def max_weight_matching(G, maxcardinality=False):
Notes
------
If G has edges with 'weight' attribute the edge data are used as
If G has edges with weight attributes the edge data are used as
weight values else the weights are assumed to be 1.
This function takes time O(number_of_nodes ** 3).
Expand Down Expand Up @@ -154,7 +159,7 @@ def leaves(self):
maxweight = 0
allinteger = True
for i,j,d in G.edges(data=True):
wt=d.get('weight',1)
wt=d.get(weight,1)
if i != j and wt > maxweight:
maxweight = wt
allinteger = allinteger and (str(type(wt)).split("'")[1]
Expand Down Expand Up @@ -231,7 +236,7 @@ def leaves(self):

# Return 2 * slack of edge (v, w) (does not work inside blossoms).
def slack(v, w):
return dualvar[v] + dualvar[w] - 2 * G[v][w].get('weight',1)
return dualvar[v] + dualvar[w] - 2 * G[v][w].get(weight,1)

# Assign label t to the top-level blossom containing vertex w,
# coming through an edge from vertex v.
Expand Down Expand Up @@ -573,7 +578,7 @@ def verifyOptimum():
# 0. all edges have non-negative slack and
# 1. all matched edges have zero slack;
for i,j,d in G.edges(data=True):
wt=d.get('weight',1)
wt=d.get(weight,1)
if i == j:
continue # ignore self-loops
s = dualvar[i] + dualvar[j] - 2 * wt
Expand Down
79 changes: 43 additions & 36 deletions networkx/algorithms/tests/test_matching.py
Expand Up @@ -45,6 +45,13 @@ def test_trivial5(self):
assert_equal(nx.max_weight_matching(G, 1),
{1: 2, 2: 1, 3: 4, 4: 3})

def test_trivial6(self):
"""Small graph with arbitrary weight attribute"""
G = nx.Graph()
G.add_edge('one', 'two', weight=10, abcd=11)
G.add_edge('two', 'three', weight=11, abcd=10)
assert_equal(nx.max_weight_matching(G, weight='abcd'),
{'one': 'two', 'two': 'one'})

def test_floating_point_weights(self):
"""Floating point weights"""
Expand Down Expand Up @@ -72,7 +79,7 @@ def test_negative_weights(self):
def test_s_blossom(self):
"""Create S-blossom and use it for augmentation:"""
G = nx.Graph()
G.add_weighted_edges_from([ (1, 2, 8), (1, 3, 9),
G.add_weighted_edges_from([ (1, 2, 8), (1, 3, 9),
(2, 3, 10), (3, 4, 7) ])
assert_equal(nx.max_weight_matching(G),
{1: 2, 2: 1, 3: 4, 4: 3})
Expand All @@ -84,7 +91,7 @@ def test_s_blossom(self):
def test_s_t_blossom(self):
"""Create S-blossom, relabel as T-blossom, use for augmentation:"""
G = nx.Graph()
G.add_weighted_edges_from([ (1, 2, 9), (1, 3, 8), (2, 3, 10),
G.add_weighted_edges_from([ (1, 2, 9), (1, 3, 8), (2, 3, 10),
(1, 4, 5), (4, 5, 4), (1, 6, 3) ])
assert_equal(nx.max_weight_matching(G),
{1: 6, 2: 3, 3: 2, 4: 5, 5: 4, 6: 1})
Expand All @@ -101,27 +108,27 @@ def test_nested_s_blossom(self):
"""Create nested S-blossom, use for augmentation:"""

G = nx.Graph()
G.add_weighted_edges_from([ (1, 2, 9), (1, 3, 9), (2, 3, 10),
(2, 4, 8), (3, 5, 8), (4, 5, 10),
G.add_weighted_edges_from([ (1, 2, 9), (1, 3, 9), (2, 3, 10),
(2, 4, 8), (3, 5, 8), (4, 5, 10),
(5, 6, 6) ])
assert_equal(nx.max_weight_matching(G),
{1: 3, 2: 4, 3: 1, 4: 2, 5: 6, 6: 5})

def test_nested_s_blossom_relabel(self):
"""Create S-blossom, relabel as S, include in nested S-blossom:"""
G = nx.Graph()
G.add_weighted_edges_from([ (1, 2, 10), (1, 7, 10), (2, 3, 12),
(3, 4, 20), (3, 5, 20), (4, 5, 25),
G.add_weighted_edges_from([ (1, 2, 10), (1, 7, 10), (2, 3, 12),
(3, 4, 20), (3, 5, 20), (4, 5, 25),
(5, 6, 10), (6, 7, 10), (7, 8, 8) ])
assert_equal(nx.max_weight_matching(G),
{1: 2, 2: 1, 3: 4, 4: 3, 5: 6, 6: 5, 7: 8, 8: 7})

def test_nested_s_blossom_expand(self):
"""Create nested S-blossom, augment, expand recursively:"""
G = nx.Graph()
G.add_weighted_edges_from([ (1, 2, 8), (1, 3, 8), (2, 3, 10),
(2, 4, 12),(3, 5, 12), (4, 5, 14),
(4, 6, 12), (5, 7, 12), (6, 7, 14),
G.add_weighted_edges_from([ (1, 2, 8), (1, 3, 8), (2, 3, 10),
(2, 4, 12),(3, 5, 12), (4, 5, 14),
(4, 6, 12), (5, 7, 12), (6, 7, 14),
(7, 8, 12) ])
assert_equal(nx.max_weight_matching(G),
{1: 2, 2: 1, 3: 5, 4: 6, 5: 3, 6: 4, 7: 8, 8: 7})
Expand All @@ -130,30 +137,30 @@ def test_nested_s_blossom_expand(self):
def test_s_blossom_relabel_expand(self):
"""Create S-blossom, relabel as T, expand:"""
G = nx.Graph()
G.add_weighted_edges_from([ (1, 2, 23), (1, 5, 22), (1, 6, 15),
(2, 3, 25), (3, 4, 22), (4, 5, 25),
G.add_weighted_edges_from([ (1, 2, 23), (1, 5, 22), (1, 6, 15),
(2, 3, 25), (3, 4, 22), (4, 5, 25),
(4, 8, 14), (5, 7, 13) ])
assert_equal(nx.max_weight_matching(G),
{1: 6, 2: 3, 3: 2, 4: 8, 5: 7, 6: 1, 7: 5, 8: 4})

def test_nested_s_blossom_relabel_expand(self):
"""Create nested S-blossom, relabel as T, expand:"""
G = nx.Graph()
G.add_weighted_edges_from([ (1, 2, 19), (1, 3, 20), (1, 8, 8),
(2, 3, 25), (2, 4, 18), (3, 5, 18),
G.add_weighted_edges_from([ (1, 2, 19), (1, 3, 20), (1, 8, 8),
(2, 3, 25), (2, 4, 18), (3, 5, 18),
(4, 5, 13), (4, 7, 7), (5, 6, 7) ])
assert_equal(nx.max_weight_matching(G),
{1: 8, 2: 3, 3: 2, 4: 7, 5: 6, 6: 5, 7: 4, 8: 1})


def test_nasty_blossom1(self):
"""Create blossom, relabel as T in more than one way, expand,
"""Create blossom, relabel as T in more than one way, expand,
augment:
"""
G = nx.Graph()
G.add_weighted_edges_from([ (1, 2, 45), (1, 5, 45), (2, 3, 50),
(3, 4, 45), (4, 5, 50), (1, 6, 30),
(3, 9, 35), (4, 8, 35), (5, 7, 26),
G.add_weighted_edges_from([ (1, 2, 45), (1, 5, 45), (2, 3, 50),
(3, 4, 45), (4, 5, 50), (1, 6, 30),
(3, 9, 35), (4, 8, 35), (5, 7, 26),
(9, 10, 5) ])
assert_equal(nx.max_weight_matching(G),
{1: 6, 2: 3, 3: 2, 4: 8, 5: 7,
Expand All @@ -162,50 +169,50 @@ def test_nasty_blossom1(self):
def test_nasty_blossom2(self):
"""Again but slightly different:"""
G = nx.Graph()
G.add_weighted_edges_from([ (1, 2, 45), (1, 5, 45), (2, 3, 50),
(3, 4, 45), (4, 5, 50), (1, 6, 30),
(3, 9, 35), (4, 8, 26), (5, 7, 40),
G.add_weighted_edges_from([ (1, 2, 45), (1, 5, 45), (2, 3, 50),
(3, 4, 45), (4, 5, 50), (1, 6, 30),
(3, 9, 35), (4, 8, 26), (5, 7, 40),
(9, 10, 5) ])
assert_equal(nx.max_weight_matching(G),
{1: 6, 2: 3, 3: 2, 4: 8, 5: 7,
{1: 6, 2: 3, 3: 2, 4: 8, 5: 7,
6: 1, 7: 5, 8: 4, 9: 10, 10: 9})

def test_nasty_blossom_least_slack(self):
"""Create blossom, relabel as T, expand such that a new
"""Create blossom, relabel as T, expand such that a new
least-slack S-to-free dge is produced, augment:
"""
G = nx.Graph()
G.add_weighted_edges_from([ (1, 2, 45), (1, 5, 45), (2, 3, 50),
(3, 4, 45), (4, 5, 50), (1, 6, 30),
(3, 9, 35), (4, 8, 28), (5, 7, 26),
G.add_weighted_edges_from([ (1, 2, 45), (1, 5, 45), (2, 3, 50),
(3, 4, 45), (4, 5, 50), (1, 6, 30),
(3, 9, 35), (4, 8, 28), (5, 7, 26),
(9, 10, 5) ])
assert_equal(nx.max_weight_matching(G),
{1: 6, 2: 3, 3: 2, 4: 8, 5: 7,
{1: 6, 2: 3, 3: 2, 4: 8, 5: 7,
6: 1, 7: 5, 8: 4, 9: 10, 10: 9})

def test_nasty_blossom_augmenting(self):
"""Create nested blossom, relabel as T in more than one way"""
# expand outer blossom such that inner blossom ends up on an
"""Create nested blossom, relabel as T in more than one way"""
# expand outer blossom such that inner blossom ends up on an
# augmenting path:
G = nx.Graph()
G.add_weighted_edges_from([ (1, 2, 45), (1, 7, 45), (2, 3, 50),
(3, 4, 45), (4, 5, 95), (4, 6, 94),
(5, 6, 94), (6, 7, 50), (1, 8, 30),
G.add_weighted_edges_from([ (1, 2, 45), (1, 7, 45), (2, 3, 50),
(3, 4, 45), (4, 5, 95), (4, 6, 94),
(5, 6, 94), (6, 7, 50), (1, 8, 30),
(3, 11, 35), (5, 9, 36), (7, 10, 26),
(11, 12, 5) ])
assert_equal(nx.max_weight_matching(G),
{1: 8, 2: 3, 3: 2, 4: 6, 5: 9, 6: 4,
{1: 8, 2: 3, 3: 2, 4: 6, 5: 9, 6: 4,
7: 10, 8: 1, 9: 5, 10: 7, 11: 12, 12: 11})

def test_nasty_blossom_expand_recursively(self):
"""Create nested S-blossom, relabel as S, expand recursively:"""
G = nx.Graph()
G.add_weighted_edges_from([ (1, 2, 40), (1, 3, 40), (2, 3, 60),
(2, 4, 55), (3, 5, 55), (4, 5, 50),
(1, 8, 15), (5, 7, 30), (7, 6, 10),
G.add_weighted_edges_from([ (1, 2, 40), (1, 3, 40), (2, 3, 60),
(2, 4, 55), (3, 5, 55), (4, 5, 50),
(1, 8, 15), (5, 7, 30), (7, 6, 10),
(8, 10, 10), (4, 9, 30) ])
assert_equal(nx.max_weight_matching(G),
{1: 2, 2: 1, 3: 5, 4: 9, 5: 3,
{1: 2, 2: 1, 3: 5, 4: 9, 5: 3,
6: 7, 7: 6, 8: 10, 9: 4, 10: 8})

def test_maximal_matching():
Expand Down

0 comments on commit 94e4b23

Please sign in to comment.