Skip to content

Commit 8419c53

Browse files
committed
Fix the implementation to properly handle None nodes
1 parent f0a5ea2 commit 8419c53

File tree

1 file changed

+59
-29
lines changed

1 file changed

+59
-29
lines changed

pydatastructs/graphs/algorithms.py

Lines changed: 59 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1327,29 +1327,43 @@ def bfs():
13271327
u = queue.popleft()
13281328
if dist[u] < dist[None]:
13291329
for v in graph.neighbors(u):
1330-
if dist[pair_V[v.name]] == float('inf'):
1331-
dist[pair_V[v.name]] = dist[u] + 1
1332-
queue.append(pair_V[v.name])
1333-
return dist[None] != float('inf')
1330+
if v.name in pair_V:
1331+
alt = pair_V[v.name]
1332+
if alt is None:
1333+
dist[None] = dist[u] + 1
1334+
queue.append(None)
1335+
elif dist.get(alt, float('inf')) == float('inf'):
1336+
dist[alt] = dist[u] + 1
1337+
queue.append(alt)
1338+
return dist.get(None, float('inf')) != float('inf')
13341339

13351340
def dfs(u):
1336-
if u is not None:
1337-
for v in graph.neighbors(u):
1338-
if dist[pair_V[v.name]] == dist[u] + 1:
1339-
if dfs(pair_V[v.name]):
1341+
if u is None:
1342+
return True
1343+
for v in graph.neighbors(u):
1344+
if v.name in pair_V:
1345+
alt = pair_V[v.name]
1346+
if alt is None:
1347+
pair_V[v.name] = u
1348+
pair_U[u] = v.name
1349+
return True
1350+
elif dist.get(alt, float('inf')) == dist.get(u, float('inf')) + 1:
1351+
if dfs(alt):
13401352
pair_V[v.name] = u
13411353
pair_U[u] = v.name
13421354
return True
1343-
dist[u] = float('inf')
1344-
return False
1345-
return True
1355+
dist[u] = float('inf')
1356+
return False
13461357

13471358
matching = set()
13481359
while bfs():
13491360
for u in U:
13501361
if pair_U[u] is None:
1351-
if dfs(u):
1352-
matching.add((u, pair_U[u]))
1362+
dfs(u)
1363+
1364+
for u in U:
1365+
if pair_U[u] is not None:
1366+
matching.add((u, pair_U[u]))
13531367

13541368
return matching
13551369

@@ -1392,8 +1406,8 @@ def maximum_matching(graph: Graph, algorithm: str, **kwargs) -> set:
13921406
>>> graph.add_edge('v_1', 'v_2')
13931407
>>> graph.add_edge('v_2', 'v_3')
13941408
>>> graph.add_edge('v_4', 'v_1')
1395-
>>> maximum_matching(graph, 'hopcroft_karp')
1396-
>>> {('v_1', 'v_4'), ('v_3', 'v_2')}
1409+
>>> maximum_matching(graph, 'hopcroft_karp', make_undirected=True)
1410+
>>> {('v_3', 'v_2'), ('v_1', 'v_4')}
13971411
13981412
References
13991413
==========
@@ -1448,22 +1462,33 @@ def bfs():
14481462
u = queue.popleft()
14491463
if dist[u] < dist[None]:
14501464
for v in graph.neighbors(u):
1451-
if dist[pair_V[v.name]] == float('inf'):
1452-
dist[pair_V[v.name]] = dist[u] + 1
1453-
queue.append(pair_V[v.name])
1454-
return dist[None] != float('inf')
1465+
if v.name in pair_V:
1466+
alt = pair_V[v.name]
1467+
if alt is None:
1468+
dist[None] = dist[u] + 1
1469+
queue.append(None)
1470+
elif dist.get(alt, float('inf')) == float('inf'):
1471+
dist[alt] = dist[u] + 1
1472+
queue.append(alt)
1473+
return dist.get(None, float('inf')) != float('inf')
14551474

14561475
def dfs(u):
1457-
if u is not None:
1458-
for v in graph.neighbors(u):
1459-
if dist[pair_V[v.name]] == dist[u] + 1:
1460-
if dfs(pair_V[v.name]):
1476+
if u is None:
1477+
return True
1478+
for v in graph.neighbors(u):
1479+
if v.name in pair_V:
1480+
alt = pair_V[v.name]
1481+
if alt is None:
1482+
pair_V[v.name] = u
1483+
pair_U[u] = v.name
1484+
return True
1485+
elif dist.get(alt, float('inf')) == dist.get(u, float('inf')) + 1:
1486+
if dfs(alt):
14611487
pair_V[v.name] = u
14621488
pair_U[u] = v.name
14631489
return True
1464-
dist[u] = float('inf')
1465-
return False
1466-
return True
1490+
dist[u] = float('inf')
1491+
return False
14671492

14681493
matching = set()
14691494

@@ -1474,7 +1499,7 @@ def dfs(u):
14741499
results = Executor.map(dfs, unmatched_nodes)
14751500

14761501
for u, success in zip(unmatched_nodes, results):
1477-
if success:
1502+
if success and pair_U[u] is not None:
14781503
matching.add((u, pair_U[u]))
14791504

14801505
return matching
@@ -1497,6 +1522,8 @@ def maximum_matching_parallel(graph: Graph, algorithm: str, num_threads: int, **
14971522
'hopcroft_karp' -> Hopcroft-Karp algorithm for Bipartite Graphs as given in [1].
14981523
num_threads: int
14991524
The maximum number of threads to be used.
1525+
make_undirected: bool
1526+
If False, the graph should be undirected or unwanted results may be obtained. The graph can be made undirected by setting this to true.
15001527
backend: pydatastructs.Backend
15011528
The backend to be used.
15021529
Optional, by default, the best available
@@ -1520,8 +1547,8 @@ def maximum_matching_parallel(graph: Graph, algorithm: str, num_threads: int, **
15201547
>>> graph.add_bidirectional_edge('v_1', 'v_2')
15211548
>>> graph.add_bidirectional_edge('v_2', 'v_3')
15221549
>>> graph.add_bidirectional_edge('v_4', 'v_1')
1523-
>>> maximum_matching_parallel(graph, 'hopcroft_karp', 1)
1524-
>>> {('v_1', 'v_4'), ('v_3', 'v_2')}
1550+
>>> maximum_matching_parallel(graph, 'hopcroft_karp', 1, make_undirected=True)
1551+
>>> {('v_3', 'v_2'), ('v_1', 'v_4')}
15251552
15261553
References
15271554
==========
@@ -1531,6 +1558,9 @@ def maximum_matching_parallel(graph: Graph, algorithm: str, num_threads: int, **
15311558

15321559
raise_if_backend_is_not_python(
15331560
maximum_matching_parallel, kwargs.get('backend', Backend.PYTHON))
1561+
make_undirected = kwargs.get('make_undirected', False)
1562+
if make_undirected:
1563+
graph = graph.to_undirected_adjacency_list()
15341564

15351565
import pydatastructs.graphs.algorithms as algorithms
15361566
func = "_maximum_matching_" + algorithm + "_parallel"

0 commit comments

Comments
 (0)