#!/usr/bin/env python3 import igraph as ig def split_neighbor_layer(g: ig.Graph, n: int) -> tuple[list[set[ig.Vertex]], list[set[ig.Vertex]]]: layer_a: set[ig.Vertex] = set(g.vs.select(step=n)) layer_b: set[ig.Vertex] = set(g.vs.select(step=n+1)) layer_a_data: list[set[ig.Vertex]] = [] layer_b_data: list[set[ig.Vertex]] = [] select_next = lambda p: set(x for x in p.neighbors() if x['step'] == n + 1) select_last = lambda p: set(x for x in p.neighbors() if x['step'] == n) while len(layer_a) != 0: side_a: set[ig.Vertex] = set() side_b: set[ig.Vertex] = set() point = layer_a.pop() side_a.add(point) while True: nexts = set() for x in side_a: nexts.update(select_next(x)) if nexts == side_b: break side_b.update(nexts) for x in nexts: if x in layer_b: layer_b.remove(x) lasts = set() for x in side_b: lasts.update(select_last(x)) if lasts == side_a: break side_a.update(lasts) for x in lasts: if x in layer_a: layer_a.remove(x) layer_a_data.append(side_a) layer_b_data.append(side_b) assert len(layer_b) == 0 return layer_a_data, layer_b_data def combine_split_result(data_a: list[set[ig.Vertex]], data_b: list[set[ig.Vertex]]) -> list[set[ig.Vertex]]: result = [] while True: data_a = list(filter(None, data_a)) data_b = list(filter(None, data_b)) data_a = sorted(data_a, key=lambda x: len(x)) data_b = sorted(data_b, key=lambda x: len(x)) if len(data_a) == 0: assert len(data_b) == 0 break # print(len(data_a)) # for x in data_a: # print([y.attributes() for y in x]) # print(len(data_b)) # for x in data_b: # print([y.attributes() for y in x]) # print('-' * 64) if len(data_a[0]) <= len(data_b[0]): union = data_a[0] peer_unions = data_b else: union = data_b[0] peer_unions = data_a for peer_union in peer_unions: mid = union & peer_union if len(mid) == 0: continue result.append(mid) for x in mid: union.remove(x) peer_union.remove(x) return result def split_layers(g: ig.Graph) -> list[list[set[ig.Vertex]]]: assert min(g.vs['step']) == 0 layer_num = max(g.vs['step']) + 1 print(layer_num) layers = [{'up': [], 'down': []} for x in range(layer_num)] layers[0]['up'] = [set(g.vs.select(step=0))] layers[-1]['down'] = [set(g.vs.select(step=layer_num-1))] for layer_num in range(layer_num - 1): data_a, data_b = split_neighbor_layer(g, layer_num) layers[layer_num]['down'] = data_a layers[layer_num + 1]['up'] = list(filter(None, data_b)) for layer in layers: assert set() not in layer['up'] assert set() not in layer['down'] up = set() [up.update(x) for x in layer['up']] down = set() [down.update(x) for x in layer['down']] assert up == down result = [] for layer in layers: result.append(combine_split_result(layer['up'], layer['down'])) for layer_num, layer in enumerate(result): assert set() not in layer layer_nodes = set() [layer_nodes.update(x) for x in layer] assert layer_nodes == set(g.vs.select(step=layer_num)) return result def export_new_graph(g: ig.Graph, split_data: list[list[set[ig.Vertex]]]) -> ig.Graph: ng = ig.Graph(sum([len(x) for x in split_data])) g_index = 0 index_map = {} for layer_index, unions in enumerate(split_data): for union_index, nodes in enumerate(unions): index_map[(layer_index, union_index)] = g_index ng.vs[g_index]['step'] = layer_index ng.vs[g_index]['codes'] = [x['code'] for x in nodes] g_index += 1 for layer_index in range(len(split_data)-1): curr_layer = split_data[layer_index] next_layer = split_data[layer_index+1] def union_neighbors(curr_union_index: int) -> list[int]: next_union_indexes = set() for node in curr_layer[curr_union_index]: next_nodes = [x for x in node.neighbors() if x['step'] == layer_index+1] for next_node in next_nodes: for next_union_index in range(len(next_layer)): if next_node in next_layer[next_union_index]: next_union_indexes.add(next_union_index) return sorted(next_union_indexes) for union_index in range(len(curr_layer)): union_a = index_map[(layer_index, union_index)] edges = [(union_a, index_map[(layer_index+1, x)]) for x in union_neighbors(union_index)] ng.add_edges(edges) return ng if __name__ == '__main__': raw = ig.Graph.Read_Pickle('data/DAA7F30.pkl') # raw = ig.Graph.Read_Pickle('data/DBAB4CC.pkl') # raw = ig.Graph.Read_Pickle('main_combined.pkl') print(raw.summary()) gg = export_new_graph(raw, split_layers(raw)) print(gg.summary()) # print(gg.isomorphic(raw)) # for x in gg.vs: # x['color'] = 'yellow' # gg.vs[0]['color'] = 'red' # print(gg) # ig.plot(gg, 'demo.png', vertex_size=10) gg.write_pickle('main_combined.pkl') # gg.write_graphml('main_combined.graphml')