从 Nauty 数据结构出发认识群论

在阅读本文前,强烈建议有志入门群论的同学观看 3blue1brown 魔群 视频。

对于计算机方向同学,可以尝试从数据结构的角度理解。本文主要基于文档、网站 Nauty 和 Nauty 的 python binding, pynauty(github.com) 展开。

Nauty 数据结构

本小节截选自 Nauty 文档。上文提到,我们通过将三维的分子结构转换为一维数组来进行群的作用。如下图所示:

在这里插入图片描述

之所以从数据结构角度理解,是因为 Nauty 的数据结构是一维的,更符合程序员思维,涉及抽象符号较少。下面我们关注其中涉及到的群操作。

交换

交换是一种群的作用(Action),一串数组中,通过更换几个数字的位置,可以得到新的数组。如下图所示:

在这里插入图片描述

交换前后,新标签和旧标签对比,可以得到交换的位点。本例中,如果使用抽象语言,这种群作用通过 (0,1) 表示。Nauty 通过将这种群作用施加在标准数组上,得到能够代表这种群作用的数组。

上例混淆了起始数组和标准数组,可看下例:

在这里插入图片描述

这种形式能够将群作用结构化,他们都有着特定的数组长度。Nauty 文档里举了一个更加复杂的例子:

在这里插入图片描述

是从 Nauty description 倒推 Abstracted description.

我们可以看到,和标准数组相比 0, 2, 51, 3, 6, 4 的位置是混乱的两组,但是 7,8 没有变,所以 7, 8 没有在抽象描述中显现。

自同构群

上小节介绍了交换作用的数据结构,但脱离了图的语境。

在这里插入图片描述

我们再回看该分子图。一种交换,比如说 1, 0, 2, 3, 4, 5 ,代表着交换 0, 1 两个碳原子交换。从化学角度看,我们认为这种交换并不改变分子结构。如果使用群论的术语,我们说 1, 0, 2, 3, 4, 5 是一种自同构群。(严谨的说,是自同构群作用,一个图的自同构群包含了所有类似的群作用)注意,并不是所有的群作用都是自同构群,只有交换前后结构不变的群作用可以叫做自同构群。比如, 2, 1, 0, 3, 4, 5 表示 0, 2 交换,显然会改变分子结构,因此, 2, 1, 0, 3, 4, 5 只是一种群作用而不是自同构群。(其实这里表述仍然不严谨,但可以这样理解)

轨道

轨道二字会让人联想到 “行星轨道” ,“原子轨道” 等。在 “原子轨道” 里,电子和原子核间的相互作用简化为了电子围绕原子核转动,同一轨道上的电子能量相同,后来这种思想延续到了分子轨道中 “简并轨道” 概念里。

群论语境下,轨道也有类似的含义。同一轨道的元素 (element) 互相交换,结构不变。以下面分子图为例:

在这里插入图片描述

我们可以说 0, 1 处于同一轨道,因为交换二者不会带来结构的变化。群论里,我们用 {0, 1}, {3, 5}, {2, 4} 来表述轨道。

Nauty 使用一个数组来存储轨道信息:

在这里插入图片描述

同一轨道中,数值最小的元素代表整个轨道。比如:{2, 4} 对应位置由 2 来代替。

下面是 Nauty 手册给的一个例子,可做练习:

在这里插入图片描述

染色

分子图的染色很好理解,不同的点代表不同的原子。我们这里仅讨论点的染色。下图是 ASE 渲染的分子图:

在这里插入图片描述

Nauty 使用两个数组来表示一种染色方案,分别是 labptn.

lab 是变动较大的,没有太多含义。我们这里按照 CHO 的顺序排列。

在这里插入图片描述

ptn 中 1 表示对应位点和后面一位位点的颜色相同,0 表示对应位点和后面一位位点颜色不同。所以 101010 表示 {0, 1}, {3, 5}, {2, 4} 三种颜色。注意,染色和轨道是不同的概念。染色通常作为试错的手段,探索自同构群,比如,目前最快的图同构算法就是通过染色实现的。而轨道是图的固有性质,是固定不变的,需要被求解的。

Nauty 手册给了一个染色的例子,可做练习:

在这里插入图片描述

值得注意的是,此处的 1 可以替换为其他数值,比如下面的染色和上面一致:

在这里插入图片描述

Nauty 的搜索树

