2. correct an error in the common walk kernel. DON NOT use the old one. 3. improve the method to construct fully-labeled direct product graphs, much faster for sparse graphs.tags/v0.1
| @@ -0,0 +1 @@ | |||
| ljia@ljia-Precision-7520.4716:1530265749 | |||
| @@ -24,7 +24,7 @@ def commonwalkkernel(*args, | |||
| edge_label='bond_type', | |||
| n=None, | |||
| weight=1, | |||
| compute_method='exp'): | |||
| compute_method=None): | |||
| """Calculate common walk graph kernels up to depth d between graphs. | |||
| Parameters | |||
| ---------- | |||
| @@ -40,10 +40,11 @@ def commonwalkkernel(*args, | |||
| n : integer | |||
| Longest length of walks. | |||
| weight: integer | |||
| Weight coefficient of different lengths of walks. | |||
| Weight coefficient of different lengths of walks, which represents beta in 'exp' method and gamma in 'geo'. | |||
| compute_method : string | |||
| Method used to compute walk kernel. The Following choices are available: | |||
| 'direct' : direct product graph method, as shown in reference [1]. The time complexity is O(n^6) for unlabeled graphs with n vertices. | |||
| 'exp' : exponential serial method applied on the direct product graph, as shown in reference [1]. The time complexity is O(n^6) for graphs with n vertices. | |||
| 'geo' : geometric serial method applied on the direct product graph, as shown in reference [1]. The time complexity is O(n^6) for graphs with n vertices. | |||
| 'brute' : brute force, simply search for all walks and compare them. | |||
| Return | |||
| @@ -66,6 +67,8 @@ def commonwalkkernel(*args, | |||
| if not ds_attrs['edge_labeled']: | |||
| for G in Gn: | |||
| nx.set_edge_attributes(G, '0', 'bond_type') | |||
| if not ds_attrs['is_directed']: | |||
| Gn = [G.to_directed() for G in Gn] | |||
| start_time = time.time() | |||
| @@ -77,7 +80,7 @@ def commonwalkkernel(*args, | |||
| file=sys.stdout) | |||
| for i in range(0, len(Gn)): | |||
| for j in range(i, len(Gn)): | |||
| Kmatrix[i][j] = _untilnwalkkernel_exp(Gn[i], Gn[j], node_label, | |||
| Kmatrix[i][j] = _commonwalkkernel_exp(Gn[i], Gn[j], node_label, | |||
| edge_label, weight) | |||
| Kmatrix[j][i] = Kmatrix[i][j] | |||
| pbar.update(1) | |||
| @@ -90,7 +93,7 @@ def commonwalkkernel(*args, | |||
| file=sys.stdout) | |||
| for i in range(0, len(Gn)): | |||
| for j in range(i, len(Gn)): | |||
| Kmatrix[i][j] = _untilnwalkkernel_geo(Gn[i], Gn[j], node_label, | |||
| Kmatrix[i][j] = _commonwalkkernel_geo(Gn[i], Gn[j], node_label, | |||
| edge_label, weight) | |||
| Kmatrix[j][i] = Kmatrix[i][j] | |||
| pbar.update(1) | |||
| @@ -106,7 +109,7 @@ def commonwalkkernel(*args, | |||
| for i in range(0, len(Gn)): | |||
| for j in range(i, len(Gn)): | |||
| Kmatrix[i][j] = _untilnwalkkernel_brute( | |||
| Kmatrix[i][j] = _commonwalkkernel_brute( | |||
| all_walks[i], | |||
| all_walks[j], | |||
| node_label=node_label, | |||
| @@ -122,7 +125,7 @@ def commonwalkkernel(*args, | |||
| return Kmatrix, run_time | |||
| def _untilnwalkkernel_exp(G1, G2, node_label, edge_label, beta): | |||
| def _commonwalkkernel_exp(G1, G2, node_label, edge_label, beta): | |||
| """Calculate walk graph kernels up to n between 2 graphs using exponential series. | |||
| Parameters | |||
| @@ -168,7 +171,7 @@ def _untilnwalkkernel_exp(G1, G2, node_label, edge_label, beta): | |||
| D = np.zeros((len(ew), len(ew))) | |||
| for i in range(len(ew)): | |||
| D[i][i] = np.exp(beta * ew[i]) | |||
| # print('D: ', D) | |||
| # print('D: ', D) | |||
| # print('hshs: ', T.I * D * T) | |||
| # print(np.exp(-2)) | |||
| @@ -176,16 +179,16 @@ def _untilnwalkkernel_exp(G1, G2, node_label, edge_label, beta): | |||
| # print(np.exp(weight * D)) | |||
| # print(ev) | |||
| # print(np.linalg.inv(ev)) | |||
| exp_D = ev * D * ev.I | |||
| exp_D = ev * D * ev.T | |||
| # print(exp_D) | |||
| # print(np.exp(weight * A)) | |||
| # print('-------') | |||
| return np.sum(exp_D.diagonal()) | |||
| return exp_D.sum() | |||
| def _untilnwalkkernel_geo(G1, G2, node_label, edge_label, gamma): | |||
| """Calculate walk graph kernels up to n between 2 graphs using geometric series. | |||
| def _commonwalkkernel_geo(G1, G2, node_label, edge_label, gamma): | |||
| """Calculate common walk graph kernels up to n between 2 graphs using geometric series. | |||
| Parameters | |||
| ---------- | |||
| @@ -207,46 +210,14 @@ def _untilnwalkkernel_geo(G1, G2, node_label, edge_label, gamma): | |||
| # get tensor product / direct product | |||
| gp = direct_product(G1, G2, node_label, edge_label) | |||
| A = nx.adjacency_matrix(gp).todense() | |||
| # print(A) | |||
| # from matplotlib import pyplot as plt | |||
| # nx.draw_networkx(G1) | |||
| # plt.show() | |||
| # nx.draw_networkx(G2) | |||
| # plt.show() | |||
| # nx.draw_networkx(gp) | |||
| # plt.show() | |||
| # print(G1.nodes(data=True)) | |||
| # print(G2.nodes(data=True)) | |||
| # print(gp.nodes(data=True)) | |||
| # print(gp.edges(data=True)) | |||
| ew, ev = np.linalg.eig(A) | |||
| # print('ew: ', ew) | |||
| # print(ev) | |||
| # T = np.matrix(ev) | |||
| # print('T: ', T) | |||
| # T = ev.I | |||
| D = np.zeros((len(ew), len(ew))) | |||
| for i in range(len(ew)): | |||
| D[i][i] = np.exp(beta * ew[i]) | |||
| # print('D: ', D) | |||
| # print('hshs: ', T.I * D * T) | |||
| # print(np.exp(-2)) | |||
| # print(D) | |||
| # print(np.exp(weight * D)) | |||
| # print(ev) | |||
| # print(np.linalg.inv(ev)) | |||
| exp_D = ev * D * ev.I | |||
| # print(exp_D) | |||
| # print(np.exp(weight * A)) | |||
| # print('-------') | |||
| return np.sum(exp_D.diagonal()) | |||
| mat = np.identity(len(A)) - gamma * A | |||
| try: | |||
| return mat.I.sum() | |||
| except np.linalg.LinAlgError: | |||
| return np.nan | |||
| def _untilnwalkkernel_brute(walks1, | |||
| def _commonwalkkernel_brute(walks1, | |||
| walks2, | |||
| node_label='atom', | |||
| edge_label='bond_type', | |||
| @@ -19,7 +19,11 @@ from pygraph.utils.graphdataset import get_dataset_attributes | |||
| def randomwalkkernel(*args, | |||
| node_label='atom', | |||
| edge_label='bond_type', | |||
| edge_weight=None, | |||
| h=10, | |||
| p=None, | |||
| q=None, | |||
| weight=None, | |||
| compute_method=''): | |||
| """Calculate random walk graph kernels. | |||
| Parameters | |||
| @@ -33,7 +37,7 @@ def randomwalkkernel(*args, | |||
| node attribute used as label. The default node label is atom. | |||
| edge_label : string | |||
| edge attribute used as label. The default edge label is bond_type. | |||
| n : integer | |||
| h : integer | |||
| Longest length of walks. | |||
| method : string | |||
| Method used to compute the random walk kernel. Available methods are 'sylvester', 'conjugate', 'fp', 'spectral' and 'kron'. | |||
| @@ -46,7 +50,25 @@ def randomwalkkernel(*args, | |||
| compute_method = compute_method.lower() | |||
| h = int(h) | |||
| Gn = args[0] if len(args) == 1 else [args[0], args[1]] | |||
| Kmatrix = np.zeros((len(Gn), len(Gn))) | |||
| eweight = None | |||
| if edge_weight == None: | |||
| print('\n None edge weight specified. Set all weight to 1.\n') | |||
| else: | |||
| try: | |||
| some_weight = list( | |||
| nx.get_edge_attributes(Gn[0], edge_weight).values())[0] | |||
| if isinstance(some_weight, float) or isinstance(some_weight, int): | |||
| eweight = edge_weight | |||
| else: | |||
| print( | |||
| '\n Edge weight with name %s is not float or integer. Set all weight to 1.\n' | |||
| % edge_weight) | |||
| except: | |||
| print( | |||
| '\n Edge weight with name "%s" is not found in the edge attributes. Set all weight to 1.\n' | |||
| % edge_weight) | |||
| ds_attrs = get_dataset_attributes( | |||
| Gn, | |||
| attr_names=['node_labeled', 'edge_labeled', 'is_directed'], | |||
| @@ -71,76 +93,224 @@ def randomwalkkernel(*args, | |||
| # labeled=labeled) for i in range(0, len(Gn)) | |||
| # ] | |||
| pbar = tqdm( | |||
| total=(1 + len(Gn)) * len(Gn) / 2, | |||
| desc='calculating kernels', | |||
| file=sys.stdout) | |||
| if compute_method == 'sylvester': | |||
| import warnings | |||
| warnings.warn( | |||
| 'The Sylvester equation (rather than generalized Sylvester equation) is used; only walks of length 1 is considered.' | |||
| 'The Sylvester equation (rather than generalized Sylvester equation) is used; edge label number has to smaller than 3.' | |||
| ) | |||
| from control import dlyap | |||
| Kmatrix = _randomwalkkernel_sylvester(Gn, weight, p, q, node_label, | |||
| edge_label, eweight) | |||
| elif compute_method == 'conjugate': | |||
| for i in range(0, len(Gn)): | |||
| for j in range(i, len(Gn)): | |||
| Kmatrix[i][j] = _randomwalkkernel_sylvester( | |||
| all_walks[i], | |||
| all_walks[j], | |||
| node_label=node_label, | |||
| edge_label=edge_label) | |||
| Kmatrix[i][j] = _randomwalkkernel_conjugate( | |||
| Gn[i], Gn[j], node_label, edge_label) | |||
| Kmatrix[j][i] = Kmatrix[i][j] | |||
| pbar.update(1) | |||
| elif compute_method == 'conjugate': | |||
| pass | |||
| elif compute_method == 'fp': | |||
| pass | |||
| for i in range(0, len(Gn)): | |||
| for j in range(i, len(Gn)): | |||
| Kmatrix[i][j] = _randomwalkkernel_fp(Gn[i], Gn[j], node_label, | |||
| edge_label) | |||
| Kmatrix[j][i] = Kmatrix[i][j] | |||
| pbar.update(1) | |||
| elif compute_method == 'spectral': | |||
| pass | |||
| for i in range(0, len(Gn)): | |||
| for j in range(i, len(Gn)): | |||
| Kmatrix[i][j] = _randomwalkkernel_spectral( | |||
| Gn[i], Gn[j], node_label, edge_label) | |||
| Kmatrix[j][i] = Kmatrix[i][j] | |||
| pbar.update(1) | |||
| elif compute_method == 'kron': | |||
| pass | |||
| for i in range(0, len(Gn)): | |||
| for j in range(i, len(Gn)): | |||
| Kmatrix[i][j] = _randomwalkkernel_kron(Gn[i], Gn[j], | |||
| node_label, edge_label) | |||
| Kmatrix[j][i] = Kmatrix[i][j] | |||
| pbar.update(1) | |||
| else: | |||
| raise Exception( | |||
| 'compute method name incorrect. Available methods: "sylvester", "conjugate", "fp", "spectral" and "kron".' | |||
| ) | |||
| for i in range(0, len(Gn)): | |||
| for j in range(i, len(Gn)): | |||
| Kmatrix[i][j] = _randomwalkkernel_do( | |||
| all_walks[i], | |||
| all_walks[j], | |||
| node_label=node_label, | |||
| edge_label=edge_label, | |||
| labeled=labeled) | |||
| Kmatrix[j][i] = Kmatrix[i][j] | |||
| # for i in range(0, len(Gn)): | |||
| # for j in range(i, len(Gn)): | |||
| # Kmatrix[i][j] = _randomwalkkernel_do( | |||
| # all_walks[i], | |||
| # all_walks[j], | |||
| # node_label=node_label, | |||
| # edge_label=edge_label, | |||
| # labeled=labeled) | |||
| # Kmatrix[j][i] = Kmatrix[i][j] | |||
| run_time = time.time() - start_time | |||
| print( | |||
| "\n --- kernel matrix of walk kernel up to %d of size %d built in %s seconds ---" | |||
| % (n, len(Gn), run_time)) | |||
| "\n --- kernel matrix of random walk kernel of size %d built in %s seconds ---" | |||
| % (len(Gn), run_time)) | |||
| return Kmatrix, run_time | |||
| def _randomwalkkernel_sylvester(walks1, | |||
| walks2, | |||
| node_label='atom', | |||
| edge_label='bond_type'): | |||
| def _randomwalkkernel_sylvester(Gn, lmda, p, q, node_label, edge_label, | |||
| eweight): | |||
| """Calculate walk graph kernels up to n between 2 graphs using Sylvester method. | |||
| Parameters | |||
| ---------- | |||
| walks1, walks2 : list | |||
| List of walks in 2 graphs, where for unlabeled graphs, each walk is represented by a list of nodes; while for labeled graphs, each walk is represented by a string consists of labels of nodes and edges on that walk. | |||
| G1, G2 : NetworkX graph | |||
| Graphs between which the kernel is calculated. | |||
| node_label : string | |||
| node attribute used as label. The default node label is atom. | |||
| node attribute used as label. | |||
| edge_label : string | |||
| edge attribute used as label. The default edge label is bond_type. | |||
| edge attribute used as label. | |||
| Return | |||
| ------ | |||
| kernel : float | |||
| Kernel between 2 graphs. | |||
| """ | |||
| from control import dlyap | |||
| Kmatrix = np.zeros((len(Gn), len(Gn))) | |||
| if q == None: | |||
| # don't normalize adjacency matrices if q is a uniform vector. | |||
| A_list = [ | |||
| nx.adjacency_matrix(G, eweight).todense() for G in tqdm( | |||
| Gn, desc='compute adjacency matrices', file=sys.stdout) | |||
| ] | |||
| if p == None: | |||
| pbar = tqdm( | |||
| total=(1 + len(Gn)) * len(Gn) / 2, | |||
| desc='calculating kernels', | |||
| file=sys.stdout) | |||
| for i in range(0, len(Gn)): | |||
| for j in range(i, len(Gn)): | |||
| A = lmda * A_list[j] | |||
| Q = A_list[i] | |||
| # use uniform distribution if there is no prior knowledge. | |||
| nb_pd = len(A_list[i]) * len(A_list[j]) | |||
| pd_uni = 1 / nb_pd | |||
| C = np.full((len(A_list[j]), len(A_list[i])), pd_uni) | |||
| try: | |||
| X = dlyap(A, Q, C) | |||
| X = np.reshape(X, (-1, 1), order='F') | |||
| # use uniform distribution if there is no prior knowledge. | |||
| q_direct = np.full((1, nb_pd), pd_uni) | |||
| Kmatrix[i][j] = np.dot(q_direct, X) | |||
| except TypeError: | |||
| # print('sth wrong.') | |||
| Kmatrix[i][j] = np.nan | |||
| Kmatrix[j][i] = Kmatrix[i][j] | |||
| pbar.update(1) | |||
| # A_list = [] | |||
| # for G in tqdm(Gn, desc='compute adjacency matrices', file=sys.stdout): | |||
| # A_tilde = nx.adjacency_matrix(G, weight=None).todense() | |||
| # # normalized adjacency matrices | |||
| # # A_list.append(A_tilde / A_tilde.sum(axis=0)) | |||
| # A_list.append(A_tilde) | |||
| return Kmatrix | |||
| def _randomwalkkernel_conjugate(G1, G2, node_label, edge_label): | |||
| """Calculate walk graph kernels up to n between 2 graphs using conjugate method. | |||
| Parameters | |||
| ---------- | |||
| G1, G2 : NetworkX graph | |||
| Graphs between which the kernel is calculated. | |||
| node_label : string | |||
| node attribute used as label. | |||
| edge_label : string | |||
| edge attribute used as label. | |||
| Return | |||
| ------ | |||
| kernel : float | |||
| Kernel between 2 graphs. | |||
| """ | |||
| dpg = nx.tensor_product(G1, G2) # direct product graph | |||
| import matplotlib.pyplot as plt | |||
| nx.draw_networkx(G1) | |||
| plt.show() | |||
| nx.draw_networkx(G2) | |||
| plt.show() | |||
| nx.draw_networkx(dpg) | |||
| plt.show() | |||
| X = dlyap(A, Q, C) | |||
| return kernel | |||
| def _randomwalkkernel_fp(G1, G2, node_label, edge_label): | |||
| """Calculate walk graph kernels up to n between 2 graphs using Fixed-Point method. | |||
| Parameters | |||
| ---------- | |||
| G1, G2 : NetworkX graph | |||
| Graphs between which the kernel is calculated. | |||
| node_label : string | |||
| node attribute used as label. | |||
| edge_label : string | |||
| edge attribute used as label. | |||
| Return | |||
| ------ | |||
| kernel : float | |||
| Kernel between 2 graphs. | |||
| """ | |||
| dpg = nx.tensor_product(G1, G2) # direct product graph | |||
| X = dlyap(A, Q, C) | |||
| return kernel | |||
| def _randomwalkkernel_spectral(G1, G2, node_label, edge_label): | |||
| """Calculate walk graph kernels up to n between 2 graphs using spectral decomposition method. | |||
| Parameters | |||
| ---------- | |||
| G1, G2 : NetworkX graph | |||
| Graphs between which the kernel is calculated. | |||
| node_label : string | |||
| node attribute used as label. | |||
| edge_label : string | |||
| edge attribute used as label. | |||
| Return | |||
| ------ | |||
| kernel : float | |||
| Kernel between 2 graphs. | |||
| """ | |||
| dpg = nx.tensor_product(G1, G2) # direct product graph | |||
| X = dlyap(A, Q, C) | |||
| return kernel | |||
| def _randomwalkkernel_kron(G1, G2, node_label, edge_label): | |||
| """Calculate walk graph kernels up to n between 2 graphs using nearest Kronecker product approximation method. | |||
| Parameters | |||
| ---------- | |||
| G1, G2 : NetworkX graph | |||
| Graphs between which the kernel is calculated. | |||
| node_label : string | |||
| node attribute used as label. | |||
| edge_label : string | |||
| edge attribute used as label. | |||
| Return | |||
| ------ | |||
| kernel : float | |||
| Treelet Kernel between 2 graphs. | |||
| Kernel between 2 graphs. | |||
| """ | |||
| dpg = nx.tensor_product(G1, G2) # direct product graph | |||
| @@ -8,6 +8,7 @@ import pathlib | |||
| sys.path.insert(0, "../") | |||
| from tqdm import tqdm | |||
| import time | |||
| from itertools import combinations_with_replacement, product | |||
| import networkx as nx | |||
| import numpy as np | |||
| @@ -39,8 +40,6 @@ def spkernel(*args, node_label='atom', edge_weight=None, node_kernels=None): | |||
| # pre-process | |||
| Gn = args[0] if len(args) == 1 else [args[0], args[1]] | |||
| Gn = [nx.to_directed(G) for G in Gn] | |||
| weight = None | |||
| if edge_weight == None: | |||
| print('\n None edge weight specified. Set all weight to 1.\n') | |||
| @@ -89,174 +88,158 @@ def spkernel(*args, node_label='atom', edge_weight=None, node_kernels=None): | |||
| # node symb and non-synb labeled | |||
| if ds_attrs['node_attr_dim'] > 0: | |||
| if ds_attrs['is_directed']: | |||
| for i in range(0, len(Gn)): | |||
| for j in range(i, len(Gn)): | |||
| for e1 in Gn[i].edges(data=True): | |||
| for e2 in Gn[j].edges(data=True): | |||
| if e1[2]['cost'] == e2[2]['cost']: | |||
| kn = node_kernels['mix'] | |||
| try: | |||
| n11, n12, n21, n22 = Gn[i].nodes[e1[ | |||
| 0]], Gn[i].nodes[e1[1]], Gn[ | |||
| j].nodes[e2[0]], Gn[j].nodes[ | |||
| e2[1]] | |||
| kn1 = kn(n11[node_label], n21[ | |||
| node_label], [n11['attributes']], | |||
| [n21['attributes']]) * kn( | |||
| n12[node_label], | |||
| n22[node_label], | |||
| [n12['attributes']], | |||
| [n22['attributes']]) | |||
| Kmatrix[i][j] += kn1 | |||
| except KeyError: # missing labels or attributes | |||
| pass | |||
| Kmatrix[j][i] = Kmatrix[i][j] | |||
| pbar.update(1) | |||
| for i, j in combinations_with_replacement( | |||
| range(0, len(Gn)), 2): | |||
| for e1, e2 in product( | |||
| Gn[i].edges(data=True), Gn[j].edges(data=True)): | |||
| if e1[2]['cost'] == e2[2]['cost']: | |||
| kn = node_kernels['mix'] | |||
| try: | |||
| n11, n12, n21, n22 = Gn[i].nodes[e1[0]], Gn[ | |||
| i].nodes[e1[1]], Gn[j].nodes[e2[0]], Gn[ | |||
| j].nodes[e2[1]] | |||
| kn1 = kn(n11[node_label], n21[node_label], [ | |||
| n11['attributes'] | |||
| ], [n21['attributes']]) * kn( | |||
| n12[node_label], n22[node_label], | |||
| [n12['attributes']], [n22['attributes']]) | |||
| Kmatrix[i][j] += kn1 | |||
| except KeyError: # missing labels or attributes | |||
| pass | |||
| Kmatrix[j][i] = Kmatrix[i][j] | |||
| pbar.update(1) | |||
| else: | |||
| for i in range(0, len(Gn)): | |||
| for j in range(i, len(Gn)): | |||
| for e1 in Gn[i].edges(data=True): | |||
| for e2 in Gn[j].edges(data=True): | |||
| if e1[2]['cost'] == e2[2]['cost']: | |||
| kn = node_kernels['mix'] | |||
| try: | |||
| # each edge walk is counted twice, starting from both its extreme nodes. | |||
| n11, n12, n21, n22 = Gn[i].nodes[e1[ | |||
| 0]], Gn[i].nodes[e1[1]], Gn[ | |||
| j].nodes[e2[0]], Gn[j].nodes[ | |||
| e2[1]] | |||
| kn1 = kn(n11[node_label], n21[ | |||
| node_label], [n11['attributes']], | |||
| [n21['attributes']]) * kn( | |||
| n12[node_label], | |||
| n22[node_label], | |||
| [n12['attributes']], | |||
| [n22['attributes']]) | |||
| kn2 = kn(n11[node_label], n22[ | |||
| node_label], [n11['attributes']], | |||
| [n22['attributes']]) * kn( | |||
| n12[node_label], | |||
| n21[node_label], | |||
| [n12['attributes']], | |||
| [n21['attributes']]) | |||
| Kmatrix[i][j] += kn1 + kn2 | |||
| except KeyError: # missing labels or attributes | |||
| pass | |||
| Kmatrix[j][i] = Kmatrix[i][j] | |||
| pbar.update(1) | |||
| for i, j in combinations_with_replacement( | |||
| range(0, len(Gn)), 2): | |||
| for e1, e2 in product( | |||
| Gn[i].edges(data=True), Gn[j].edges(data=True)): | |||
| if e1[2]['cost'] == e2[2]['cost']: | |||
| kn = node_kernels['mix'] | |||
| try: | |||
| # each edge walk is counted twice, starting from both its extreme nodes. | |||
| n11, n12, n21, n22 = Gn[i].nodes[e1[0]], Gn[ | |||
| i].nodes[e1[1]], Gn[j].nodes[e2[0]], Gn[ | |||
| j].nodes[e2[1]] | |||
| kn1 = kn(n11[node_label], n21[node_label], [ | |||
| n11['attributes'] | |||
| ], [n21['attributes']]) * kn( | |||
| n12[node_label], n22[node_label], | |||
| [n12['attributes']], [n22['attributes']]) | |||
| kn2 = kn(n11[node_label], n22[node_label], [ | |||
| n11['attributes'] | |||
| ], [n22['attributes']]) * kn( | |||
| n12[node_label], n21[node_label], | |||
| [n12['attributes']], [n21['attributes']]) | |||
| Kmatrix[i][j] += kn1 + kn2 | |||
| except KeyError: # missing labels or attributes | |||
| pass | |||
| Kmatrix[j][i] = Kmatrix[i][j] | |||
| pbar.update(1) | |||
| # node symb labeled | |||
| else: | |||
| if ds_attrs['is_directed']: | |||
| for i in range(0, len(Gn)): | |||
| for j in range(i, len(Gn)): | |||
| for e1 in Gn[i].edges(data=True): | |||
| for e2 in Gn[j].edges(data=True): | |||
| if e1[2]['cost'] == e2[2]['cost']: | |||
| kn = node_kernels['symb'] | |||
| try: | |||
| n11, n12, n21, n22 = Gn[i].nodes[e1[ | |||
| 0]], Gn[i].nodes[e1[1]], Gn[ | |||
| j].nodes[e2[0]], Gn[j].nodes[ | |||
| e2[1]] | |||
| kn1 = kn(n11[node_label], | |||
| n21[node_label]) * kn( | |||
| n12[node_label], | |||
| n22[node_label]) | |||
| Kmatrix[i][j] += kn1 | |||
| except KeyError: # missing labels | |||
| pass | |||
| Kmatrix[j][i] = Kmatrix[i][j] | |||
| pbar.update(1) | |||
| for i, j in combinations_with_replacement( | |||
| range(0, len(Gn)), 2): | |||
| for e1, e2 in product( | |||
| Gn[i].edges(data=True), Gn[j].edges(data=True)): | |||
| if e1[2]['cost'] == e2[2]['cost']: | |||
| kn = node_kernels['symb'] | |||
| try: | |||
| n11, n12, n21, n22 = Gn[i].nodes[e1[0]], Gn[ | |||
| i].nodes[e1[1]], Gn[j].nodes[e2[0]], Gn[ | |||
| j].nodes[e2[1]] | |||
| kn1 = kn(n11[node_label], | |||
| n21[node_label]) * kn( | |||
| n12[node_label], n22[node_label]) | |||
| Kmatrix[i][j] += kn1 | |||
| except KeyError: # missing labels | |||
| pass | |||
| Kmatrix[j][i] = Kmatrix[i][j] | |||
| pbar.update(1) | |||
| else: | |||
| for i in range(0, len(Gn)): | |||
| for j in range(i, len(Gn)): | |||
| for e1 in Gn[i].edges(data=True): | |||
| for e2 in Gn[j].edges(data=True): | |||
| if e1[2]['cost'] == e2[2]['cost']: | |||
| kn = node_kernels['symb'] | |||
| try: | |||
| # each edge walk is counted twice, starting from both its extreme nodes. | |||
| n11, n12, n21, n22 = Gn[i].nodes[e1[ | |||
| 0]], Gn[i].nodes[e1[1]], Gn[ | |||
| j].nodes[e2[0]], Gn[j].nodes[ | |||
| e2[1]] | |||
| kn1 = kn(n11[node_label], | |||
| n21[node_label]) * kn( | |||
| n12[node_label], | |||
| n22[node_label]) | |||
| kn2 = kn(n11[node_label], | |||
| n22[node_label]) * kn( | |||
| n12[node_label], | |||
| n21[node_label]) | |||
| Kmatrix[i][j] += kn1 + kn2 | |||
| except KeyError: # missing labels | |||
| pass | |||
| Kmatrix[j][i] = Kmatrix[i][j] | |||
| pbar.update(1) | |||
| for i, j in combinations_with_replacement( | |||
| range(0, len(Gn)), 2): | |||
| for e1, e2 in product( | |||
| Gn[i].edges(data=True), Gn[j].edges(data=True)): | |||
| if e1[2]['cost'] == e2[2]['cost']: | |||
| kn = node_kernels['symb'] | |||
| try: | |||
| # each edge walk is counted twice, starting from both its extreme nodes. | |||
| n11, n12, n21, n22 = Gn[i].nodes[e1[0]], Gn[ | |||
| i].nodes[e1[1]], Gn[j].nodes[e2[0]], Gn[ | |||
| j].nodes[e2[1]] | |||
| kn1 = kn(n11[node_label], | |||
| n21[node_label]) * kn( | |||
| n12[node_label], n22[node_label]) | |||
| kn2 = kn(n11[node_label], | |||
| n22[node_label]) * kn( | |||
| n12[node_label], n21[node_label]) | |||
| Kmatrix[i][j] += kn1 + kn2 | |||
| except KeyError: # missing labels | |||
| pass | |||
| Kmatrix[j][i] = Kmatrix[i][j] | |||
| pbar.update(1) | |||
| else: | |||
| # node non-synb labeled | |||
| if ds_attrs['node_attr_dim'] > 0: | |||
| if ds_attrs['is_directed']: | |||
| for i in range(0, len(Gn)): | |||
| for j in range(i, len(Gn)): | |||
| for e1 in Gn[i].edges(data=True): | |||
| for e2 in Gn[j].edges(data=True): | |||
| if e1[2]['cost'] == e2[2]['cost']: | |||
| kn = node_kernels['nsymb'] | |||
| try: | |||
| # each edge walk is counted twice, starting from both its extreme nodes. | |||
| n11, n12, n21, n22 = Gn[i].nodes[e1[ | |||
| 0]], Gn[i].nodes[e1[1]], Gn[ | |||
| j].nodes[e2[0]], Gn[j].nodes[ | |||
| e2[1]] | |||
| kn1 = kn([n11['attributes']], | |||
| [n21['attributes']]) * kn( | |||
| [n12['attributes']], | |||
| [n22['attributes']]) | |||
| Kmatrix[i][j] += kn1 | |||
| except KeyError: # missing attributes | |||
| pass | |||
| Kmatrix[j][i] = Kmatrix[i][j] | |||
| pbar.update(1) | |||
| for i, j in combinations_with_replacement( | |||
| range(0, len(Gn)), 2): | |||
| for e1, e2 in product( | |||
| Gn[i].edges(data=True), Gn[j].edges(data=True)): | |||
| if e1[2]['cost'] == e2[2]['cost']: | |||
| kn = node_kernels['nsymb'] | |||
| try: | |||
| # each edge walk is counted twice, starting from both its extreme nodes. | |||
| n11, n12, n21, n22 = Gn[i].nodes[e1[0]], Gn[ | |||
| i].nodes[e1[1]], Gn[j].nodes[e2[0]], Gn[ | |||
| j].nodes[e2[1]] | |||
| kn1 = kn([n11['attributes']], | |||
| [n21['attributes']]) * kn( | |||
| [n12['attributes']], | |||
| [n22['attributes']]) | |||
| Kmatrix[i][j] += kn1 | |||
| except KeyError: # missing attributes | |||
| pass | |||
| Kmatrix[j][i] = Kmatrix[i][j] | |||
| pbar.update(1) | |||
| else: | |||
| for i in range(0, len(Gn)): | |||
| for j in range(i, len(Gn)): | |||
| for e1 in Gn[i].edges(data=True): | |||
| for e2 in Gn[j].edges(data=True): | |||
| if e1[2]['cost'] == e2[2]['cost']: | |||
| kn = node_kernels['nsymb'] | |||
| try: | |||
| # each edge walk is counted twice, starting from both its extreme nodes. | |||
| n11, n12, n21, n22 = Gn[i].nodes[e1[ | |||
| 0]], Gn[i].nodes[e1[1]], Gn[ | |||
| j].nodes[e2[0]], Gn[j].nodes[ | |||
| e2[1]] | |||
| kn1 = kn([n11['attributes']], | |||
| [n21['attributes']]) * kn( | |||
| [n12['attributes']], | |||
| [n22['attributes']]) | |||
| kn2 = kn([n11['attributes']], | |||
| [n22['attributes']]) * kn( | |||
| [n12['attributes']], | |||
| [n21['attributes']]) | |||
| Kmatrix[i][j] += kn1 + kn2 | |||
| except KeyError: # missing attributes | |||
| pass | |||
| Kmatrix[j][i] = Kmatrix[i][j] | |||
| pbar.update(1) | |||
| for i, j in combinations_with_replacement( | |||
| range(0, len(Gn)), 2): | |||
| for e1, e2 in product( | |||
| Gn[i].edges(data=True), Gn[j].edges(data=True)): | |||
| if e1[2]['cost'] == e2[2]['cost']: | |||
| kn = node_kernels['nsymb'] | |||
| try: | |||
| # each edge walk is counted twice, starting from both its extreme nodes. | |||
| n11, n12, n21, n22 = Gn[i].nodes[e1[0]], Gn[ | |||
| i].nodes[e1[1]], Gn[j].nodes[e2[0]], Gn[ | |||
| j].nodes[e2[1]] | |||
| kn1 = kn([n11['attributes']], | |||
| [n21['attributes']]) * kn( | |||
| [n12['attributes']], | |||
| [n22['attributes']]) | |||
| kn2 = kn([n11['attributes']], | |||
| [n22['attributes']]) * kn( | |||
| [n12['attributes']], | |||
| [n21['attributes']]) | |||
| Kmatrix[i][j] += kn1 + kn2 | |||
| except KeyError: # missing attributes | |||
| pass | |||
| Kmatrix[j][i] = Kmatrix[i][j] | |||
| pbar.update(1) | |||
| # node unlabeled | |||
| else: | |||
| for i in range(0, len(Gn)): | |||
| for j in range(i, len(Gn)): | |||
| for e1 in Gn[i].edges(data=True): | |||
| for e2 in Gn[j].edges(data=True): | |||
| if e1[2]['cost'] == e2[2]['cost']: | |||
| Kmatrix[i][j] += 1 | |||
| Kmatrix[j][i] = Kmatrix[i][j] | |||
| pbar.update(1) | |||
| for i, j in combinations_with_replacement(range(0, len(Gn)), 2): | |||
| for e1, e2 in product( | |||
| Gn[i].edges(data=True), Gn[j].edges(data=True)): | |||
| if e1[2]['cost'] == e2[2]['cost']: | |||
| Kmatrix[i][j] += 1 | |||
| Kmatrix[j][i] = Kmatrix[i][j] | |||
| pbar.update(1) | |||
| run_time = time.time() - start_time | |||
| print( | |||
| @@ -119,7 +119,7 @@ def untotterTransformation(G, node_label, edge_label): | |||
| def direct_product(G1, G2, node_label, edge_label): | |||
| """Return the direct/tensor product of G1 and G2. | |||
| """Return the direct/tensor product of directed graphs G1 and G2. | |||
| Parameters | |||
| ---------- | |||
| @@ -137,7 +137,7 @@ def direct_product(G1, G2, node_label, edge_label): | |||
| Notes | |||
| ----- | |||
| This method differs from networkx.tensor_product in that this method only adds nodes and edges in G1 and G2 that have the same labels to direct product graph. | |||
| This method differs from networkx.tensor_product in that this method only adds nodes and edges in G1 and G2 that have the same labels to the direct product graph. | |||
| References | |||
| ---------- | |||
| @@ -147,25 +147,37 @@ def direct_product(G1, G2, node_label, edge_label): | |||
| from itertools import product | |||
| # G = G.to_directed() | |||
| gt = nx.Graph() | |||
| gt = nx.DiGraph() | |||
| # add nodes | |||
| for u, v in product(G1, G2): | |||
| if G1.nodes[u][node_label] == G2.nodes[v][node_label]: | |||
| gt.add_node((u, v)) | |||
| gt.nodes[(u, v)].update({node_label: G1.nodes[u][node_label]}) | |||
| # add edges | |||
| for u, v in product(gt, gt): | |||
| if (u[0], v[0]) in G1.edges and ( | |||
| u[1], v[1] | |||
| ) in G2.edges and G1.edges[u[0], | |||
| v[0]][edge_label] == G2.edges[u[1], | |||
| v[1]][edge_label]: | |||
| gt.add_edge((u[0], u[1]), (v[0], v[1])) | |||
| gt.edges[(u[0], u[1]), (v[0], v[1])].update({ | |||
| # add edges, faster for sparse graphs (no so many edges), which is the most case for now. | |||
| for (u1, v1), (u2, v2) in product(G1.edges, G2.edges): | |||
| if (u1, u2) in gt and ( | |||
| v1, v2 | |||
| ) in gt and G1.edges[u1, v1][edge_label] == G2.edges[u2, | |||
| v2][edge_label]: | |||
| gt.add_edge((u1, u2), (v1, v2)) | |||
| gt.edges[(u1, u2), (v1, v2)].update({ | |||
| edge_label: | |||
| G1.edges[u[0], v[0]][edge_label] | |||
| G1.edges[u1, v1][edge_label] | |||
| }) | |||
| # # add edges, faster for dense graphs (a lot of edges, complete graph would be super). | |||
| # for u, v in product(gt, gt): | |||
| # if (u[0], v[0]) in G1.edges and ( | |||
| # u[1], v[1] | |||
| # ) in G2.edges and G1.edges[u[0], | |||
| # v[0]][edge_label] == G2.edges[u[1], | |||
| # v[1]][edge_label]: | |||
| # gt.add_edge((u[0], u[1]), (v[0], v[1])) | |||
| # gt.edges[(u[0], u[1]), (v[0], v[1])].update({ | |||
| # edge_label: | |||
| # G1.edges[u[0], v[0]][edge_label] | |||
| # }) | |||
| # relabel nodes using consecutive integers for convenience of kernel calculation. | |||
| # gt = nx.convert_node_labels_to_integers( | |||
| # gt, first_label=0, label_attribute='label_orignal') | |||