在graph-tool的GTK循环中同步更新图属性与拓扑结构的正确方法
问题描述
我想实现一个动态布局的图动画,属于生命游戏的图类比实现,需要在动画运行过程中同步更新图的拓扑结构和顶点属性。
此前我采用的实现方法是:在第i-1次迭代时复制当前图,基于原图计算所有变更后返回更新后的副本,通过这种方式构建完整的图序列。
但这个方法存在问题:使用GraphWindow实现动画时要求原地修改图,不能生成新的副本。所以我目前的方案是每次调用update_state函数时遍历两次图:第一次遍历计算所有顶点的新状态,第二次遍历统一赋值更新。现在批量赋值对标量属性可以正常生效,但我没有实现字符串类型属性的批量赋值。
我现在的核心需求是:每次调用update_state时,同步更新两个顶点属性:
- 对应顶点状态的标量属性
vp.state - 对应顶点颜色的字符串类型属性
vp.color
以下是我写的run_simulation函数(对应更新状态的核心函数)的代码片段:
def run_simulation(): global count, g to_remove = [] alterations = [] r = 0 for v in g.get_vertices(): if len(g.get_all_edges(v)) == 0: print(r) r+=1 to_remove.append(v) else: Nv = g.get_all_neighbors(v) sum_nbstates = sum([g.vp.state[u] for u in Nv]) nb_size = len(Nv) acoef = sum_nbstates / nb_size if g.vp.state[v] == 1: if 2 / nb_size <= acoef <= 3 / nb_size: state = 1 color = "white" else: state = 0 color = "black" elif g.vp.state[v] == 0: # if (acoef == 3/nb_size): if 2 / nb_size <= acoef <= 4 / nb_size: state = 1 color = "white" else: state = 0 color = "black" alterations.append([v, state, color]) states.append(state) colors.append(color) ###some checks #print(vertices) diff = len(to_remove) if diff >= 1: print(f"diff {diff}, count {count}") #this eventually results in something ≠ 0, I still haven't find out why ### for (v, new_state, new_color) in alterations: g.vp.state[v] = new_state g.vp.color[v] = new_color gt.sfdp_layout( g, pos=g.vp.pos, eweight=g.ep.weight, max_iter=1, init_step=0.01, K=0.5 ) g.remove_vertex(to_remove) count += 1 win.graph.regenerate_surface() win.graph.queue_draw() if OFFSCREEN: pixbuf = win.get_pixbuf() pixbuf.savev(r'./frames/graphol%06d.png' % count, 'png', [], []) if count >= max_count: sys.exit(0) return True
解决方案
针对字符串属性无法批量赋值的问题,可采用以下优化方案实现高效原地更新:
- 字符串属性映射优化
不直接用字符串类型的顶点属性存储颜色,新增一个整数类型的颜色映射属性vp.color_code,0对应黑色、1对应白色,批量更新vp.color_code后,仅需在绘制时做一次值到颜色的映射即可。如果必须直接更新字符串属性,可提前构建颜色列表,用g.vp.color.a = np.array(color_list, dtype=object)的方式实现批量赋值,注意numpy数组要指定dtype为object才能存储字符串。 - 遍历逻辑确认
当前的两次遍历逻辑是正确的,避免了迭代过程中修改属性影响当前轮次的状态计算,不需要调整。你当前的代码已经排除了待删除顶点的赋值操作,不会产生无效赋值。 - 孤立顶点差值问题排查
代码中打印的diff不为0属于正常现象,每一轮迭代都会产生度数为0的孤立顶点,这些顶点会被加入to_remove列表删除,如果不需要删除孤立顶点,注释掉g.remove_vertex(to_remove)行即可。
内容的提问来源于stack exchange,提问作者izzorts
相关产品推荐
相关产品推荐

