-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathPartialOrder.py
116 lines (99 loc) · 3.23 KB
/
PartialOrder.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
"""PartialOrder.py
Various operations on partial orders and directed acyclic graphs.
D. Eppstein, July 2006.
"""
import unittest
from DFS import preorder,postorder
import BipartiteMatching
def isTopologicalOrder(G,L):
"""Check that L is a topological ordering of directed graph G."""
vnum = {}
for i in range(len(L)):
if L[i] not in G:
return False
vnum[L[i]] = i
for v in G:
if v not in vnum:
return False
for w in G[v]:
if w not in vnum or vnum[w] <= vnum[v]:
return False
return True
def TopologicalOrder(G):
"""Find a topological ordering of directed graph G."""
L = list(postorder(G))
L.reverse()
if not isTopologicalOrder(G,L):
raise ValueError("TopologicalOrder: graph is not acyclic.")
return L
def isAcyclic(G):
"""Return True if G is a directed acyclic graph, False otherwise."""
L = list(postorder(G))
L.reverse()
return isTopologicalOrder(G,L)
def TransitiveClosure(G):
"""
The transitive closure of graph G.
This is a graph on the same vertex set containing an edge (v,w)
whenever v != w and there is a directed path from v to w in G.
"""
TC = {v:set(preorder(G,v)) for v in G}
for v in G:
TC[v].remove(v)
return TC
def TracePaths(G):
"""
Turn a DAG with indegree and outdegree <= 1 into a sequence of lists.
"""
L = []
for v in TopologicalOrder(G):
if L and v not in G[L[-1]]:
yield L
L = []
L.append(v)
if L:
yield L
def MinimumPathDecomposition(G):
"""
Cover a directed acyclic graph with a minimum number of paths.
"""
M,A,B = BipartiteMatching.matching(G)
H = {v:[] for v in G}
for v in G:
if v in M:
H[M[v]] = (v,)
return TracePaths(H)
def MinimumChainDecomposition(G):
"""
Cover a partial order with a minimum number of chains.
By Dilworth's theorem the number of chains equals the size
of the largest antichain of the order. The input should be
a directed acyclic graph, not necessarily transitively closed.
"""
return MinimumPathDecomposition(TransitiveClosure(G))
def MaximumAntichain(G):
"""
Find a maximum antichain in the given directed acyclic graph.
"""
if not isAcyclic(G):
raise ValueError("MaximumAntichain: input is not acyclic.")
TC = TransitiveClosure(G)
M,A,B = BipartiteMatching.matching(TransitiveClosure(G))
return set(A).intersection(B)
class PartialOrderTest(unittest.TestCase):
cube = {i:[i^b for b in (1,2,4,8) if i^b > i] for i in range(16)}
def testHypercubeAcyclic(self):
self.assert_(isAcyclic(self.cube))
def testHypercubeClosure(self):
TC = TransitiveClosure(self.cube)
for i in range(16):
self.assertEqual(TC[i],
{j for j in range(16) if i & j == i and i != j})
def testHypercubeAntichain(self):
A = MaximumAntichain(self.cube)
self.assertEqual(A,{3,5,6,9,10,12})
def testHypercubeDilworth(self):
CD = list(MinimumChainDecomposition(self.cube))
self.assertEqual(len(CD),6)
if __name__ == "__main__":
unittest.main()