上一章节介绍了群论相关的专业术语。下面我们以 Nauty 网站上介绍的 搜索树 为例,综合运用这些术语。开始之前,我们首先介绍一下图的自同构问题 (graph isomorphism problem

图的自同构问题

图的自同构问题,旨在判断两个图是否一致。如下图所示:

在这里插入图片描述

三种图看起来是不一样的,但如果仅考虑拓扑关系,三者每条边、每个顶点都能找到一致的。比如,上图用黑色圈圈起了一个顶点,该顶点连接的三条边的颜色是一致的。(图自目前最快的图同构算法

图的自同构可以定义为:一张图中的每个点都能在另一张图中找到映射,且点的邻居的颜色一致,和点相连的边的颜色一致。

Nauty 算法起初就是为了解决这一问题而产生的,一个简化版的介绍可以看我之前的博客

搜索树中的专业术语

在第 7 页 PPT 之前,我们已经通过特定的染色程序得到了两种染色方案。

在这里插入图片描述

通过对比二者,比如说 3, 7 ,颜色是发生变化的。但变化前后,红色三个邻居的颜色是一致的。所以,我们认为群作用 (3, 7) 是一种自同构的群作用。然而,这种说法并不严谨,因为上图中一共出现了三对类似的群作用。这三对是同时发生的。因此,准确的说法是,(3, 7)(4, 5)(8, 9) 是一种自同构的群作用。

我们用类似的方法得到第二种群作用,与上面得到的一起,产生了一个轨道。

在这里插入图片描述

观察轨道和两个群可以发现,轨道的运算可以简单理解为群的交并。

在这里插入图片描述

值得注意的是,g1g2 虽然有相同的成分,但由于存在不同的成分,二者并不能相互转化。后续的遍历过程又生成了其他的群(自同构群作用),但颗粒度都较大。群论里,我们称这种最小颗粒度的群为 generator (因为没有其他更小的)。

此外,还有一个常被提到的概念是群的秩。(这里的群指囊括了所有群作用的群)这个数字指,最少自乘多少次可以回到原始状态,反应了一个群的复杂程度。Explain of order

Nauty 里通过两个参数和一个公式来描述一个群的秩:

在这里插入图片描述

Pynauty

使用 pynauty(github.com) 可以很好的练习群论知识。

Pynauty 实现了以下主要功能:

  1. 给定一个图,返回其自同构群相关信息。
  2. 给定两个图,返回二者是否同构。
  3. 给定一个图,返回正则化的命名序列。
  4. 可以考虑点的特征信息。

下面程序把乙二醛的图信息转化成了 Pynauty 格式,解出了这张图的自同构群信息,给出了正则化命名序列:

from pynauty import *
from ase.build import molecule
from ase.visualize import view
from ase.neighborlist import build_neighbor_list


a_mol = molecule(name='OCHCHO')
neighborlist = build_neighbor_list(a_mol, bothways=False, self_interaction=False)

view(a_mol)

mat = neighborlist.get_connectivity_matrix(sparse=True)

adj_dic = {}
for i in range(len(a_mol)):
    if len(mat[i]) == 0:
        continue
    adj_dic.update({i: list(mat[i].tocoo().col)})

a_g = Graph(number_of_vertices=len(a_mol), directed=False, adjacency_dict=adj_dic)
generators, grpsize1, grpsize2, orbits, numorbits = autgrp(a_g)
lab = canon_label(a_g)

需要注意的是,我们传入的图信息,不包括点和边的特征信息。目前 Pynauty 可以通过染色的方式传入点的特征信息,但不支持边的特征信息录入。此外,Pynauty 仅能在 Linux 和 Mac 平台运行,没有 win 版本。

虽然功能有限,但如果只是使用 Nauty 程序包的核心功能,即解决两图是否同构的问题,Pynauty 不失为一种很好的选择。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
nauty是一个用于图同构的软件包,可以高效地测试两个图是否同构。下面是使用nauty判断两个图是否同构的具体流程: 1. 对于两个图G1和G2,分别将它们转换成nauty可以处理的格式。 2. 对于两个图的nauty格式,使用nauty生成它们的归一化表示。 3. 比较两个图的归一化表示是否相同,如果相同,则认为G1和G2同构。 下面是使用C语言代码实现nauty判断两个图是否同构的示例: ``` #include <stdio.h> #include <stdlib.h> #include <nauty.h> // 定义图的最大节点数和最大边数 #define MAXN 100 #define MAXM 5000 int main() { // 读入两个图 int n, m1, m2; scanf("%d%d%d", &n, &m1, &m2); // 构造两个图的邻接矩阵 graph g1[MAXM * 2], g2[MAXM * 2]; int deg1[MAXN], deg2[MAXN]; memset(deg1, 0, sizeof(deg1)); memset(deg2, 0, sizeof(deg2)); for (int i = 0; i < m1; i++) { int u, v; scanf("%d%d", &u, &v); u--; v--; g1[i * 2] = u; g1[i * 2 + 1] = v; deg1[u]++; deg1[v]++; } for (int i = 0; i < m2; i++) { int u, v; scanf("%d%d", &u, &v); u--; v--; g2[i * 2] = u; g2[i * 2 + 1] = v; deg2[u]++; deg2[v]++; } // 转换成nauty格式 static DEFAULTOPTIONS_GRAPH(options); options.getcanon = TRUE; options.defaultptn = FALSE; statsblk stats; int lab[MAXN], ptn[MAXN], orbits[MAXN]; int m = SETWORDSNEEDED(n); graph canong1[m * n], canong2[m * n]; EMPTYGRAPH(canong1, 1, n); EMPTYGRAPH(canong2, 1, n); for (int i = 0; i < n; i++) { lab[i] = i; ptn[i] = 0; } int idx1 = 0, idx2 = 0; for (int i = 0; i < n; i++) { ptn[idx1++] = i; if (i == n - 1 || deg1[i] != deg1[i + 1]) { ptn[idx1 - 1] = 0; densenauty(g1, lab, ptn, orbits, &options, &stats, m, n, canong1); EMPTYGRAPH(g1, m, n); idx1 = 0; } ptn[idx2++] = i; if (i == n - 1 || deg2[i] != deg2[i + 1]) { ptn[idx2 - 1] = 0; densenauty(g2, lab, ptn, orbits, &options, &stats, m, n, canong2); EMPTYGRAPH(g2, m, n); idx2 = 0; } } // 判断是否同构 if (memcmp(canong1, canong2, m * n * sizeof(graph)) == 0) { printf("Same\n"); } else { printf("Different\n"); } return 0; } ``` 在上面的代码中,我们先输入两个图的节点数、边数和边的信息,然后构造它们的邻接矩阵。接着,我们使用nauty将它们转换成nauty可以处理的格式,并生成它们的归一化表示。最后,比较两个图的归一化表示是否相同,从而判断它们是否同构。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值