Browse Source

feat: add support of ring folding

master
Dnomd343 3 weeks ago
parent
commit
1794bc8733
  1. 273
      graph/circle.py
  2. 7
      graph/combine.py
  3. 34
      graph/cut.py

273
graph/circle.py

@ -1,175 +1,104 @@
import marimo
__generated_with = "0.9.27"
app = marimo.App(width="medium")
@app.cell
def __():
import klotski
code = klotski.CommonCode(0x1A9BF0C00)
code.to_string(shorten=True) # just demo
return code, klotski
@app.cell(hide_code=True)
def __():
import igraph as ig
g = ig.Graph.Read_Pickle('main_combined.pkl')
for i in range(g.vcount()):
g.vs[i]['codes'] = ['|'.join(g.vs[i]['codes'])]
g.summary()
return g, i, ig
@app.cell
def __():
# loop = g.girth(return_shortest_circle=True)
# [(g.vs[x].degree(), g.vs[x]) for x in loop] # 8852(78) / 8923(79) / 8848(78) / 8922(79)
return
@app.cell
def __():
# print(g.vs[8852].degree() + g.vs[8848].degree()) # endpoint pattern 1 -> 10
# print(g.vs[8923].degree() + g.vs[8922].degree()) # endpoint pattern 2 -> 8
return
@app.cell(hide_code=True)
def __():
import marimo as mo
return (mo,)
@app.cell(hide_code=True)
def __(mo):
mo.md(
r"""
`endpoints: 8852 <---> 8848`
So, we should try to combine `8923` and `8922`, the step are both `79`.
"""
)
return
@app.cell
def __():
# neigh_a = g.vs[8923].neighbors()
# neigh_b = g.vs[8922].neighbors()
# neigh = sorted(set(neigh_a) | set(neigh_b))
# print([x.index for x in neigh_a])
# print([x.index for x in neigh_b])
# print([x.index for x in neigh])
# new_point = g.add_vertex()
# assert g.vs[8923]['step'] == g.vs[8922]['step']
# new_point['step'] = g.vs[8923]['step']
# new_point['codes'] = g.vs[8923]['codes'] + g.vs[8922]['codes']
# new_point
return
@app.cell
def __(g):
# edges = [(x.index, new_point.index) for x in neigh]
# print(edges)
# g.add_edges(edges)
# g.delete_vertices([8923, 8922])
g.summary()
return
@app.cell
def __(g):
def find_combine_points(circle_points: list[int]) -> list[tuple[int, int]]:
step_list = [g.vs[x]['step'] for x in circle_points]
# step_list = [53, 52, 51, 52, 53, 54, 55, 56, 57, 56, 55, 54]
size = len(step_list)
assert size % 2 == 0
half_size = int(size / 2)
target = -1
search_list = step_list + step_list + step_list
for index in range(size, size * 2):
right = search_list[index+1:index+half_size]
left = list(reversed(search_list[index+1-half_size:index]))
# print(f'{search_list[index]}: {left} vs {right} ({left == right})')
if left == right:
target = index - size
break
# TODO: check degree() value
assert target != -1
# print(target, step_list[target])
def fix_index(val: int):
if val < 0:
return val + size
if val >= size:
return val - size
return val
combine_points = []
for index in range(target + 1, target + half_size):
mirror = target * 2 - index
combine_points.append((fix_index(index), fix_index(mirror)))
return [(circle_points[x], circle_points[y]) for x, y in combine_points]
# find_combine_points([8852, 8923, 8848, 8922])
return (find_combine_points,)
@app.cell
def __(find_combine_points, g):
# NOTE: only for mirror circle pattern
def combine_points(point_pairs: list[tuple[int, int]]):
g.vs['id'] = range(g.vcount())
for id_a, id_b in point_pairs:
point_a = [x for x in g.vs if x['id'] == id_a][0]
point_b = [x for x in g.vs if x['id'] == id_b][0]
assert point_a['step'] == point_b['step']
print('combine:', point_a, point_b)
neigh_a = point_a.neighbors()
neigh_b = point_b.neighbors()
neigh = sorted(set(neigh_a) | set(neigh_b))
new_point = g.add_vertex()
new_point['step'] = point_a['step']
new_point['codes'] = point_a['codes'] + point_b['codes']
# print(new_point)
g.add_edges([(x.index, new_point.index) for x in neigh])
g.delete_vertices([point_a.index, point_b.index])
for i_ in range(146):
#!/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())
loop = g.girth(return_shortest_circle=True)
print('circle:', loop)
combine_points(find_combine_points(loop))
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')
return combine_points, i_, loop
# g.write_graphml('main_circle.graphml')
if __name__ == "__main__":
app.run()
# g = load_graph('main_cut.pkl')
# fold_circle(g)
# g.write_pickle('main_cut_circle.pkl')
# g.write_graphml('main_cut_circle.graphml')

7
graph/combine.py

@ -159,18 +159,11 @@ def export_new_graph(g: ig.Graph, split_data: list[list[set[ig.Vertex]]]) -> ig.
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')

34
graph/cut.py

@ -3,7 +3,7 @@
import igraph as ig
def split_max_graph(g: ig.Graph, cut_point: int):
def split_max_graph(g: ig.Graph, cut_point: int) -> ig.Graph:
gg = g.copy()
edges = [(cut_point, x.index) for x in gg.vs[cut_point].neighbors()]
gg.delete_edges(edges)
@ -11,33 +11,17 @@ def split_max_graph(g: ig.Graph, cut_point: int):
return g.subgraph(components[0] + [cut_point])
def split_subgraph(g: ig.Graph):
# def demo():
# sizes = set()
# for cut_point in g.articulation_points():
# gg: ig.Graph = g.copy()
# gg.delete_vertices(cut_point)
# subs = gg.decompose()
# sizes.update([x.vcount() for x in subs])
# print(sorted(sizes))
# demo()
for _ in range(1034):
print(g.vcount(), g.ecount())
def split_subgraph(g: ig.Graph) -> ig.Graph:
while True:
points = g.articulation_points()
assert len(points) > 0
if len(points) == 0:
break
g = split_max_graph(g, points[0])
# ig.plot(g, 'demo.png', bbox=(2000, 2000))
print(g.vcount(), g.ecount())
# g.write_graphml('main_cir.graphml')
g.write_pickle('main_cir.pkl')
print(g.vcount(), g.ecount())
return g
if __name__ == '__main__':
raw = ig.Graph.Read_Pickle('main_combined.pkl')
# print(raw.vcount())
split_subgraph(raw)
split_subgraph(raw).write_pickle('main_cut.pkl')
# split_subgraph(raw).write_graphml('main_cut.graphml')

Loading…
Cancel
Save