You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

sylvester_equation.py 7.8 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. Created on Wed Aug 19 17:24:46 2020
  5. @author: ljia
  6. @references:
  7. [1] S Vichy N Vishwanathan, Nicol N Schraudolph, Risi Kondor, and Karsten M Borgwardt. Graph kernels. Journal of Machine Learning Research, 11(Apr):1201–1242, 2010.
  8. """
  9. import sys
  10. from tqdm import tqdm
  11. import numpy as np
  12. import networkx as nx
  13. from control import dlyap
  14. from gklearn.utils.parallel import parallel_gm, parallel_me
  15. from gklearn.kernels import RandomWalkMeta
  16. class SylvesterEquation(RandomWalkMeta):
  17. def __init__(self, **kwargs):
  18. super().__init__(**kwargs)
  19. def _compute_gm_series(self):
  20. self._check_edge_weight(self._graphs, self._verbose)
  21. self._check_graphs(self._graphs)
  22. if self._verbose >= 2:
  23. import warnings
  24. warnings.warn('All labels are ignored.')
  25. lmda = self._weight
  26. # compute Gram matrix.
  27. gram_matrix = np.zeros((len(self._graphs), len(self._graphs)))
  28. if self._q is None:
  29. # don't normalize adjacency matrices if q is a uniform vector. Note
  30. # A_wave_list actually contains the transposes of the adjacency matrices.
  31. if self._verbose >= 2:
  32. iterator = tqdm(self._graphs, desc='compute adjacency matrices', file=sys.stdout)
  33. else:
  34. iterator = self._graphs
  35. A_wave_list = [nx.adjacency_matrix(G, self._edge_weight).todense().transpose() for G in iterator]
  36. # # normalized adjacency matrices
  37. # A_wave_list = []
  38. # for G in tqdm(Gn, desc='compute adjacency matrices', file=sys.stdout):
  39. # A_tilde = nx.adjacency_matrix(G, eweight).todense().transpose()
  40. # norm = A_tilde.sum(axis=0)
  41. # norm[norm == 0] = 1
  42. # A_wave_list.append(A_tilde / norm)
  43. if self._p is None: # p is uniform distribution as default.
  44. from itertools import combinations_with_replacement
  45. itr = combinations_with_replacement(range(0, len(self._graphs)), 2)
  46. if self._verbose >= 2:
  47. iterator = tqdm(itr, desc='Computing kernels', file=sys.stdout)
  48. else:
  49. iterator = itr
  50. for i, j in iterator:
  51. kernel = self._kernel_do(A_wave_list[i], A_wave_list[j], lmda)
  52. gram_matrix[i][j] = kernel
  53. gram_matrix[j][i] = kernel
  54. else: # @todo
  55. pass
  56. else: # @todo
  57. pass
  58. return gram_matrix
  59. def _compute_gm_imap_unordered(self):
  60. self._check_edge_weight(self._graphs, self._verbose)
  61. self._check_graphs(self._graphs)
  62. if self._verbose >= 2:
  63. import warnings
  64. warnings.warn('All labels are ignored.')
  65. # compute Gram matrix.
  66. gram_matrix = np.zeros((len(self._graphs), len(self._graphs)))
  67. if self._q is None:
  68. # don't normalize adjacency matrices if q is a uniform vector. Note
  69. # A_wave_list actually contains the transposes of the adjacency matrices.
  70. if self._verbose >= 2:
  71. iterator = tqdm(self._graphs, desc='compute adjacency matrices', file=sys.stdout)
  72. else:
  73. iterator = self._graphs
  74. A_wave_list = [nx.adjacency_matrix(G, self._edge_weight).todense().transpose() for G in iterator] # @todo: parallel?
  75. if self._p is None: # p is uniform distribution as default.
  76. def init_worker(A_wave_list_toshare):
  77. global G_A_wave_list
  78. G_A_wave_list = A_wave_list_toshare
  79. do_fun = self._wrapper_kernel_do
  80. parallel_gm(do_fun, gram_matrix, self._graphs, init_worker=init_worker,
  81. glbv=(A_wave_list,), n_jobs=self._n_jobs, verbose=self._verbose)
  82. else: # @todo
  83. pass
  84. else: # @todo
  85. pass
  86. return gram_matrix
  87. def _compute_kernel_list_series(self, g1, g_list):
  88. self._check_edge_weight(g_list + [g1], self._verbose)
  89. self._check_graphs(g_list + [g1])
  90. if self._verbose >= 2:
  91. import warnings
  92. warnings.warn('All labels are ignored.')
  93. lmda = self._weight
  94. # compute kernel list.
  95. kernel_list = [None] * len(g_list)
  96. if self._q is None:
  97. # don't normalize adjacency matrices if q is a uniform vector. Note
  98. # A_wave_list actually contains the transposes of the adjacency matrices.
  99. A_wave_1 = nx.adjacency_matrix(g1, self._edge_weight).todense().transpose()
  100. if self._verbose >= 2:
  101. iterator = tqdm(g_list, desc='compute adjacency matrices', file=sys.stdout)
  102. else:
  103. iterator = g_list
  104. A_wave_list = [nx.adjacency_matrix(G, self._edge_weight).todense().transpose() for G in iterator]
  105. if self._p is None: # p is uniform distribution as default.
  106. if self._verbose >= 2:
  107. iterator = tqdm(range(len(g_list)), desc='Computing kernels', file=sys.stdout)
  108. else:
  109. iterator = range(len(g_list))
  110. for i in iterator:
  111. kernel = self._kernel_do(A_wave_1, A_wave_list[i], lmda)
  112. kernel_list[i] = kernel
  113. else: # @todo
  114. pass
  115. else: # @todo
  116. pass
  117. return kernel_list
  118. def _compute_kernel_list_imap_unordered(self, g1, g_list):
  119. self._check_edge_weight(g_list + [g1], self._verbose)
  120. self._check_graphs(g_list + [g1])
  121. if self._verbose >= 2:
  122. import warnings
  123. warnings.warn('All labels are ignored.')
  124. # compute kernel list.
  125. kernel_list = [None] * len(g_list)
  126. if self._q is None:
  127. # don't normalize adjacency matrices if q is a uniform vector. Note
  128. # A_wave_list actually contains the transposes of the adjacency matrices.
  129. A_wave_1 = nx.adjacency_matrix(g1, self._edge_weight).todense().transpose()
  130. if self._verbose >= 2:
  131. iterator = tqdm(g_list, desc='compute adjacency matrices', file=sys.stdout)
  132. else:
  133. iterator = g_list
  134. A_wave_list = [nx.adjacency_matrix(G, self._edge_weight).todense().transpose() for G in iterator] # @todo: parallel?
  135. if self._p is None: # p is uniform distribution as default.
  136. def init_worker(A_wave_1_toshare, A_wave_list_toshare):
  137. global G_A_wave_1, G_A_wave_list
  138. G_A_wave_1 = A_wave_1_toshare
  139. G_A_wave_list = A_wave_list_toshare
  140. do_fun = self._wrapper_kernel_list_do
  141. def func_assign(result, var_to_assign):
  142. var_to_assign[result[0]] = result[1]
  143. itr = range(len(g_list))
  144. len_itr = len(g_list)
  145. parallel_me(do_fun, func_assign, kernel_list, itr, len_itr=len_itr,
  146. init_worker=init_worker, glbv=(A_wave_1, A_wave_list), method='imap_unordered',
  147. n_jobs=self._n_jobs, itr_desc='Computing kernels', verbose=self._verbose)
  148. else: # @todo
  149. pass
  150. else: # @todo
  151. pass
  152. return kernel_list
  153. def _wrapper_kernel_list_do(self, itr):
  154. return itr, self._kernel_do(G_A_wave_1, G_A_wave_list[itr], self._weight)
  155. def _compute_single_kernel_series(self, g1, g2):
  156. self._check_edge_weight([g1] + [g2], self._verbose)
  157. self._check_graphs([g1] + [g2])
  158. if self._verbose >= 2:
  159. import warnings
  160. warnings.warn('All labels are ignored.')
  161. lmda = self._weight
  162. if self._q is None:
  163. # don't normalize adjacency matrices if q is a uniform vector. Note
  164. # A_wave_list actually contains the transposes of the adjacency matrices.
  165. A_wave_1 = nx.adjacency_matrix(g1, self._edge_weight).todense().transpose()
  166. A_wave_2 = nx.adjacency_matrix(g2, self._edge_weight).todense().transpose()
  167. if self._p is None: # p is uniform distribution as default.
  168. kernel = self._kernel_do(A_wave_1, A_wave_2, lmda)
  169. else: # @todo
  170. pass
  171. else: # @todo
  172. pass
  173. return kernel
  174. def _kernel_do(self, A_wave1, A_wave2, lmda):
  175. S = lmda * A_wave2
  176. T_t = A_wave1
  177. # use uniform distribution if there is no prior knowledge.
  178. nb_pd = len(A_wave1) * len(A_wave2)
  179. p_times_uni = 1 / nb_pd
  180. M0 = np.full((len(A_wave2), len(A_wave1)), p_times_uni)
  181. X = dlyap(S, T_t, M0)
  182. X = np.reshape(X, (-1, 1), order='F')
  183. # use uniform distribution if there is no prior knowledge.
  184. q_times = np.full((1, nb_pd), p_times_uni)
  185. return np.dot(q_times, X)
  186. def _wrapper_kernel_do(self, itr):
  187. i = itr[0]
  188. j = itr[1]
  189. return i, j, self._kernel_do(G_A_wave_list[i], G_A_wave_list[j], self._weight)

A Python package for graph kernels, graph edit distances and graph pre-image problem.