1 超图
在数学中,超图(Hypergraph)是一种广义上的图,是有限集合中最一般的离散结构,在信息科学、生命科学等领域有着广泛的应用。(如果有疑问,可以参看我写的另外一篇超图基础知识的博客。)
图 由 边(edge) 和 顶点(vertex 或 node) 组成。
超图 由 超边(hyperedge) 和 顶点(vertex) 组成。
类型 | 元素 |
---|---|
图 | 顶点 + 边 |
超图 | 顶点 + 超边 |
普通图,一条边只能连接两个顶点,而超图的一条边可以连接任意数量的顶点。
记超图为
G
G
G,顶点集合为
V
\mathcal{V}
V,超边集合为
E
\mathcal{E}
E,则有
G
=
(
V
,
E
)
G = (\mathcal{V}, \mathcal{E})
G=(V,E)
如果是加权超图,则:
G
=
(
V
,
E
,
W
)
G = (\mathcal{V}, \mathcal{E}, W)
G=(V,E,W)
其中
W
W
W 表示权重。
对于顶点集合
V
\mathcal{V}
V 中的任意顶点
v
v
v,可以表示为
v
∈
V
v \in \mathcal{V}
v∈V;
对于超边集合
E
\mathcal{E}
E 中的任意超边
e
e
e,可以表示为
e
∈
E
e \in \mathcal{E}
e∈E,且
e
⊂
V
e \subset \mathcal{V}
e⊂V。
2 代码
顶点 v v v 应该包含一些属性:
- 名称 —— 顶点得有一个专属编码吧,不与其他顶点重复,用于区分不同的顶点;
- 权重 —— 顶点的权重,这个可有可无,看你的实际需求。
变量:类型 | 含义 | 符号 |
---|---|---|
Vertex: class | 顶点 | v v v |
name: str (int) | 顶点名称 | |
weight: float | 顶点权重 | w ( v ) w(v) w(v) |
那么确定好顶点的属性之后,就可以定义顶点类了。
class Vertex:
def __init__(self, name: str = None, weight: float = None):
self.name = name
self.weight = weight
超边 e e e 应该包含一些属性:
- 名称 —— 超边得有一个专属编码吧,不与其他超边重复,用于区分不同的超边;
- 顶点集 —— 超边是由特定的顶点集组成的,那么得保存该超边包含的顶点集;
- 权重 —— 超边的权重,这个可有可无,看你的实际需求。
变量:类型 | 含义 | 符号 |
---|---|---|
Edge: class | 超边 | e e e |
name: str (int) | 超边名称 | |
vertices: List [Vertex] | 超边包含的顶点集 | ⋃ v ∈ e v \bigcup_{v \in e} v ⋃v∈ev |
weight: float | 超边权重 | w ( e ) w(e) w(e) |
那么确定好超边的属性之后,就可以定义超边类了。
from typing import List
import Vertex
class Edge:
def __init__(self, name: str = None, weight: float = None, vertices: List[Vertex] = None):
self.name = name
self.weight = weight
self.vertices = vertices
超图 G G G 是由顶点集 V V V 和超边集 E E E 组成的。那么定义 G G G 只需要将 V V V 和 E E E 的定义组装起来就可以啦。
超图 G G G 应该包含一些属性:
- 顶点集 —— 对应 V V V;
- 超边集 —— 对应 E E E;
- 关联矩阵 —— 这个可有可无,看你的实际需求;
- 顶点度 —— 这个可有可无,看你的实际需求;
- 超边度 —— 这个可有可无,看你的实际需求;
- 顶点度矩阵 —— 这个可有可无,看你的实际需求;
- 超边度矩阵 —— 这个可有可无,看你的实际需求;
变量:类型 | 含义 | 符号 |
---|---|---|
Hypergraph: class | 超图 | G G G |
vertices: List [Vertex] | 顶点集 | V V V |
edges: List [Edge] | 超边集 | E E E |
num_vertices: int | 顶点的数量 | ∣ V ∣ \lvert V \rvert ∣V∣ |
num_edges: int | 超边的数量 | ∣ E ∣ \lvert E \rvert ∣E∣ |
incident_matrix: np.ndarray | 关联矩阵 | H H H |
vertices_degree: np.ndarray | 所有顶点的度 | d d d |
edges_degree: np.ndarray | 所有超边的度 | δ \delta δ |
vertex_degree_diagonal_matrix: np.ndarray | 顶点度组成的对角矩阵 | D v D_v Dv |
edge_degree_diagonal_matrix: np.ndarray | 超边度组成的对角矩阵 | D e D_e De |
超图类定义:
from typing import List
import numpy as np
import Edge
import Vertex
class Hypergraph(object):
def __init__(self, vertices: List[Vertex] = None, edges: List[Edge] = None):
self.vertices = vertices
self.edges = edges
if vertices is None or edges is None:
self.num_vertices = 0
self.num_edges = 0
self.incident_matrix = None
self.vertices_degree = None
self.edges_degree = None
self.vertex_degree_diagonal_matrix = None
self.edge_degree_diagonal_matrix = None
else:
self.num_vertices = len(vertices)
self.num_edges = len(edges)
self.incident_matrix = self.calculate_incident_matrix()
self.vertices_degree = self.calculate_vertex_degree()
self.edges_degree = self.calculate_edge_degree()
self.vertex_degree_diagonal_matrix = self.calculate_diagonal_matrix_of_vertex_degree()
self.edge_degree_diagonal_matrix = self.calculate_diagonal_matrix_of_edge_degree()
def init_hypergraph_from_files(self, dataset_dir: str):
self.vertices, self.edges = hypergraph_construction(dataset_dir)
self.num_vertices = len(self.vertices)
self.num_edges = len(self.edges)
self.incident_matrix = self.calculate_incident_matrix()
self.vertices_degree = self.calculate_vertex_degree()
self.edges_degree = self.calculate_edge_degree()
self.vertex_degree_diagonal_matrix = self.calculate_diagonal_matrix_of_vertex_degree()
self.edge_degree_diagonal_matrix = self.calculate_diagonal_matrix_of_edge_degree()
def calculate_incident_matrix(self):
"""
Calculate the incident matrix of the hypergraph.
:return: The incident matrix of the hypergraph.
"""
incident_matrix = np.zeros(shape=(self.num_vertices, self.num_edges), dtype=int)
for i in range(self.num_vertices):
vertex = self.vertices[i]
for j in range(self.num_edges):
edge = self.edges[j]
if vertex in edge.vertices:
incident_matrix[i, j] = 1
return incident_matrix
def calculate_vertex_degree(self):
"""
Calculate the degree of vertices in the hypergraph.
:return: The degree of vertices in the hypergraph.
"""
edge_weights = np.zeros(shape=(self.num_edges,), dtype=np.float64)
for i in range(self.num_edges):
edge = self.edges[i]
edge_weights[i] = edge.weight
edge_weights = edge_weights.reshape(-1, 1)
vertex_degree_array = np.dot(self.incident_matrix, edge_weights)
return vertex_degree_array.reshape(vertex_degree_array.size, )
def calculate_edge_degree(self):
"""
Calculate the degree of edges in the hypergraph.
:return: The degree of edges in the hypergraph.
"""
edges_degree = self.incident_matrix.sum(axis=0)
return edges_degree
def calculate_diagonal_matrix_of_vertex_degree(self):
"""
Create a diagonal matrix with the degrees of vertex as the diagonal elements.
:return: The diagonal matrix.
"""
return np.diag(self.vertices_degree)
def calculate_diagonal_matrix_of_edge_degree(self):
"""
Create a diagonal matrix with the degrees of edge as the diagonal elements.
:return: The diagonal matrix.
"""
return np.diag(self.edges_degree)
3 说明
以上代码注重 “低耦合、高内聚” 的设计理念。在实际中,为了简略和便捷,往往会简化以上代码。请根据需要设计代码。