Python 枚举所有具有 5 个节点的无环有向图的代码
Python code to enumerate over all acyclic digraphs with 5 nodes
所以我最近在学习这门关于概率图形模型的课程,我想尝试一个动手的例子。在示例中,我想遍历所有可能的组合 (29,281) 个无环有向图(或 DAG),看看它们如何拟合数据。
我知道所有可能的图的数量由
给出
from scipy.misc import comb
import numpy as np
def a(n):
if n == 0:
return 1
else:
sum = 0
for k in range(1,n+1):
sum += np.power(-1,k+1) * \
comb(n,k) * \
np.power(2,k*(n-k)) * \
a(n-k)
return sum
这给了我们系列 A003024
但我想找到能够遍历所有这些可能的图而不仅仅是计算它们的算法。
我知道有一些代码可用于无向图,但我无法让它们为我工作。
我对图形的任何形式表示持开放态度,无论是库、自定义函数还是列表列表。
示例 - 当您有两个标签时,有 3 个可能的图形:
[[A:{}], [B:{}]] # A B no connection P(A,B) = P(A)P(B)
[[A:{B}], [B:{}]] # A -> B P(A,B) = P(A)P(B|A)
[[A:{}], [B:{A}]] # A <- B P(A,B) = P(B)P(A|B)
将图形简单地视为边的连接(节点可以保持不变)可能更容易。
从 numEdges = 0(无连通图)开始,到 numEdges = numNodes - 1(全连通图)。对于每个 numEdges,只需将边放置在每个可能的排列中。
使用全连接图设置可能的边。例如对于五个节点:
AB
AC
AD
AE
BC
BD
BE
CD
CE
DE
注意模式:A(B、C、D、E)、B(C、D、E)、C(D、E)、D(E)
准备好可能的边列表后,根据排列放置下边应该很简单。
因为你想要 29,281 个结果图,标签对你来说一定很重要(IOW,你不是通过同构来修改。)在 networkx 中使用暴力方法:
from itertools import combinations, product
import networkx as nx
def gen_dag(num_nodes):
all_edges = list(combinations(range(num_nodes), 2))
for p in product([None, 1, -1], repeat=len(all_edges)):
G = nx.DiGraph()
G.add_nodes_from(range(num_nodes))
edges = [edge[::edge_dir] for edge, edge_dir in zip(all_edges, p) if edge_dir]
G.add_edges_from(edges)
if nx.is_directed_acyclic_graph(G):
yield G
这给出了
In [75]: graphs = list(gen_dag(5))
In [76]: len(graphs)
Out[76]: 29281
和(例如)
In [79]: graphs[1234].edges()
Out[79]: OutEdgeView([(3, 1), (3, 4), (4, 0), (4, 2)])
In [80]: nx.adjacency_matrix(graphs[1234]).todense()
Out[80]:
matrix([[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 1, 0, 0, 1],
[1, 0, 1, 0, 0]], dtype=int64)
所以我最近在学习这门关于概率图形模型的课程,我想尝试一个动手的例子。在示例中,我想遍历所有可能的组合 (29,281) 个无环有向图(或 DAG),看看它们如何拟合数据。
我知道所有可能的图的数量由
给出from scipy.misc import comb
import numpy as np
def a(n):
if n == 0:
return 1
else:
sum = 0
for k in range(1,n+1):
sum += np.power(-1,k+1) * \
comb(n,k) * \
np.power(2,k*(n-k)) * \
a(n-k)
return sum
这给了我们系列 A003024
但我想找到能够遍历所有这些可能的图而不仅仅是计算它们的算法。
我知道有一些代码可用于无向图,但我无法让它们为我工作。
我对图形的任何形式表示持开放态度,无论是库、自定义函数还是列表列表。
示例 - 当您有两个标签时,有 3 个可能的图形:
[[A:{}], [B:{}]] # A B no connection P(A,B) = P(A)P(B)
[[A:{B}], [B:{}]] # A -> B P(A,B) = P(A)P(B|A)
[[A:{}], [B:{A}]] # A <- B P(A,B) = P(B)P(A|B)
将图形简单地视为边的连接(节点可以保持不变)可能更容易。
从 numEdges = 0(无连通图)开始,到 numEdges = numNodes - 1(全连通图)。对于每个 numEdges,只需将边放置在每个可能的排列中。
使用全连接图设置可能的边。例如对于五个节点:
AB
AC
AD
AE
BC
BD
BE
CD
CE
DE
注意模式:A(B、C、D、E)、B(C、D、E)、C(D、E)、D(E)
准备好可能的边列表后,根据排列放置下边应该很简单。
因为你想要 29,281 个结果图,标签对你来说一定很重要(IOW,你不是通过同构来修改。)在 networkx 中使用暴力方法:
from itertools import combinations, product
import networkx as nx
def gen_dag(num_nodes):
all_edges = list(combinations(range(num_nodes), 2))
for p in product([None, 1, -1], repeat=len(all_edges)):
G = nx.DiGraph()
G.add_nodes_from(range(num_nodes))
edges = [edge[::edge_dir] for edge, edge_dir in zip(all_edges, p) if edge_dir]
G.add_edges_from(edges)
if nx.is_directed_acyclic_graph(G):
yield G
这给出了
In [75]: graphs = list(gen_dag(5))
In [76]: len(graphs)
Out[76]: 29281
和(例如)
In [79]: graphs[1234].edges()
Out[79]: OutEdgeView([(3, 1), (3, 4), (4, 0), (4, 2)])
In [80]: nx.adjacency_matrix(graphs[1234]).todense()
Out[80]:
matrix([[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 1, 0, 0, 1],
[1, 0, 1, 0, 0]], dtype=int64)