-
Notifications
You must be signed in to change notification settings - Fork 1
/
Cograph_generator.py
233 lines (208 loc) · 5.98 KB
/
Cograph_generator.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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
from Trees import Tree
from sage.all import Graph
" Cograph Generation with linear delay (A. Jones, F. Protti, R. Vecchio)"
def next_partition(a):
"""
given a partition a (of number n) find the partition immediately next to a
all partitions of 4 are: [1,1,1,1], [1,1,2],[1,3],[2,2] (in increasing order)
next_partition([1,1,2]) is [1,3]
"""
k=len(a)
n=sum(a)
if a[0] != (n//2):
if a[k-1]-a[k-2]<=1:
return a[:k-2]+[a[k-2]+a[k-1]]
else:
a[k-2]+=1
a[k-1]-=1
q=a[k-1]//a[k-2]
r=a[k-1]%a[k-2]
if q>1:
return a[:k-2]+[a[k-2]]*q+[a[k-2]+r]
else:
return a
else:
if n!=3:
return None
else:
if a[1]==n//2+n%2:
return None
else:
return [1,2]
def rebuild_node(u,a):
"""
rebuild_node(u,a): given a node u of the ordered tree T and a partition a,
the subtree T(u) is replaced by the partition a, that is induced by u.
"""
k=len(a)
# replace the children of u with the partition a
for i in range(k):
# add the i-th child of u
if i<len(u.children):
u.children[i]=Tree(a[i])
else:
u.add_child(Tree(a[i]))
u.children[i].parent=u
if a[i]>1: # if i is not a leaf, then its children should be leaves
this_child=u.children[i]
for j in range(a[i]):
this_child.add_child(Tree(1))
for i in range(k, len(u.children)):
# If there are more "existing" children than we need to delete them
u.children[k]=None
u.children.remove(None)
def find_pivot(T,pivot):
"""
find_pivot(T,pivot): given a tree T and a list pivot it finds the pivot i.e. the first node in reverse postorder traversal
that does not induce a maximum partition.
"""
for child in reversed(T.children):
if pivot==[]:
find_pivot(child,pivot)
""" (Definition 9: pivot node)
if a node T is not a leaf, it does not induce a maximum partition
and it is the first such node in the inverse postorder traversal,
then it is the PIVOT.
the maximum partition of u is [floor(u/2),ceil(u/2)]
"""
i=T.name
if pivot==[] and T.name!=1 and ((i//2!=T.children[0].name) or ((i//2+i%2)!=T.children[1].name)):
pivot.append(T)
T.info='p'
def next_tree(T):
"""
Given the co-tree T, find the next cotree
"""
pivot=[]
find_pivot(T,pivot)
if pivot!=[]: # If there is a pivot, then we can find the next tree
"find the existing partition that is induced by pivot"
partition=[]
for i in pivot[0].children:
partition.append(i.name)
b=next_partition(partition) # finds the next partition induced by the subtree pivot[0]
rebuild_node(pivot[0],b) # changes the subtree "pivot[0]"
x=pivot[0]
while True:
if x.parent!=None:
ancestor=x.parent
# modify the bigger siblings of node x
is_bigger_sibling=False
for y in range(len(ancestor.children)) : # y = all siblings of x
this_child=ancestor.children[y]
if this_child.info=='p':
is_bigger_sibling=True
if is_bigger_sibling==True and this_child.info!='p': # true only for bigger siblings of x
if x.name==this_child.name:
temp=Tree(x.name)
x.copy_tree(temp)
ancestor.children[y]=temp #copy subtree T(x) in T(y)
temp.parent=ancestor
else:
c=[]
for i in range(ancestor.children[y].name):
c.append(1)
rebuild_node(ancestor.children[y],c)
# set x= parent of x
x.info=None # reset the pivot mark
x=ancestor
if x==None:
break
x.info='p' # parent gets the pivot mark
else:
break
return True
else: # If there is no pivot, then there is no other tree
return False
def cograph_generator(n):
"""
Input integer n
Output, all cographs with n nodes
"""
if n>=2:
# Construct the minimum tree
T=Tree(n)
for j in range(n):
T.add_child(Tree(1))
i=1
flag=True
while flag:
## T corresponds to 2 cotrees: one with '0' root and one with '1' root
tree1=Tree(T.name)
T.copy_tree(tree1)
tree0=Tree(T.name)
T.copy_tree(tree0)
# tree 1 has root '1'
counter=[0]
change_label(tree1,1,counter)
# tree 0 has root '0'
counter=[0]
change_label(tree0,0,counter)
tree00=Tree('1')
tree00.add_child(tree0)
yield tree1
yield tree00
flag=next_tree(T) # Find the next tree. return False if there is no other Tree
if not flag:
break
#print(" ")
i+=1
print ("The number of cotrees is: ",2*i)
else:
print ("Number of vertices must be >=2")
def change_label(tree,status,counter):
"""
Given the the co-tree tree,that each node has aslabel the number of its children,
change the label into "0" for parallel nodes, "1" for series nodes and a "node-number" for the leaves/nodes
counter: counts the total nodes of the tree, so that each one gets a unique label/name.
status: counts the "level" of each node, so that the internal nodes get the label series/parallel.
"""
if tree.name!=1:
tree.name=str(status%2)
else:
tree.name=counter[0]
counter[0]+=1
for child in tree.children:
if child!=None:
change_label(child,status+1,counter)
def tree_to_graph(tree):
"""
returns the corresponding cograph of the tree
"""
g=Graph()
tree_to_graph_rec(tree,g)
return g
def tree_to_graph_rec(tree,g):
"""
Create the co_graph by adding recursively one by one its vertices and edges
"""
for child in tree.children:
tree_to_graph_rec(child,g)
if tree.name!='1' and tree.name!='0':
tree.info='v'
g.add_vertex(tree.name)
find_neighbors(tree,g)
tree.reset_info()
def find_neighbors(tree,g):
"""
Given the node "tree", find its adjacent nodes
"""
ancestor=tree.parent
ancestor.info='v'
while ancestor!=None:
if ancestor.name=='1':
for sibling in ancestor.children:
if sibling!=tree and sibling.info!='v':
add_neighbor(tree,sibling,g)
elif ancestor.name=='0':
ancestor.info='v'
ancestor=ancestor.parent
def add_neighbor(tree,neighbor,g):
"""
Given two adjacent nodes "tree" and "neighbor", create their connecting edge in the graph
"""
if neighbor.name!='0' and neighbor.name!='1':
g.add_edge(tree.name,neighbor.name)
else:
for child in neighbor.children:
add_neighbor(tree,child,g)