华容道高性能计算引擎
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

105 lines
3.8 KiB

#!/usr/bin/env python3
import igraph as ig
from itertools import takewhile
def load_graph(pkl_file: str) -> ig.Graph:
graph = ig.Graph.Read_Pickle(pkl_file)
for i in range(graph.vcount()):
graph.vs[i]['codes'] = ['|'.join(graph.vs[i]['codes'])]
print(graph.summary())
return graph
def find_fold_points(step_list: list[int], degree_list: list[int]) -> list[tuple[int, int]]:
size = len(step_list)
assert size == len(degree_list) and size > 0 and size % 2 == 0
half_size = size // 2
def fix_index(raw_id: int) -> int:
if raw_id < 0:
return raw_id + size
elif raw_id >= size:
return raw_id - size
return raw_id
fold_info = []
for index in range(size):
right_ids = [fix_index(x) for x in range(index + 1, index + half_size)]
left_ids = [fix_index(x) for x in range(index - 1, index - half_size, -1)]
assert len(left_ids) == len(right_ids)
steps = ((step_list[a], step_list[b]) for a, b in zip(left_ids, right_ids))
fold_len = len(list(takewhile(lambda x: x[0] == x[1], steps)))
fold_ids = list(zip(left_ids[:fold_len], right_ids[:fold_len]))
fold_degree = sum(degree_list[a] + degree_list[b] for a, b in fold_ids)
assert fold_len < half_size
assert fold_degree >= 4 * fold_len
assert len(fold_ids) == fold_len
fold_info.append({
'index': index,
'fold_len': fold_len,
'fold_degree': fold_degree,
'fold_ids': fold_ids,
})
# print(f'{index}: {fold_degree} {fold_ids} ({fold_len})')
# need larger `fold_len` and smaller `fold_degree`
fold_info.sort(key=lambda x: (-x['fold_len'], x['fold_degree'])) # maybe we need min_common_code
return fold_info[0]['fold_ids']
def combine_points(graph: ig.Graph, pairs: list[tuple[int, int]]) -> None:
graph.vs['id'] = range(graph.vcount())
for id_a, id_b in pairs:
points_a = [x for x in graph.vs if x['id'] == id_a]
points_b = [x for x in graph.vs if x['id'] == id_b]
assert len(points_a) == 1 and len(points_b) == 1
point_a, point_b = points_a[0], points_b[0]
assert point_a['step'] == point_b['step']
neighs_a = point_a.neighbors()
neighs_b = point_b.neighbors()
neighs = sorted(set(neighs_a) | set(neighs_b))
assert len(neighs_a) >= 2 and len(neighs_b) >= 2
point_new = graph.add_vertex()
point_new['step'] = point_a['step']
point_new['codes'] = point_a['codes'] + point_b['codes']
# print(point_new)
graph.add_edges([(neigh.index, point_new.index) for neigh in neighs])
graph.delete_vertices([point_a.index, point_b.index])
def fold_circle(graph: ig.Graph) -> None:
while True:
print(g.summary())
if g.is_tree():
break
circle = g.girth(return_shortest_circle=True)
circle = [graph.vs[x] for x in circle]
fold_pairs = find_fold_points([x['step'] for x in circle], [x.degree() for x in circle])
fold_pairs = [(circle[a].index, circle[b].index) for a, b in fold_pairs]
print('fold:', fold_pairs)
combine_points(graph, fold_pairs)
if __name__ == '__main__':
# val = find_fold_point([53, 52, 51, 52, 53, 54, 55, 56, 57, 56, 55, 54], [2, 2, 3, 2, 2, 2, 2, 2, 3, 2, 2, 2])
# val = find_fold_point([65, 66, 67, 66, 65, 64, 65, 66, 67, 66, 65, 64, 63, 64], [2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 3, 2])
# val = find_fold_point([53, 54, 55, 54, 53, 54, 53, 52, 51, 52], [2, 2, 2, 2, 3, 2, 2, 2, 3, 2])
# print(val)
g = load_graph('main_combined.pkl')
fold_circle(g)
g.write_pickle('main_circle.pkl')
# g.write_graphml('main_circle.graphml')
# g = load_graph('main_cut.pkl')
# fold_circle(g)
# g.write_pickle('main_cut_circle.pkl')
# g.write_graphml('main_cut_circle.graphml')