chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:36:30 +08:00
commit 55ab4e4a73
473 changed files with 72932 additions and 0 deletions
+185
View File
@@ -0,0 +1,185 @@
import easygraph as eg
import numpy as np
from easygraph.utils import *
__all__ = ["NOBE", "NOBE_GA"]
@not_implemented_for("multigraph")
def NOBE(G, K):
"""Graph embedding via NOBE[1].
Parameters
----------
G : easygraph.Graph
An unweighted and undirected graph.
K : int
Embedding dimension k
Returns
-------
Y : list
list of embedding vectors (y1, y2, · · · , yn)
Examples
--------
>>> NOBE(G,K=15)
References
----------
.. [1] https://www.researchgate.net/publication/325004496_On_Spectral_Graph_Embedding_A_Non-Backtracking_Perspective_and_Graph_Approximation
"""
dict = {}
a = 0
for i in G.nodes:
dict[i] = a
a += 1
LG = graph_to_d_atleast2(G)
N = len(G)
P, pair = Transition(LG)
V = eigs_nodes(P, K)
Y = embedding(V, pair, K, N, dict, G)
return Y
@not_implemented_for("multigraph")
@only_implemented_for_UnDirected_graph
def NOBE_GA(G, K):
"""Graph embedding via NOBE-GA[1].
Parameters
----------
G : easygraph.Graph
An unweighted and undirected graph.
K : int
Embedding dimension k
Returns
-------
Y : list
list of embedding vectors (y1, y2, · · · , yn)
Examples
--------
>>> NOBE_GA(G,K=15)
References
----------
.. [1] https://www.researchgate.net/publication/325004496_On_Spectral_Graph_Embedding_A_Non-Backtracking_Perspective_and_Graph_Approximation
"""
from scipy.sparse.linalg import eigs
N = len(G)
A = np.eye(N, N)
for i in G.edges:
(u, v, t) = i
u = int(u) - 1
v = int(v) - 1
A[u, v] = 1
degree = G.degree()
D_inv = np.zeros([N, N])
a = 0
for i in degree:
D_inv[a, a] = 1 / degree[i]
a += 1
D_I_inv = np.zeros([N, N])
b = 0
for i in degree:
if degree[i] > 1:
D_I_inv[b, b] = 1 / (degree[i] - 1)
b += 1
I = np.identity(N)
M_D = 0.5 * A * D_I_inv * (I - D_inv)
D_D = 0.5 * I
T_ua = np.zeros([2 * N, 2 * N])
T_ua[0:N, 0:N] = M_D
T_ua[N : 2 * N, N : 2 * N] = M_D
T_ua[N : 2 * N, 0:N] = D_D
T_ua[0:N, N : 2 * N] = D_D
Y1, Y = eigs(T_ua, K + 1, which="LR")
Y = Y[0:N, :-1]
return Y
def graph_to_d_atleast2(G):
n = len(G)
LG = eg.Graph()
LG = G.copy()
new_node = n
degree = LG.degree()
node = LG.nodes.copy()
for i in node:
if degree[i] == 1:
for neighbors in LG.neighbors(node=i):
LG.add_edge(i, new_node)
LG.add_edge(new_node, neighbors)
break
new_node = new_node + 1
return LG
def Transition(LG):
N = len(LG)
M = LG.size()
LLG = eg.DiGraph()
for i in LG.edges:
(u, v, t) = i
LLG.add_edge(u, v)
LLG.add_edge(v, u)
degree = LLG.degree()
P = np.zeros([2 * M, 2 * M])
pair = []
k = 0
l = 0
for i in LLG.edges:
l = 0
for j in LLG.edges:
(u, v, t) = i
(x, y, z) = j
if v == x and u != y:
P[k][l] = 1 / (degree[v] - 1)
l += 1
k += 1
a = 0
for i in LLG.edges:
(u, v, t) = i
pair.append([u, v])
a += 1
return P, pair
def eigs_nodes(P, K):
from scipy.sparse.linalg import eigs
M = np.size(P, 0)
L = np.zeros([M, M])
I = np.identity(M)
P_T = P.T
L = I - (P + P_T) / 2
U, D = eigs(L, K + 1, which="LR")
D = D[:, :-1]
V = np.zeros([M, K], dtype=complex)
a = 0
for i in D:
V[a] = i
a += 1
return V
def embedding(V, pair, K, N, dict, G):
Y = np.zeros([N, K], dtype=complex)
idx = 0
for i in pair:
[v, u] = i
if u in G.nodes:
t = dict[u]
for j in range(0, len(V[idx])):
Y[t, j] += V[idx, j]
idx += 1
return Y
@@ -0,0 +1,13 @@
from .deepwalk import *
from .NOBE import *
from .node2vec import *
try:
from .line import *
from .sdne import *
except:
print(
"Warning raise in module:graph_embedding. Please install packages Pytorch"
" before you use functions related to graph_embedding"
)
@@ -0,0 +1,103 @@
import random
from easygraph.functions.graph_embedding.node2vec import (
_get_embedding_result_from_gensim_skipgram_model,
)
from easygraph.functions.graph_embedding.node2vec import learn_embeddings
from easygraph.utils import *
from tqdm import tqdm
__all__ = ["deepwalk"]
@not_implemented_for("multigraph")
def deepwalk(G, dimensions=128, walk_length=80, num_walks=10, **skip_gram_params):
"""Graph embedding via DeepWalk.
Parameters
----------
G : easygraph.Graph or easygraph.DiGraph
dimensions : int
Embedding dimensions, optional(default: 128)
walk_length : int
Number of nodes in each walk, optional(default: 80)
num_walks : int
Number of walks per node, optional(default: 10)
skip_gram_params : dict
Parameters for gensim.models.Word2Vec - do not supply `size`, it is taken from the `dimensions` parameter
Returns
-------
embedding_vector : dict
The embedding vector of each node
most_similar_nodes_of_node : dict
The most similar nodes of each node and its similarity
Examples
--------
>>> deepwalk(G,
... dimensions=128, # The graph embedding dimensions.
... walk_length=80, # Walk length of each random walks.
... num_walks=10, # Number of random walks.
... skip_gram_params = dict( # The skip_gram parameters in Python package gensim.
... window=10,
... min_count=1,
... batch_words=4,
... iter=15
... ))
References
----------
.. [1] https://arxiv.org/abs/1403.6652
"""
G_index, index_of_node, node_of_index = G.to_index_node_graph()
walks = simulate_walks(G_index, walk_length=walk_length, num_walks=num_walks)
model = learn_embeddings(walks=walks, dimensions=dimensions, **skip_gram_params)
(
embedding_vector,
most_similar_nodes_of_node,
) = _get_embedding_result_from_gensim_skipgram_model(
G=G, index_of_node=index_of_node, node_of_index=node_of_index, model=model
)
del G_index
return embedding_vector, most_similar_nodes_of_node
def simulate_walks(G, walk_length, num_walks):
walks = []
nodes = list(G.nodes)
print("Walk iteration:")
for walk_iter in tqdm(range(num_walks)):
random.shuffle(nodes)
for node in nodes:
walks.append(_deepwalk_walk(G, walk_length=walk_length, start_node=node))
return walks
def _deepwalk_walk(G, walk_length, start_node):
"""
Simulate a random walk starting from start node.
"""
walk = [start_node]
while len(walk) < walk_length:
cur = walk[-1]
cur_nbrs = sorted(G.neighbors(cur))
if len(cur_nbrs) > 0:
pick_node = random.choice(cur_nbrs)
walk.append(pick_node)
else:
break
return walk
+303
View File
@@ -0,0 +1,303 @@
import time
import warnings
import easygraph as eg
import numpy as np
import torch
import torch.nn as nn
from easygraph.utils import alias_draw
from easygraph.utils import alias_setup
from sklearn import preprocessing
# from easygraph.functions.graph_embedding import *
from tqdm import tqdm
warnings.filterwarnings("ignore")
class LINE(nn.Module):
"""Graph embedding via LINE.
Parameters
----------
G : easygraph.Graph or easygraph.DiGraph
dimension: int
walk_length: int
walk_num: int
negative: int
batch_size: int
init_alpha: float
order: int
Returns
-------
embedding_vector : dict
The embedding vector of each node
Examples
--------
>>> model = LINE(
... dimension=128,
... walk_length=80,
... walk_num=20,
... negative=5,
... batch_size=128,
... init_alpha=0.025,
... order=3 )
>>> model.train()
>>> emb = model(g, return_dict=True) # g: easygraph.Graph or easygraph.DiGraph
References
----------
.. [1] Tang, J., Qu, M., Wang, M., Zhang, M., Yan, J., & Mei, Q. (2015, May). Line: Large-scale information network embedding. In Proceedings of the 24th international conference on world wide web (pp. 1067-1077).
https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/frp0228-Tang.pdf
"""
@staticmethod
def add_args(parser):
"""Add model-specific arguments to the parser."""
parser.add_argument(
"--walk-length",
type=int,
default=80,
help="Length of walk per source. Default is 80.",
)
parser.add_argument(
"--walk-num",
type=int,
default=20,
help="Number of walks per source. Default is 20.",
)
parser.add_argument(
"--negative",
type=int,
default=5,
help="Number of negative node in sampling. Default is 5.",
)
parser.add_argument(
"--batch-size",
type=int,
default=1000,
help="Batch size in SGD training process. Default is 1000.",
)
parser.add_argument(
"--alpha",
type=float,
default=0.025,
help="Initial learning rate of SGD. Default is 0.025.",
)
parser.add_argument(
"--order",
type=int,
default=3,
help="Order of proximity in LINE. Default is 3 for 1+2.",
)
parser.add_argument("--hidden-size", type=int, default=128)
@classmethod
def build_model_from_args(cls, args):
return cls(
args.hidden_size,
args.walk_length,
args.walk_num,
args.negative,
args.batch_size,
args.alpha,
args.order,
)
def __init__(
self,
dimension=128,
walk_length=80,
walk_num=20,
negative=5,
batch_size=128,
init_alpha=0.025,
order=3,
):
super(LINE, self).__init__()
self.dimension = dimension
self.walk_length = walk_length
self.walk_num = walk_num
self.negative = negative
self.batch_size = batch_size
self.init_alpha = init_alpha
self.order = order
def forward(self, g, return_dict=True):
# run LINE algorithm, 1-order, 2-order or 3(1-order + 2-order)
self.G = g
self.is_directed = g.is_directed()
self.num_node = len(g.nodes)
self.num_edge = g.number_of_edges()
self.num_sampling_edge = self.walk_length * self.walk_num * self.num_node
node2id = dict([(node, vid) for vid, node in enumerate(g.nodes)])
self.edges = [[node2id[e[0]], node2id[e[1]]] for e in self.G.edges]
self.edges_prob = np.asarray([1.0 for e in g.edges])
self.edges_prob /= np.sum(self.edges_prob)
self.edges_table, self.edges_prob = alias_setup(self.edges_prob)
degree_weight = np.asarray([0] * self.num_node)
degree_weight = np.array(list(g.degree(node2id[u] for u in g.nodes).values()))
# for u,v in g.edges:
# degree_weight[node2id[u]] += 1.0
# if not self.is_directed:
# degree_weight[node2id[v]] += 1.0
self.node_prob = np.power(degree_weight, 0.75)
self.node_prob /= np.sum(self.node_prob)
self.node_table, self.node_prob = alias_setup(self.node_prob)
if self.order == 3:
self.dimension = int(self.dimension / 2)
if self.order == 1 or self.order == 3:
print("train line with 1-order")
print(type(self.dimension))
self.emb_vertex = (
np.random.random((self.num_node, self.dimension)) - 0.5
) / self.dimension
self._train_line(order=1)
embedding1 = preprocessing.normalize(self.emb_vertex, "l2")
if self.order == 2 or self.order == 3:
print("train line with 2-order")
self.emb_vertex = (
np.random.random((self.num_node, self.dimension)) - 0.5
) / self.dimension
self.emb_context = self.emb_vertex
self._train_line(order=2)
embedding2 = preprocessing.normalize(self.emb_vertex, "l2")
if self.order == 1:
embeddings = embedding1
elif self.order == 2:
embeddings = embedding2
else:
print("concatenate two embedding...")
embeddings = np.hstack((embedding1, embedding2))
if return_dict:
features_matrix = dict()
for vid, node in enumerate(g.nodes):
features_matrix[node] = embeddings[vid]
else:
features_matrix = np.zeros((len(g.nodes), embeddings.shape[1]))
nx_nodes = list(g.nodes)
features_matrix[nx_nodes] = embeddings[np.arange(len(g.nodes))]
return features_matrix
def _update(self, vec_u, vec_v, vec_error, label):
# update vetex embedding and vec_error
f = 1 / (1 + np.exp(-np.sum(vec_u * vec_v, axis=1)))
g = (self.alpha * (label - f)).reshape((len(label), 1))
vec_error += g * vec_v
vec_v += g * vec_u
def _train_line(self, order):
# train Line model with order
self.alpha = self.init_alpha
batch_size = self.batch_size
t0 = time.time()
num_batch = int(self.num_sampling_edge / batch_size)
epoch_iter = tqdm(range(num_batch))
for b in epoch_iter:
if b % 100 == 0:
epoch_iter.set_description(
# f"Progress: {b * 1.0 / num_batch * 100:.4f}, alpha: {self.alpha:.6f}, time: {time.time() - t0:.4f}"
)
self.alpha = self.init_alpha * max((1 - b * 1.0 / num_batch), 0.0001)
u, v = [0] * batch_size, [0] * batch_size
for i in range(batch_size):
edge_id = alias_draw(self.edges_table, self.edges_prob)
u[i], v[i] = self.edges[edge_id]
if not self.is_directed and np.random.rand() > 0.5:
v[i], u[i] = self.edges[edge_id]
vec_error = np.zeros((batch_size, self.dimension))
label, target = np.asarray([1 for i in range(batch_size)]), np.asarray(v)
for j in range(1 + self.negative):
if j != 0:
label = np.asarray([0 for i in range(batch_size)])
for i in range(batch_size):
target[i] = alias_draw(self.node_table, self.node_prob)
if order == 1:
self._update(
self.emb_vertex[u], self.emb_vertex[target], vec_error, label
)
else:
self._update(
self.emb_vertex[u], self.emb_context[target], vec_error, label
)
self.emb_vertex[u] += vec_error
if __name__ == "__main__":
dataset = eg.CiteseerGraphDataset(
force_reload=True
) # Download CiteseerGraphDataset contained in EasyGraph
num_classes = dataset.num_classes
g = dataset[0]
labels = g.ndata["label"]
edge_list = []
for i in g.edges:
edge_list.append((i[0], i[1]))
g1 = eg.Graph()
g1.add_edges_from(edge_list)
# print(g.edges)
# print(g.__dir__())
model = LINE(
dimension=128,
walk_length=80,
walk_num=20,
negative=5,
batch_size=128,
init_alpha=0.025,
order=3,
)
print(model)
model.train()
out = model(g1, return_dict=True)
keylist = sorted(out)
tmp = torch.cat(
(
torch.unsqueeze(torch.tensor(out[keylist[0]]), -2),
torch.unsqueeze(torch.tensor(out[keylist[1]]), -2),
),
0,
)
for i in range(2, len(keylist)):
tmp = torch.cat((tmp, torch.unsqueeze(torch.tensor(out[keylist[i]]), -2)), 0)
torch.save(tmp, "line.emb")
print(tmp, tmp.shape)
line_emb = []
for i in range(0, len(tmp)):
line_emb.append(list(tmp[i]))
line_emb = np.array(line_emb)
# tsne = TSNE(n_components=2)
# z = tsne.fit_transform(line_emb)
# z_data = np.vstack((z.T, labels)).T
# df_tsne = pd.DataFrame(z_data, columns=['Dim1', 'Dim2', 'class'])
# df_tsne['class'] = df_tsne['class'].astype(int)
# df_tsne.head()
#
# plt.figure(figsize=(8, 8))
# sns.scatterplot(data=df_tsne, hue='class', x='Dim1', y='Dim2', palette=['green','orange','brown','red', 'blue','black'])
# plt.savefig('torch_line_citeseer.pdf', bbox_inches='tight')
# plt.show()
#
#
Binary file not shown.
@@ -0,0 +1,175 @@
from __future__ import print_function
import argparse
import csv
import time
import warnings
from datetime import datetime
import easygraph as eg
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
import torch
from easygraph.datasets.citation_graph import CiteseerGraphDataset
from easygraph.functions.community import greedy_modularity_communities
from easygraph.functions.community import modularity
from easygraph.functions.graph_embedding import *
from mpl_toolkits.mplot3d import Axes3D
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
warnings.filterwarnings("ignore")
if __name__ == "__main__":
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
dataset = CiteseerGraphDataset(
force_reload=True
) # Download CiteseerGraphDataset contained in EasyGraph
num_classes = dataset.num_classes
g = dataset[0]
labels = g.ndata["label"]
print(labels, labels.shape, len(g.nodes))
print("Graph embedding via DeepWalk...........")
deepwalk_emb, _ = deepwalk(g, dimensions=128, walk_length=80, num_walks=10)
# print(deepwalk_emb, len(deepwalk_emb))
dw_emb = []
for i in range(0, len(deepwalk_emb)):
dw_emb.append(list(deepwalk_emb[i]))
# print(len(dw_emb))
dw_emb = np.array(dw_emb)
print(dw_emb)
tsne = TSNE(n_components=2, verbose=1, random_state=0)
z = tsne.fit_transform(dw_emb)
z_data = np.vstack((z.T, labels)).T
df_tsne = pd.DataFrame(z_data, columns=["Dim1", "Dim2", "class"])
df_tsne["class"] = df_tsne["class"].astype(int)
plt.figure(figsize=(8, 8))
sns.scatterplot(
data=df_tsne,
hue="class",
x="Dim1",
y="Dim2",
palette=["green", "orange", "brown", "red", "blue", "black"],
)
plt.savefig(
"figs/dw_citeseer.pdf", bbox_inches="tight"
) # save embeddings if needed
plt.savefig("figs/dw_citeseer.png", bbox_inches="tight")
plt.show()
print("Graph embedding via Node2Vec..............")
node2vec_emb, _ = node2vec(
g, dimensions=128, walk_length=80, num_walks=10, p=4, q=0.25
)
# print(node2vec_emb, len(node2vec_emb))
n2v_emb = []
for i in range(0, len(node2vec_emb)):
n2v_emb.append(list(node2vec_emb[i]))
# print(len(n2v_emb))
n2v_emb = np.array(n2v_emb)
print(n2v_emb)
tsne = TSNE(n_components=2, verbose=1, random_state=0)
z = tsne.fit_transform(n2v_emb)
z_data = np.vstack((z.T, labels)).T
df_tsne = pd.DataFrame(z_data, columns=["Dim1", "Dim2", "class"])
df_tsne["class"] = df_tsne["class"].astype(int)
plt.figure(figsize=(8, 8))
sns.scatterplot(
data=df_tsne,
hue="class",
x="Dim1",
y="Dim2",
palette=["green", "orange", "brown", "red", "blue", "black"],
)
plt.savefig("figs/n2v_citeseer.pdf", bbox_inches="tight")
plt.savefig("figs/n2v_citeseer.png", bbox_inches="tight")
plt.show()
print("Graph embedding via LINE........")
model = LINE(
dimension=128,
walk_length=80,
walk_num=10,
negative=5,
batch_size=128,
init_alpha=0.025,
order=2,
)
model.train()
line_emb = model(g, return_dict=True)
l_emb = []
for i in range(0, len(line_emb)):
l_emb.append(list(line_emb[i]))
# print(len(l_emb))
l_emb = np.array(l_emb)
print(l_emb)
tsne = TSNE(n_components=2, verbose=1, random_state=0)
z = tsne.fit_transform(l_emb)
z_data = np.vstack((z.T, labels)).T
df_tsne = pd.DataFrame(z_data, columns=["Dim1", "Dim2", "class"])
df_tsne["class"] = df_tsne["class"].astype(int)
plt.figure(figsize=(8, 8))
sns.scatterplot(
data=df_tsne,
hue="class",
x="Dim1",
y="Dim2",
palette=["green", "orange", "brown", "red", "blue", "black"],
)
plt.savefig("figs/line_citeseer.pdf", bbox_inches="tight")
plt.savefig("figs/line_citeseer.png", bbox_inches="tight")
plt.show()
print("Graph embedding via SDNE...........")
model = eg.SDNE(
g,
node_size=len(g.nodes),
nhid0=256,
nhid1=32,
dropout=0.025,
alpha=5e-4,
beta=10,
)
sdne_emb = model.train(model)
sd_emb = []
for i in range(0, len(sdne_emb)):
sd_emb.append(list(sdne_emb[i]))
# print(len(sd_emb))
sd_emb = np.array(sd_emb)
print(sd_emb)
tsne = TSNE(n_components=2, verbose=1, random_state=0)
z = tsne.fit_transform(sd_emb)
z_data = np.vstack((z.T, labels)).T
df_tsne = pd.DataFrame(z_data, columns=["Dim1", "Dim2", "class"])
df_tsne["class"] = df_tsne["class"].astype(int)
plt.figure(figsize=(8, 8))
sns.scatterplot(
data=df_tsne,
hue="class",
x="Dim1",
y="Dim2",
palette=["green", "orange", "brown", "red", "blue", "black"],
)
plt.savefig("figs/sdne_citeseer2.pdf", bbox_inches="tight")
plt.savefig("figs/sdne_citeseer2.png", bbox_inches="tight")
plt.show()
@@ -0,0 +1,308 @@
import random
import numpy as np
from easygraph.utils import *
from tqdm import tqdm
__all__ = ["node2vec"]
@not_implemented_for("multigraph")
def node2vec(
G,
dimensions=128,
walk_length=80,
num_walks=10,
p=1.0,
q=1.0,
weight_key=None,
workers=None,
**skip_gram_params,
):
"""Graph embedding via Node2Vec.
Parameters
----------
G : easygraph.Graph or easygraph.DiGraph
dimensions : int
Embedding dimensions, optional(default: 128)
walk_length : int
Number of nodes in each walk, optional(default: 80)
num_walks : int
Number of walks per node, optional(default: 10)
p : float
The return hyper parameter, optional(default: 1.0)
q : float
The input parameter, optional(default: 1.0)
weight_key : string or None (default: None)
On weighted graphs, this is the key for the weight attribute
workers : int or None, optional(default : None)
The number of workers generating random walks (default: None). None if not using only one worker.
skip_gram_params : dict
Parameters for gensim.models.Word2Vec - do not supply 'size', it is taken from the 'dimensions' parameter
Returns
-------
embedding_vector : dict
The embedding vector of each node
most_similar_nodes_of_node : dict
The most similar nodes of each node and its similarity
Examples
--------
>>> node2vec(G,
... dimensions=128, # The graph embedding dimensions.
... walk_length=80, # Walk length of each random walks.
... num_walks=10, # Number of random walks.
... p=1.0, # The `p` possibility in random walk in [1]_
... q=1.0, # The `q` possibility in random walk in [1]_
... weight_key='weight',
... skip_gram_params=dict( # The skip_gram parameters in Python package gensim.
... window=10,
... min_count=1,
... batch_words=4
... ))
References
----------
.. [1] https://arxiv.org/abs/1607.00653
"""
G_index, index_of_node, node_of_index = G.to_index_node_graph()
if workers is None:
walks = simulate_walks(
G_index,
walk_length=walk_length,
num_walks=num_walks,
p=p,
q=q,
weight_key=weight_key,
)
else:
from joblib import Parallel
from joblib import delayed
num_walks_lists = np.array_split(range(num_walks), workers)
walks = Parallel(n_jobs=workers)(
delayed(simulate_walks)(
G_index, walk_length, len(num_walks), p, q, weight_key
)
for num_walks in num_walks_lists
)
# Change multidimensional array to one dimensional array
walks = [walk for walk_group in walks for walk in walk_group]
model = learn_embeddings(walks=walks, dimensions=dimensions, **skip_gram_params)
(
embedding_vector,
most_similar_nodes_of_node,
) = _get_embedding_result_from_gensim_skipgram_model(
G=G, index_of_node=index_of_node, node_of_index=node_of_index, model=model
)
del G_index
return embedding_vector, most_similar_nodes_of_node
def _get_embedding_result_from_gensim_skipgram_model(
G, index_of_node, node_of_index, model
):
embedding_vector = dict()
most_similar_nodes_of_node = dict()
def change_string_to_node_from_gensim_return_value(value_including_str):
# As the return value of gensim model.wv.most_similar includes string index in G_index,
# the string index should be changed to the original node element in G.
result = []
for node_index, value in value_including_str:
node_index = int(node_index)
node = node_of_index[node_index]
result.append((node, value))
return result
for node in G.nodes:
# Output node names are always strings in gensim
embedding_vector[node] = model.wv[str(index_of_node[node])]
most_similar_nodes = model.wv.most_similar(str(index_of_node[node]))
most_similar_nodes_of_node[
node
] = change_string_to_node_from_gensim_return_value(most_similar_nodes)
return embedding_vector, most_similar_nodes_of_node
def simulate_walks(G, walk_length, num_walks, p, q, weight_key=None):
alias_nodes, alias_edges = _preprocess_transition_probs(G, p, q, weight_key)
walks = []
nodes = list(G.nodes)
for walk_iter in tqdm(range(num_walks)):
random.shuffle(nodes)
for node in nodes:
walks.append(
_node2vec_walk(
G,
walk_length=walk_length,
start_node=node,
alias_nodes=alias_nodes,
alias_edges=alias_edges,
)
)
return walks
def _preprocess_transition_probs(G, p, q, weight_key=None):
is_directed = G.is_directed()
alias_nodes = {}
for node in G.nodes:
if weight_key is None:
unnormalized_probs = [1.0 for nbr in sorted(G.neighbors(node))]
else:
unnormalized_probs = [
G[node][nbr][weight_key] for nbr in sorted(G.neighbors(node))
]
norm_const = sum(unnormalized_probs)
normalized_probs = [float(u_prob) / norm_const for u_prob in unnormalized_probs]
alias_nodes[node] = _alias_setup(normalized_probs)
alias_edges = {}
triads = {}
if is_directed:
for edge in G.edges:
alias_edges[(edge[0], edge[1])] = _get_alias_edge(
G, edge[0], edge[1], p, q, weight_key
)
else:
for edge in G.edges:
alias_edges[(edge[0], edge[1])] = _get_alias_edge(
G, edge[0], edge[1], p, q, weight_key
)
alias_edges[(edge[1], edge[0])] = _get_alias_edge(
G, edge[1], edge[0], p, q, weight_key
)
return alias_nodes, alias_edges
def _get_alias_edge(G, src, dst, p, q, weight_key=None):
unnormalized_probs = []
if weight_key is None:
for dst_nbr in sorted(G.neighbors(dst)):
if dst_nbr == src:
unnormalized_probs.append(1.0 / p)
elif G.has_edge(dst_nbr, src):
unnormalized_probs.append(1.0)
else:
unnormalized_probs.append(1.0 / q)
else:
for dst_nbr in sorted(G.neighbors(dst)):
if dst_nbr == src:
unnormalized_probs.append(G[dst][dst_nbr][weight_key] / p)
elif G.has_edge(dst_nbr, src):
unnormalized_probs.append(G[dst][dst_nbr][weight_key])
else:
unnormalized_probs.append(G[dst][dst_nbr][weight_key] / q)
norm_const = sum(unnormalized_probs)
normalized_probs = [float(u_prob) / norm_const for u_prob in unnormalized_probs]
return _alias_setup(normalized_probs)
def _alias_setup(probs):
K = len(probs)
q = np.zeros(K)
J = np.zeros(K, dtype=int)
smaller = []
larger = []
for kk, prob in enumerate(probs):
q[kk] = K * prob
if q[kk] < 1.0:
smaller.append(kk)
else:
larger.append(kk)
while len(smaller) > 0 and len(larger) > 0:
small = smaller.pop()
large = larger.pop()
J[small] = large
q[large] = q[large] + q[small] - 1.0
if q[large] < 1.0:
smaller.append(large)
else:
larger.append(large)
return J, q
def _node2vec_walk(G, walk_length, start_node, alias_nodes, alias_edges):
"""
Simulate a random walk starting from start node.
"""
walk = [start_node]
while len(walk) < walk_length:
cur = walk[-1]
cur_nbrs = sorted(G.neighbors(cur))
if len(cur_nbrs) > 0:
if len(walk) == 1:
walk.append(
cur_nbrs[_alias_draw(alias_nodes[cur][0], alias_nodes[cur][1])]
)
else:
prev = walk[-2]
next_node = cur_nbrs[
_alias_draw(
alias_edges[(prev, cur)][0], alias_edges[(prev, cur)][1]
)
]
walk.append(next_node)
else:
break
return walk
def _alias_draw(J, q):
K = len(J)
kk = int(np.floor(np.random.rand() * K))
if np.random.rand() < q[kk]:
return kk
else:
return J[kk]
def learn_embeddings(walks, dimensions, **skip_gram_params):
"""
Learn embeddings with Word2Vec.
"""
from gensim.models import Word2Vec
walks = [list(map(str, walk)) for walk in walks]
if "vector_size" not in skip_gram_params:
skip_gram_params["vector_size"] = dimensions
model = Word2Vec(walks, **skip_gram_params)
return model
+280
View File
@@ -0,0 +1,280 @@
from argparse import ArgumentDefaultsHelpFormatter
from argparse import ArgumentParser
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.utils import data
from torch.utils.data.dataloader import DataLoader
def parse_args():
parser = ArgumentParser(
formatter_class=ArgumentDefaultsHelpFormatter, conflict_handler="resolve"
)
parser.add_argument(
"--output", default="node.emb", help="Output representation file"
)
parser.add_argument(
"--workers", default=8, type=int, help="Number of parallel processes."
)
parser.add_argument(
"--weighted", action="store_true", default=False, help="Treat graph as weighted"
)
parser.add_argument(
"--epochs", default=400, type=int, help="The training epochs of SDNE"
)
parser.add_argument(
"--dropout",
default=0.05,
type=float,
help="Dropout rate (1 - keep probability)",
)
parser.add_argument(
"--weight-decay",
type=float,
default=5e-4,
help="Weight for L2 loss on embedding matrix",
)
parser.add_argument("--lr", default=0.006, type=float, help="learning rate")
parser.add_argument(
"--alpha", default=1e-2, type=float, help="alhpa is a hyperparameter in SDNE"
)
parser.add_argument(
"--beta", default=5.0, type=float, help="beta is a hyperparameter in SDNE"
)
parser.add_argument(
"--nu1", default=1e-5, type=float, help="nu1 is a hyperparameter in SDNE"
)
parser.add_argument(
"--nu2", default=1e-4, type=float, help="nu2 is a hyperparameter in SDNE"
)
parser.add_argument("--bs", default=100, type=int, help="batch size of SDNE")
parser.add_argument("--nhid0", default=1000, type=int, help="The first dim")
parser.add_argument("--nhid1", default=128, type=int, help="The second dim")
parser.add_argument(
"--step_size", default=10, type=int, help="The step size for lr"
)
parser.add_argument("--gamma", default=0.9, type=int, help="The gamma for lr")
args = parser.parse_args()
return args
class Dataload(data.Dataset):
def __init__(self, Adj, Node):
self.Adj = Adj
self.Node = Node
def __getitem__(self, index):
return index
# adj_batch = self.Adj[index]
# adj_mat = adj_batch[index]
# b_mat = torch.ones_like(adj_batch)
# b_mat[adj_batch != 0] = self.Beta
# return adj_batch, adj_mat, b_mat
def __len__(self):
return self.Node
def get_adj(g):
edges = list(g.edges)
edges = [(edges[i][0], edges[i][1]) for i in range(len(edges))]
# print(edges)
edges = np.array([np.array(i) for i in edges])
min_node, max_node = edges.min(), edges.max()
if min_node == 0:
Node = max_node + 1
else:
Node = max_node
Adj = np.zeros([Node, Node], dtype=int)
for i in range(edges.shape[0]):
g.add_edge(edges[i][0], edges[i][1])
if min_node == 0:
Adj[edges[i][0], edges[i][1]] = 1
Adj[edges[i][1], edges[i][0]] = 1
else:
Adj[edges[i][0] - 1, edges[i][1] - 1] = 1
Adj[edges[i][1] - 1, edges[i][0] - 1] = 1
Adj = torch.FloatTensor(Adj)
return Adj, Node
class SDNE(nn.Module):
"""
Graph embedding via SDNE.
Parameters
----------
graph : easygraph.Graph or easygraph.DiGraph
node: Size of nodes
nhid0, nhid1: Two dimensions of two hiddenlayers, default: 128, 64
dropout: One parameter for regularization, default: 0.025
alpha, beta: Twe parameters
graph=g: : easygraph.Graph or easygraph.DiGraph
Examples
--------
>>> import easygraph as eg
>>> model = eg.SDNE(graph=g, node_size= len(g.nodes), nhid0=128, nhid1=64, dropout=0.025, alpha=2e-2, beta=10)
>>> emb = model.train(model, epochs, lr, bs, step_size, gamma, nu1, nu2, device, output)
epochs, "--epochs", default=400, type=int, help="The training epochs of SDNE"
alpha, "--alpha", default=2e-2, type=float, help="alhpa is a hyperparameter in SDNE"
beta, "--beta", default=10.0, type=float, help="beta is a hyperparameter in SDNE"
lr, "--lr", default=0.006, type=float, help="learning rate"
bs, "--bs", default=100, type=int, help="batch size of SDNE"
step_size, "--step_size", default=10, type=int, help="The step size for lr"
gamma, # "--gamma", default=0.9, type=int, help="The gamma for lr"
step_size, "--step_size", default=10, type=int, help="The step size for lr"
nu1, # "--nu1", default=1e-5, type=float, help="nu1 is a hyperparameter in SDNE"
nu2, "--nu2", default=1e-4, type=float, help="nu2 is a hyperparameter in SDNE"
device, "-- device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") "
output "--output", default="node.emb", help="Output representation file"
Reference
----------
.. [1] Wang, D., Cui, P., & Zhu, W. (2016, August). Structural deep network embedding. In Proceedings of the 22nd ACM SIGKDD international conference on Knowledge discovery and data mining (pp. 1225-1234).
https://www.kdd.org/kdd2016/papers/files/rfp0191-wangAemb.pdf
"""
def __init__(
self, graph, node_size, nhid0, nhid1, dropout=0.06, alpha=2e-2, beta=10.0
):
super(SDNE, self).__init__()
self.encode0 = nn.Linear(node_size, nhid0)
self.encode1 = nn.Linear(nhid0, nhid1)
self.decode0 = nn.Linear(nhid1, nhid0)
self.decode1 = nn.Linear(nhid0, node_size)
self.droput = dropout
self.alpha = alpha
self.beta = beta
self.graph = graph
def forward(self, adj_batch, adj_mat, b_mat):
t0 = F.leaky_relu(self.encode0(adj_batch))
t0 = F.leaky_relu(self.encode1(t0))
embedding = t0
t0 = F.leaky_relu(self.decode0(t0))
t0 = F.leaky_relu(self.decode1(t0))
embedding_norm = torch.sum(embedding * embedding, dim=1, keepdim=True)
L_1st = torch.sum(
adj_mat
* (
embedding_norm
- 2 * torch.mm(embedding, torch.transpose(embedding, dim0=0, dim1=1))
+ torch.transpose(embedding_norm, dim0=0, dim1=1)
)
)
L_2nd = torch.sum(((adj_batch - t0) * b_mat) * ((adj_batch - t0) * b_mat))
return L_1st, self.alpha * L_2nd, L_1st + self.alpha * L_2nd
def train(
self,
model,
epochs=100,
lr=0.006,
bs=100,
step_size=10,
gamma=0.9,
nu1=1e-5,
nu2=1e-4,
device="cpu",
output="out.emb",
):
Adj, Node = get_adj(self.graph)
model = model.to(device)
opt = optim.Adam(model.parameters(), lr=lr)
scheduler = torch.optim.lr_scheduler.StepLR(
opt, step_size=step_size, gamma=gamma
)
Data = Dataload(Adj, Node)
Data = DataLoader(
Data,
batch_size=bs,
shuffle=True,
)
for epoch in range(1, epochs + 1):
loss_sum, loss_L1, loss_L2, loss_reg = 0, 0, 0, 0
for index in Data:
adj_batch = Adj[index]
adj_mat = adj_batch[:, index]
b_mat = torch.ones_like(adj_batch)
b_mat[adj_batch != 0] = self.beta
opt.zero_grad()
L_1st, L_2nd, L_all = model(adj_batch, adj_mat, b_mat)
L_reg = 0
for param in model.parameters():
L_reg += nu1 * torch.sum(torch.abs(param)) + nu2 * torch.sum(
param * param
)
Loss = L_all + L_reg
Loss.backward()
opt.step()
loss_sum += Loss
loss_L1 += L_1st
loss_L2 += L_2nd
loss_reg += L_reg
scheduler.step(epoch)
# print("The lr for epoch %d is %f" %(epoch, scheduler.get_lr()[0]))
print("loss for epoch %d is:" % epoch)
print("loss_sum is %f" % loss_sum)
print("loss_L1 is %f" % loss_L1)
print("loss_L2 is %f" % loss_L2)
print("loss_reg is %f" % loss_reg)
# model.eval()
embedding = model.savector(Adj)
outVec = embedding.detach().numpy()
np.savetxt(output, outVec)
return outVec
def savector(self, adj):
t0 = self.encode0(adj)
t0 = self.encode1(t0)
return t0
# if __name__ == '__main__':
# args = parse_args()
# print(args)
# dataset = eg.CiteseerGraphDataset(force_reload=True) # Download CiteseerGraphDataset contained in EasyGraph
# num_classes = dataset.num_classes
# g = dataset[0]
# print(g)
# device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# adj, node = get_adj(g)
# # labels = g.ndata['label']
# nhid0, nhid1, dropout, alpha = args.nhid0, args.nhid1, args.dropout, args.alpha
# model = SDNE(node, nhid0, nhid1, dropout, alpha, graph=g)
# print(model)
#
# emb = model.train(args, device)
@@ -0,0 +1,4 @@
-1.765814423561096191e-01 2.083084881305694580e-01 -1.271556913852691650e-01 -1.702362895011901855e-01 8.119292855262756348e-01 -3.134809732437133789e-01 -9.992567449808120728e-02 -1.093881502747535706e-01
-2.064122706651687622e-01 -1.475724577903747559e-01 -1.439859867095947266e-01 -7.331190109252929688e-01 6.787545084953308105e-01 -3.651908636093139648e-01 -9.232180565595626831e-02 -8.407155275344848633e-01
-1.765814423561096191e-01 2.083084881305694580e-01 -1.271556913852691650e-01 -1.702362895011901855e-01 8.119292855262756348e-01 -3.134809732437133789e-01 -9.992567449808120728e-02 -1.093881502747535706e-01
-2.064122706651687622e-01 -1.475724577903747559e-01 -1.439859867095947266e-01 -7.331190109252929688e-01 6.787545084953308105e-01 -3.651908636093139648e-01 -9.232180565595626831e-02 -8.407155275344848633e-01
@@ -0,0 +1,101 @@
import unittest
import easygraph as eg
import numpy as np
class Test_Deepwalk(unittest.TestCase):
def setUp(self):
self.ds = eg.datasets.get_graph_karateclub()
self.edges = [(1, 4), (2, 4)]
self.test_graphs = []
self.test_graphs.append(eg.classes.DiGraph(self.edges))
self.shs = eg.common_greedy(self.ds, int(len(self.ds.nodes) / 3))
self.graph = eg.Graph()
self.graph.add_edges_from([(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)])
self.empty_graph = eg.Graph()
self.single_node_graph = eg.Graph()
self.single_node_graph.add_node(0)
def test_deepwalk(self):
for i in self.test_graphs:
print(eg.deepwalk(i))
def test_deepwalk_output_structure(self):
emb, sim = eg.deepwalk(
self.graph,
dimensions=16,
walk_length=5,
num_walks=3,
window=2,
min_count=1,
batch_words=4,
epochs=5,
)
self.assertIsInstance(emb, dict)
self.assertIsInstance(sim, dict)
for k, v in emb.items():
self.assertEqual(len(v), 16)
self.assertTrue(isinstance(v, np.ndarray))
def test_deepwalk_similarity_keys_match_nodes(self):
emb, sim = eg.deepwalk(
self.graph,
dimensions=8,
walk_length=3,
num_walks=2,
window=2,
min_count=1,
batch_words=2,
epochs=3,
)
self.assertEqual(set(emb.keys()), set(sim.keys()))
self.assertEqual(set(emb.keys()), set(self.graph.nodes))
def test_deepwalk_on_single_node(self):
emb, sim = eg.deepwalk(
self.single_node_graph,
dimensions=4,
walk_length=2,
num_walks=1,
window=1,
min_count=1,
batch_words=2,
epochs=2,
)
self.assertEqual(len(emb), 1)
self.assertEqual(list(emb.keys()), [0])
self.assertEqual(len(emb[0]), 4)
def test_deepwalk_on_empty_graph(self):
with self.assertRaises(RuntimeError):
eg.deepwalk(
self.empty_graph,
dimensions=4,
walk_length=2,
num_walks=1,
window=1,
min_count=1,
batch_words=2,
epochs=2,
)
def test_deepwalk_walk_length_zero(self):
emb, sim = eg.deepwalk(
self.graph,
dimensions=4,
walk_length=0,
num_walks=2,
window=1,
min_count=1,
batch_words=2,
epochs=2,
)
self.assertEqual(len(emb), len(self.graph.nodes))
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,77 @@
import unittest
import easygraph as eg
import numpy as np
class Test_LINE(unittest.TestCase):
def setUp(self):
self.edges = [(0, 1), (1, 2), (2, 3), (3, 4)]
self.graph = eg.Graph()
self.graph.add_edges_from(self.edges)
def test_output_is_dict_with_correct_dim(self):
model = eg.functions.graph_embedding.LINE(
dimension=16, walk_length=10, walk_num=5, order=1
)
emb = model(self.graph, return_dict=True)
self.assertIsInstance(emb, dict)
for v in emb.values():
self.assertEqual(len(v), 16)
def test_output_as_matrix(self):
model = eg.functions.graph_embedding.LINE(
dimension=8, walk_length=5, walk_num=3, order=1
)
emb = model(self.graph, return_dict=False)
self.assertEqual(emb.shape, (len(self.graph.nodes), 8))
def test_output_with_order_2(self):
model = eg.functions.graph_embedding.LINE(
dimension=16, walk_length=10, walk_num=5, order=2
)
emb = model(self.graph)
for vec in emb.values():
self.assertEqual(len(vec), 16)
def test_output_with_order_3_combination(self):
model = eg.functions.graph_embedding.LINE(
dimension=16, walk_length=10, walk_num=5, order=3
)
emb = model(self.graph)
for vec in emb.values():
self.assertEqual(len(vec), 16)
def test_directed_graph(self):
g = eg.DiGraph()
g.add_edges_from(self.edges)
model = eg.functions.graph_embedding.LINE(
dimension=8, walk_length=5, walk_num=3, order=1
)
emb = model(g)
self.assertEqual(len(emb), len(g.nodes))
def test_empty_graph_raises(self):
g = eg.Graph()
model = eg.functions.graph_embedding.LINE(
dimension=8, walk_length=5, walk_num=3, order=1
)
with self.assertRaises(Exception):
_ = model(g)
def test_embeddings_are_normalized(self):
model = eg.functions.graph_embedding.LINE(
dimension=16, walk_length=10, walk_num=5, order=1
)
emb = model(self.graph)
for vec in emb.values():
norm = np.linalg.norm(vec)
self.assertTrue(np.isclose(norm, 1.0, atol=1e-5))
def test_embedding_value_finiteness(self):
model = eg.functions.graph_embedding.LINE(
dimension=16, walk_length=10, walk_num=5, order=1
)
emb = model(self.graph)
for vec in emb.values():
self.assertTrue(np.all(np.isfinite(vec)))
@@ -0,0 +1,57 @@
import unittest
import easygraph as eg
import easygraph.functions.graph_embedding as fn
import numpy as np
class Test_Nobe(unittest.TestCase):
def setUp(self):
self.ds = eg.datasets.get_graph_karateclub()
self.edges = [(1, 4), (2, 4), (4, 1), (0, 4), (4, 3)]
self.test_directed_graphs = [eg.DiGraph()]
self.test_undirected_graphs = [eg.Graph(self.edges)]
self.test_directed_graphs.append(eg.classes.DiGraph(self.edges))
self.shs = eg.common_greedy(self.ds, int(len(self.ds.nodes) / 3))
self.valid_graph = eg.Graph([(0, 1), (1, 2), (2, 0), (2, 3), (3, 4)])
self.directed_graph = eg.DiGraph([(0, 1), (1, 2)])
self.graph_with_isolated = eg.Graph()
self.graph_with_isolated.add_edges_from([(0, 1), (1, 2)])
self.graph_with_isolated.add_node(3)
self.graph_with_isolated.add_node(4)
def test_NOBE(self):
fn.NOBE(self.test_undirected_graphs[0], 1)
def test_NOBE_GA(self):
"""
for i in self.test_graphs:
eg.functions.NOBE_GA(i, K=1)
print(i)
"""
fn.NOBE_GA(self.test_directed_graphs[1], 1)
def test_nobe_output_shape(self):
emb = fn.NOBE(self.valid_graph, K=2)
self.assertIsInstance(emb, np.ndarray)
self.assertEqual(emb.shape[1], 2)
def test_nobe_ga_output_shape(self):
undirected_graph = eg.Graph([(0, 1), (1, 2), (2, 3)])
emb = fn.NOBE_GA(undirected_graph, K=2)
self.assertIsInstance(emb, np.ndarray)
self.assertEqual(emb.shape[1], 2)
def test_nobe_on_graph_with_isolated_nodes(self):
emb = fn.NOBE(self.graph_with_isolated, K=2)
self.assertEqual(emb.shape[0], len(self.graph_with_isolated))
def test_nobe_invalid_K_zero(self):
emb = fn.NOBE(self.valid_graph, 0)
self.assertIsInstance(emb, np.ndarray)
self.assertEqual(emb.shape, (len(self.valid_graph), 0))
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,58 @@
import unittest
import easygraph as eg
import numpy as np
from easygraph.functions.graph_embedding.NOBE import NOBE
from easygraph.functions.graph_embedding.NOBE import NOBE_GA
class Test_Nobe(unittest.TestCase):
def setUp(self):
self.ds = eg.datasets.get_graph_karateclub()
self.edges = [(1, 4), (2, 4), (4, 1), (0, 4)]
self.test_graphs = [eg.classes.DiGraph(self.edges)]
self.test_undirected_graphs = [eg.classes.Graph(self.edges)]
self.shs = eg.common_greedy(self.ds, int(len(self.ds.nodes) / 3))
self.valid_graph = eg.Graph([(0, 1), (1, 2), (2, 3)])
self.directed_graph = eg.DiGraph([(0, 1), (1, 2)])
self.graph_with_isolated = eg.Graph([(0, 1), (1, 2)])
self.graph_with_isolated.add_node(5) # isolated node
#
def test_NOBE(self):
for i in self.test_graphs:
NOBE(i, K=1)
def test_NOBE_GA(self):
for i in self.test_undirected_graphs:
NOBE_GA(i, K=1)
def test_nobe_embedding_shape(self):
emb = NOBE(self.valid_graph, K=2)
self.assertIsInstance(emb, np.ndarray)
self.assertEqual(emb.shape, (len(self.valid_graph.nodes), 2))
def test_nobe_ga_embedding_shape(self):
emb = NOBE_GA(self.valid_graph, K=2)
self.assertIsInstance(emb, np.ndarray)
self.assertEqual(emb.shape, (len(self.valid_graph.nodes), 2))
def test_nobe_invalid_k_zero(self):
emb = NOBE(self.valid_graph, 0)
self.assertIsInstance(emb, np.ndarray)
self.assertEqual(emb.shape, (len(self.valid_graph), 0))
def test_nobe_ga_invalid_k_zero(self):
emb = NOBE_GA(self.valid_graph, 0)
self.assertIsInstance(emb, np.ndarray)
self.assertEqual(emb.shape, (len(self.valid_graph), 0))
def test_nobe_with_isolated_node(self):
emb = NOBE(self.graph_with_isolated, K=2)
self.assertEqual(emb.shape[0], len(self.graph_with_isolated))
# if __name__ == "__main__":
# unittest.main()
@@ -0,0 +1,107 @@
import unittest
import easygraph as eg
import numpy as np
import torch
class Test_Sdne(unittest.TestCase):
def setUp(self):
self.ds = eg.datasets.get_graph_karateclub()
self.edges = [
(1, 4),
(2, 4),
(4, 1),
(0, 4),
(4, 3),
]
self.test_graphs = []
self.test_graphs.append(eg.classes.DiGraph(self.edges))
self.shs = eg.common_greedy(self.ds, int(len(self.ds.nodes) / 3))
self.graph = eg.DiGraph()
self.graph.add_edges_from([(0, 1), (1, 2), (2, 3), (3, 0)])
self.device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
def test_sdne(self):
sdne = eg.SDNE(
graph=self.test_graphs[0],
node_size=len(self.test_graphs[0].nodes),
nhid0=128,
nhid1=64,
dropout=0.025,
alpha=2e-2,
beta=10,
)
# todo add test
# emb = sdne.train(sdne)
def test_sdne_model_instantiation(self):
model = eg.SDNE(
graph=self.graph,
node_size=len(self.graph.nodes),
nhid0=32,
nhid1=16,
dropout=0.05,
alpha=0.01,
beta=5.0,
)
self.assertIsInstance(model, eg.SDNE)
def test_sdne_training_embedding_output(self):
model = eg.SDNE(
graph=self.graph,
node_size=len(self.graph.nodes),
nhid0=16,
nhid1=8,
dropout=0.05,
alpha=0.01,
beta=5.0,
)
embedding = model.train(
model=model,
epochs=5,
lr=0.01,
bs=2,
step_size=2,
gamma=0.9,
nu1=1e-5,
nu2=1e-4,
device=self.device,
output="test.emb",
)
self.assertIsInstance(embedding, np.ndarray)
self.assertEqual(embedding.shape, (len(self.graph.nodes), 8))
def test_savector_output_shape(self):
adj, _ = eg.get_adj(self.graph)
model = eg.SDNE(
graph=self.graph,
node_size=len(self.graph.nodes),
nhid0=16,
nhid1=8,
dropout=0.05,
alpha=0.01,
beta=5.0,
)
with torch.no_grad():
emb = model.savector(adj)
self.assertEqual(emb.shape, (len(self.graph.nodes), 8))
def test_get_adj_shape_and_symmetry(self):
adj, node_count = eg.get_adj(self.graph)
self.assertEqual(adj.shape[0], node_count)
self.assertTrue(torch.equal(adj, adj.T)) # check symmetry for undirected
def test_training_on_empty_graph(self):
empty_graph = eg.Graph()
model = eg.SDNE(
graph=empty_graph,
node_size=0,
nhid0=8,
nhid1=4,
dropout=0.05,
alpha=0.01,
beta=5.0,
)
with self.assertRaises(ValueError):
model.train(model=model, epochs=5, device=self.device)