子图同构之Ullmann
1.子图同构问题描述
首先描述一下子图同构是什么意思,一个图由一系列节点和一系列连接节点的边构成,节点和边都可以有标签,也就是可以给他们按属性分类。
精确点:一个图G=(V,E)G=(V,E)G=(V,E)由集合VVV和集合EEE组成,VVV是节点(node)集合, EEE是边(edge)集合,且E⊂V×VE\subset V\times VE⊂V×V ,用LvL_vLv表示节点上的标签集合,LeL_eLe表示边上的标签集合,那么每个节点对应的标签由函数(或者说映射)λv:V→Lv\lambda_v:V\rightarrow L_vλv:V→Lv 确定,每条边对应的标签由函数λe:E→Le\lambda_e:E\rightarrow L_eλe:E→Le 确定。
现在给定两个图G1=(V1,E1)G_1=(V_1,E_1)G1=(V1,E1), G2=(V2,E2)G_2=(V_2,E_2)G2=(V2,E2) ,其中G1G_1G1是比较小的图(我们把它叫做pattern graph),G2G_2G2是比较大的图(我们把它叫做target graph),用集合M⊂V1×V2M\sub V_1\times V_2M⊂V1×V2 表示两个图中节点的对应关系。如果节点u∈V1u\in V_1u∈V1,则μ(u)∈V2\mu(u)\in V_2μ(u)∈V2表示与节点uuu对应的G2G_2G2中的节点;如果节点v∈V2v\in V_2v∈V2,则ν(v)∈V1\nu(v)\in V_1ν(v)∈V1表示与节点vvv对应的G1G_1G1中的节点。
G1G_1G1 | G2G_2G2 | |
---|---|---|
对应关系 | uuu | μ(u)\mu(u)μ(u) |
对应关系 | μ−1(v)\mu^{-1} (v)μ−1(v) | vvv |
如果以下6个条件成立,则这两个图是子图同构的。
- ∀u∈V1∃μ(u)∈V2:(u,μ(u))∈M \forall u\in V_1 \quad \exist \mu(u)\in V_2:(u,\mu(u))\in M∀u∈V1∃μ(u)∈V2:(u,μ(u))∈M
- ∀u,u′∈V1u≠u′⇒μ(u)≠μ(u′)\forall u,u^{'}\in V_1 \quad u\neq u^{'}\Rightarrow \mu(u)\neq \mu(u^{'}) ∀u,u′∈V1u=u′⇒μ(u)=μ(u′)
- ∀(u,u′)∈E1∃(μ(u),μ(u′))∈E2)\forall (u,u^{'})\in E_1 \quad \exist (\mu(u),\mu(u^{'}))\in E_2)∀(u,u′)∈E1∃(μ(u),μ(u′))∈E2)
- ∀u,u′∈V1(μ(u),μ(u′))∈E2⇒(u,u′)∈E1\forall u,u^{'} \in V_1 \quad (\mu(u),\mu(u^{'})) \in E_2 \Rightarrow (u,u^{'}) \in E_1∀u,u′∈V1(μ(u),μ(u′))∈E2⇒(u,u′)∈E1
- ∀u∈V1λV1(u)=λV2(μ(u))\forall u \in V_1 \quad \lambda_{V_1}(u)=\lambda_{V_2}(\mu(u))∀u∈V1λV1(u)=λV2(μ(u))
- ∀(u,u′)∈E1λe1(u,u′)=λe2(μ(u),μ(u′))\forall (u,u^{'}) \in E_1 \quad \lambda_{e_1}(u,u^{'})=\lambda_{e_2}(\mu(u),\mu(u^{'}))∀(u,u′)∈E1λe1(u,u′)=λe2(μ(u),μ(u′))
用人话来解释一下:
-
对于小图中每个节点,大图中都要有一个对应的节点与之对应,并且这样一对一对的节点构成了集合MMM;
-
小图中任意两个不一样的节点,他们对应的大图中的节点不能是同一个;
-
小图中每条边,大图中都有一条边与之对应,并且他们两端的节点一一对应;
-
小图中任意两个节点,如果他们对应的大图中的节点之间有一条边,那么小图中这两个节点之间也得有条边;
-
每对对应节点的label要相同,也就是这俩节点类型或属性相同;
-
每对对应边的label要相同,也就是说这俩边的类型或属性相同。
综上所述,子图同构简单来说就是,大图中有一个子结构,长得跟小图一模一样,而且这个子结构的节点之间不能多出小图中不存在的边来,如果要去掉最后这个而且,就把上面第4个条件去掉~
2 ullmann算法子图同构判定方法
首先,图G1=(V1,E1)G_1=(V_1,E_1)G1=(V1,E1)的邻接矩阵为A=[aij]A=[a_{ij}]A=[aij],图G2=(V2,E2)G_2=(V_2,E_2)G2=(V2,E2)的邻接矩阵为B=[bij]B=[b_{ij}]B=[bij],其中邻接矩阵构建规则如下:
xij={1 存在节点i→节点j的边0 不存在节点i→节点j的边 x_{ij}=\left\{ \begin{aligned} 1 \ \ \ \ \ \ 存在节点i\rightarrow 节点j的边 \\ 0 \ \ 不存在节点i\rightarrow 节点j的边 \end{aligned} \right. xij={1 存在节点i→节点j的边0 不存在节点i→节点j的边
G1G_1G1有p1p_1p1个节点和q1q_1q1条边,G2G_2G2有p2p_2p2个节点和q2q_2q2条边。
定义大小为p1×p2p_1\times p_2p1×p2的0-1矩阵M=[mij]M=[m_{ij}]M=[mij],表示G1G_1G1中节点和G2G_2G2中节点的一个映射,若mij=1m_{ij}=1mij=1则表示G2G_2G2中的节点jjj与G1G_1G1中的节点iii构成一对节点对。
定义矩阵C=[cij]=MBMTC=[c_{ij}]=MBM^TC=[cij]=MBMT,如果满足如下条件,则矩阵MMM确定了一个G1G_1G1到G2G_2G2的子图同构,也就是在G2G_2G2中找到了一个跟G1G_1G1一模一样的子结构:
(∀i∀j)1≤i≤q1,1≤j≤q1(aij=1)⇒(cij=1) (\forall i\forall j)_{1\le i\le q_1,1\le j\le q_1} (a_{ij}=1)\Rightarrow(c_{ij}=1) (∀i∀j)1≤i≤q1,1≤j≤q1(aij=1)⇒(cij=1)
解释一下:
矩阵MMM要确定一组节点的对应关系使小图和大图中对应节点构成的子图一模一样,必须满足:每一行正好有一个1,每一列最多有1个1,因为小图中每个节点都要在大图中找到一个对应的节点,而大图中的节点最多可以和小图中一个节点对应。
矩阵C=MBMTC=MBM^TC=MBMT,对矩阵BBB做行列变换,左乘行变换,右乘列变换,先左乘MMM再右乘MTM^TMT实则是交换了一系列节点编号(或顺序),CCC矩阵表达了一个将大图中的节点顺序进行一系列交换,排在最前面的q1q_1q1个节点构成的子结构,该结构正好和小图节点可以一一对应,并且小图中有的边,大图的这个子结构中也有,则找到了一个同构子图,下面举个简单例子:
A=A1^2^3^1^0102^0013^100 B=B1^2^3^4^1^01002^00103^10014^0100 A=\begin{array}{c|cccc} A & \hat 1 & \hat 2 & \hat 3 \\ \hline \hat 1 & 0 & 1 & 0 \\ \hat 2 & 0 & 0 & 1 \\ \hat 3 & 1 & 0 & 0 \\ \end{array} \ \ \ \ \ \ \ \ \ \ \ B=\begin{array}{c|cccc} B & \hat 1 & \hat 2 & \hat 3 & \hat 4 \\ \hline \hat 1 & 0 & 1 & 0 & 0 \\ \hat 2 & 0 & 0 & 1 & 0 \\ \hat 3 & 1 & 0 & 0 & 1 \\ \hat 4 & 0 & 1 & 0 & 0 \\ \end{array} A=A1^2^3^1^0012^1003^010 B=B1^2^3^4^1^00102^10013^01004^0010
有2个同构形式:
M1=M11^2^3^4^1^10002^01003^0010 M2=M21^2^3^4^1^00012^01003^0010 M_1=\begin{array}{c|cccc} M_1 & \hat 1 & \hat 2 & \hat 3 & \hat 4 \\ \hline \hat 1 & 1 & 0 & 0 & 0 \\ \hat 2 & 0 & 1 & 0 & 0 \\ \hat 3 & 0 & 0 & 1 & 0 \\ \end{array} \ \ \ \ \ \ \ \ \ \ \ M_2=\begin{array}{c|cccc} M_2 & \hat 1 & \hat 2 & \hat 3 & \hat 4 \\ \hline \hat 1 & 0 & 0 & 0 & 1 \\ \hat 2 & 0 & 1 & 0 & 0 \\ \hat 3 & 0 & 0 & 1 & 0 \\ \end{array} M1=M11^2^3^1^1002^0103^0014^000 M2=M21^2^3^1^0002^0103^0014^100
第一个:C1=M1BM1TC_1=M_1BM_1^TC1=M1BM1T,首先M1B=[010000101001]M_1B=\begin{bmatrix} 0&1&0&0\\0&0&1&0\\1&0&0&1\end{bmatrix}M1B=001100010001,即取前三行并且没有换位置,再右乘M1TM_1^TM1T得C1=[010001100]C_1=\begin{bmatrix} 0&1&0\\0&0&1\\1&0&0\end{bmatrix}C1=001100010,即取前三列并且没有换位置,此时AAA中为1的位置C1C_1C1中也都为1,也就是AAA中有的边C1C_1C1中也都有,M1M_1M1表达了:将图BBB取前三个节点,使A1−B1,A2−B2,A3−B3A1-B1,A2-B2,A3-B3A1−B1,A2−B2,A3−B3一一对应,即得到一个子图同构的解。
第二个:C2=M2BM2TC_2=M_2BM_2^TC2=M2BM2T,首先M2B=[010000101001]M_2B=\begin{bmatrix} 0&1&0&0\\0&0&1&0\\1&0&0&1\end{bmatrix}M2B=001100010001,即依次取BBB的第4行,第2行,第3行,再右乘M2TM_2^TM2T得C2=[010001100]C_2=\begin{bmatrix} 0&1&0\\0&0&1\\1&0&0\end{bmatrix}C2=001100010,即即依次取M2BM_2BM2B的第4列,第2列,第3列,此时AAA中为1的位置C2C_2C2中也都为1,也就是AAA中有的边C2C_2C2中也都有,M2M_2M2表达了:将图BBB取第4,第2,第3个节点,使A1−B4,A2−B2,A3−B3A1-B4,A2-B2,A3-B3A1−B4,A2−B2,A3−B3一一对应,即得到一个子图同构的解。
3 ullmann算法状态空间
要找出所有的子图同构形式,一个很自然的想法就是遍历所有可能的MMM,一个个判断是否符合上面的判定公式。
首先,构造大小为p1×p2p_1\times p_2p1×p2的0-1矩阵M0=[mij0]M^0=[m^0_{ij}]M0=[mij0],其中
mij0={1 大图第j个节点的度不小于小图第i个节点的度0 大图第j个节点的度小于小图第i个节点的度
m^0_{ij}=\left\{
\begin{aligned}
1 \ \ \ 大图第j个节点的度不小于小图第i个节点的度 \\
0 \ \ \ \ \ \ \ 大图第j个节点的度小于小图第i个节点的度
\end{aligned}
\right.
mij0={1 大图第j个节点的度不小于小图第i个节点的度0 大图第j个节点的度小于小图第i个节点的度
然后就按一定顺序产生矩阵MMM,要求是,MMM中为1的位置,M0M^0M0中也必须为1,并且还得满足每一行正好有一个1,每一列最多有1个1。也就是说,M0M^0M0列出了所有可能匹配上的节点对,而我们每次从中找出一组进行验证。
上面的例子中,小图节点的度均为2,大图节点的度分别为2,3,3,2。因此构造M0M^0M0如下:
M0=M01^2^3^4^1^11112^11113^1111
M_0=\begin{array}{c|cccc}
M_0 & \hat 1 & \hat 2 & \hat 3 & \hat 4 \\ \hline
\hat 1 & 1 & 1 & 1 & 1 \\
\hat 2 & 1 & 1 & 1 & 1 \\
\hat 3 & 1 & 1 & 1 & 1 \\
\end{array}
M0=M01^2^3^1^1112^1113^1114^111
然后进行深度优先搜索,定义状态sss是一个中间状态,也就是选取候选矩阵时确定某几行的状态,由sss构成搜索树,s0s_0s0对应初始矩阵M0M_0M0,sijs_i^jsij对应一个矩阵,iii表示已经选取的矩阵行数(前几行),也相当于sijs_i^jsij在搜索树中的深度,jjj表示该深度下第几种可能。
s0=s01^2^3^4^1^11112^11113^1111s11=s111^2^3^4^1^10002^11113^1111 s21=s211^2^3^4^1^10002^01003^1111 s31=s311^2^3^4^1^10002^01003^0010s11=s111^2^3^4^1^10002^11113^1111 s21=s211^2^3^4^1^10002^01003^1111 s32=s321^2^3^4^1^10002^01003^0001s11=s111^2^3^4^1^10002^11113^1111 s22=s221^2^3^4^1^10002^00103^1111 s33=s331^2^3^4^1^10002^00103^0100s11=s111^2^3^4^1^10002^11113^1111 s22=s221^2^3^4^1^10002^00103^1111 s34=s341^2^3^4^1^10002^00103^0001...
s_0=\begin{array}{c|cccc}
s_0 & \hat 1 & \hat 2 & \hat 3 & \hat 4 \\ \hline
\hat 1 & 1 & 1 & 1 & 1 \\
\hat 2 & 1 & 1 & 1 & 1 \\
\hat 3 & 1 & 1 & 1 & 1 \\
\end{array} \\
s_1^1=\begin{array}{c|cccc}
s_1^1 & \hat 1 & \hat 2 & \hat 3 & \hat 4 \\ \hline
\hat 1 & 1 & 0 & 0 & 0 \\
\hat 2 & 1 & 1 & 1 & 1 \\
\hat 3 & 1 & 1 & 1 & 1 \\
\end{array}
\ \ \ \ \ \ \ \
s_2^1=\begin{array}{c|cccc}
s_2^1 & \hat 1 & \hat 2 & \hat 3 & \hat 4 \\ \hline
\hat 1 & 1 & 0 & 0 & 0 \\
\hat 2 & 0 & 1 & 0 & 0 \\
\hat 3 & 1 & 1 & 1 & 1 \\
\end{array}
\ \ \ \ \ \ \ \
s_3^1=\begin{array}{c|cccc}
s_3^1 & \hat 1 & \hat 2 & \hat 3 & \hat 4 \\ \hline
\hat 1 & 1 & 0 & 0 & 0 \\
\hat 2 & 0 & 1 & 0 & 0 \\
\hat 3 & 0 & 0 & 1 & 0 \\
\end{array} \\
s_1^1=\begin{array}{c|cccc}
s_1^1 & \hat 1 & \hat 2 & \hat 3 & \hat 4 \\ \hline
\hat 1 & 1 & 0 & 0 & 0 \\
\hat 2 & 1 & 1 & 1 & 1 \\
\hat 3 & 1 & 1 & 1 & 1 \\
\end{array}
\ \ \ \ \ \ \ \
s_2^1=\begin{array}{c|cccc}
s_2^1 & \hat 1 & \hat 2 & \hat 3 & \hat 4 \\ \hline
\hat 1 & 1 & 0 & 0 & 0 \\
\hat 2 & 0 & 1 & 0 & 0 \\
\hat 3 & 1 & 1 & 1 & 1 \\
\end{array}
\ \ \ \ \ \ \ \
s_3^2=\begin{array}{c|cccc}
s_3^2 & \hat 1 & \hat 2 & \hat 3 & \hat 4 \\ \hline
\hat 1 & 1 & 0 & 0 & 0 \\
\hat 2 & 0 & 1 & 0 & 0 \\
\hat 3 & 0 & 0 & 0 & 1 \\
\end{array} \\
s_1^1=\begin{array}{c|cccc}
s_1^1 & \hat 1 & \hat 2 & \hat 3 & \hat 4 \\ \hline
\hat 1 & 1 & 0 & 0 & 0 \\
\hat 2 & 1 & 1 & 1 & 1 \\
\hat 3 & 1 & 1 & 1 & 1 \\
\end{array}
\ \ \ \ \ \ \ \
s_2^2=\begin{array}{c|cccc}
s_2^2 & \hat 1 & \hat 2 & \hat 3 & \hat 4 \\ \hline
\hat 1 & 1 & 0 & 0 & 0 \\
\hat 2 & 0 & 0 & 1 & 0 \\
\hat 3 & 1 & 1 & 1 & 1 \\
\end{array}
\ \ \ \ \ \ \ \
s_3^3=\begin{array}{c|cccc}
s_3^3 & \hat 1 & \hat 2 & \hat 3 & \hat 4 \\ \hline
\hat 1 & 1 & 0 & 0 & 0 \\
\hat 2 & 0 & 0 & 1 & 0 \\
\hat 3 & 0 & 1 & 0 & 0 \\
\end{array} \\
s_1^1=\begin{array}{c|cccc}
s_1^1 & \hat 1 & \hat 2 & \hat 3 & \hat 4 \\ \hline
\hat 1 & 1 & 0 & 0 & 0 \\
\hat 2 & 1 & 1 & 1 & 1 \\
\hat 3 & 1 & 1 & 1 & 1 \\
\end{array}
\ \ \ \ \ \ \ \
s_2^2=\begin{array}{c|cccc}
s_2^2 & \hat 1 & \hat 2 & \hat 3 & \hat 4 \\ \hline
\hat 1 & 1 & 0 & 0 & 0 \\
\hat 2 & 0 & 0 & 1 & 0 \\
\hat 3 & 1 & 1 & 1 & 1 \\
\end{array}
\ \ \ \ \ \ \ \
s_3^4=\begin{array}{c|cccc}
s_3^4 & \hat 1 & \hat 2 & \hat 3 & \hat 4 \\ \hline
\hat 1 & 1 & 0 & 0 & 0 \\
\hat 2 & 0 & 0 & 1 & 0 \\
\hat 3 & 0 & 0 & 0 & 1 \\
\end{array} \\
...
s0=s01^2^3^1^1112^1113^1114^111s11=s111^2^3^1^1112^0113^0114^011 s21=s211^2^3^1^1012^0113^0014^001 s31=s311^2^3^1^1002^0103^0014^000s11=s111^2^3^1^1112^0113^0114^011 s21=s211^2^3^1^1012^0113^0014^001 s32=s321^2^3^1^1002^0103^0004^001s11=s111^2^3^1^1112^0113^0114^011 s22=s221^2^3^1^1012^0013^0114^001 s33=s331^2^3^1^1002^0013^0104^000s11=s111^2^3^1^1112^0113^0114^011 s22=s221^2^3^1^1012^0013^0114^001 s34=s341^2^3^1^1002^0003^0104^001...
因此构成了一棵由状态sss构成的树:
当深度达到与小图节点数相同时,也就得到了一个候选矩阵MMM,再用第2节中的判别公式判断一下就可以了。这里可以看到,其实深度就是当下考虑的小图节点数,当前行选择的1的位置也就是选择大图中对应的节点。
用深度优先搜索遍历完整棵树后,就找到了所有的同构子图,这也就是在M0M^0M0下的暴力搜索。
4 剪枝
在暴力搜索的基础上,可以根据很多信息比如图的拓扑结构进行剪枝,只要我们确定小图第iii个节点和大图第jjj个节点对应不可能构成子图同构,就把M0M^0M0中mijm_{ij}mij设置为0。这是一开始给的约束,比如第三节中给的约束:如果小图中一个节点的度大于大图中某个节点的度,那么这两个节点不可能对应上,算法一开始就把这种情况约束掉了,不需要进行搜索。
至于剪枝规则,后来提出的很多算法有非常优越的剪枝效果,比如VF2、VF3等,后续还会总结一下这几个算法,ullmann算法可以说是子图同构的鼻祖,理解这个算法可以对子图同构问题以及树搜索和剪枝有更好的理解,有助于更好地理解后来大牛们提出的众多算法~