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.

util.py 20 kB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. Created on Tue Mar 31 17:06:22 2020
  5. @author: ljia
  6. """
  7. import numpy as np
  8. from itertools import combinations
  9. import multiprocessing
  10. from multiprocessing import Pool
  11. from functools import partial
  12. import sys
  13. from tqdm import tqdm
  14. import networkx as nx
  15. from gklearn.ged.env import GEDEnv
  16. def compute_ged(g1, g2, options):
  17. from gklearn.gedlib import librariesImport, gedlibpy
  18. ged_env = gedlibpy.GEDEnv()
  19. ged_env.set_edit_cost(options['edit_cost'], edit_cost_constant=options['edit_cost_constants'])
  20. ged_env.add_nx_graph(g1, '')
  21. ged_env.add_nx_graph(g2, '')
  22. listID = ged_env.get_all_graph_ids()
  23. ged_env.init(init_type=options['init_option'])
  24. ged_env.set_method(options['method'], ged_options_to_string(options))
  25. ged_env.init_method()
  26. g = listID[0]
  27. h = listID[1]
  28. ged_env.run_method(g, h)
  29. pi_forward = ged_env.get_forward_map(g, h)
  30. pi_backward = ged_env.get_backward_map(g, h)
  31. upper = ged_env.get_upper_bound(g, h)
  32. dis = upper
  33. # make the map label correct (label remove map as np.inf)
  34. nodes1 = [n for n in g1.nodes()]
  35. nodes2 = [n for n in g2.nodes()]
  36. nb1 = nx.number_of_nodes(g1)
  37. nb2 = nx.number_of_nodes(g2)
  38. pi_forward = [nodes2[pi] if pi < nb2 else np.inf for pi in pi_forward]
  39. pi_backward = [nodes1[pi] if pi < nb1 else np.inf for pi in pi_backward]
  40. # print(pi_forward)
  41. return dis, pi_forward, pi_backward
  42. def compute_geds_cml(graphs, options={}, sort=True, parallel=False, verbose=True):
  43. node_label_costs = options['node_label_costs'] if 'node_label_costs' in options else None
  44. edge_label_costs = options['edge_label_costs'] if 'edge_label_costs' in options else None
  45. # initialize ged env.
  46. ged_env = GEDEnv()
  47. ged_env.set_edit_cost(options['edit_cost'], edit_cost_constants=options['edit_cost_constants'])
  48. for g in graphs:
  49. ged_env.add_nx_graph(g, '')
  50. listID = ged_env.get_all_graph_ids()
  51. ged_env.set_label_costs(node_label_costs, edge_label_costs)
  52. ged_env.init(init_type=options['init_option'])
  53. if parallel:
  54. options['threads'] = 1
  55. ged_env.set_method(options['method'], options)
  56. ged_env.init_method()
  57. # compute ged.
  58. # options used to compute numbers of edit operations.
  59. neo_options = {'edit_cost': options['edit_cost'],
  60. # 'node_labels': options['node_labels'], 'edge_labels': options['edge_labels'],
  61. # 'node_attrs': options['node_attrs'], 'edge_attrs': options['edge_attrs'],
  62. 'is_cml': True,
  63. 'node_labels': ged_env.get_all_node_labels(),
  64. 'edge_labels': ged_env.get_all_edge_labels()}
  65. ged_mat = np.zeros((len(graphs), len(graphs)))
  66. if parallel:
  67. len_itr = int(len(graphs) * (len(graphs) - 1) / 2)
  68. ged_vec = [0 for i in range(len_itr)]
  69. n_edit_operations = [0 for i in range(len_itr)]
  70. itr = combinations(range(0, len(graphs)), 2)
  71. n_jobs = multiprocessing.cpu_count()
  72. if len_itr < 100 * n_jobs:
  73. chunksize = int(len_itr / n_jobs) + 1
  74. else:
  75. chunksize = 100
  76. def init_worker(graphs_toshare, ged_env_toshare, listID_toshare):
  77. global G_graphs, G_ged_env, G_listID
  78. G_graphs = graphs_toshare
  79. G_ged_env = ged_env_toshare
  80. G_listID = listID_toshare
  81. do_partial = partial(_wrapper_compute_ged_parallel, neo_options, sort)
  82. pool = Pool(processes=n_jobs, initializer=init_worker, initargs=(graphs, ged_env, listID))
  83. if verbose:
  84. iterator = tqdm(pool.imap_unordered(do_partial, itr, chunksize),
  85. desc='computing GEDs', file=sys.stdout)
  86. else:
  87. iterator = pool.imap_unordered(do_partial, itr, chunksize)
  88. # iterator = pool.imap_unordered(do_partial, itr, chunksize)
  89. for i, j, dis, n_eo_tmp in iterator:
  90. idx_itr = int(len(graphs) * i + j - (i + 1) * (i + 2) / 2)
  91. ged_vec[idx_itr] = dis
  92. ged_mat[i][j] = dis
  93. ged_mat[j][i] = dis
  94. n_edit_operations[idx_itr] = n_eo_tmp
  95. # print('\n-------------------------------------------')
  96. # print(i, j, idx_itr, dis)
  97. pool.close()
  98. pool.join()
  99. else:
  100. ged_vec = []
  101. n_edit_operations = []
  102. if verbose:
  103. iterator = tqdm(range(len(graphs)), desc='computing GEDs', file=sys.stdout)
  104. else:
  105. iterator = range(len(graphs))
  106. for i in iterator:
  107. # for i in range(len(graphs)):
  108. for j in range(i + 1, len(graphs)):
  109. if nx.number_of_nodes(graphs[i]) <= nx.number_of_nodes(graphs[j]) or not sort:
  110. dis, pi_forward, pi_backward = _compute_ged(ged_env, listID[i], listID[j], graphs[i], graphs[j])
  111. else:
  112. dis, pi_backward, pi_forward = _compute_ged(ged_env, listID[j], listID[i], graphs[j], graphs[i])
  113. ged_vec.append(dis)
  114. ged_mat[i][j] = dis
  115. ged_mat[j][i] = dis
  116. n_eo_tmp = get_nb_edit_operations(graphs[i], graphs[j], pi_forward, pi_backward, **neo_options)
  117. n_edit_operations.append(n_eo_tmp)
  118. return ged_vec, ged_mat, n_edit_operations
  119. def compute_geds(graphs, options={}, sort=True, parallel=False, verbose=True):
  120. from gklearn.gedlib import librariesImport, gedlibpy
  121. # initialize ged env.
  122. ged_env = gedlibpy.GEDEnv()
  123. ged_env.set_edit_cost(options['edit_cost'], edit_cost_constant=options['edit_cost_constants'])
  124. for g in graphs:
  125. ged_env.add_nx_graph(g, '')
  126. listID = ged_env.get_all_graph_ids()
  127. ged_env.init()
  128. if parallel:
  129. options['threads'] = 1
  130. ged_env.set_method(options['method'], ged_options_to_string(options))
  131. ged_env.init_method()
  132. # compute ged.
  133. neo_options = {'edit_cost': options['edit_cost'],
  134. 'node_labels': options['node_labels'], 'edge_labels': options['edge_labels'],
  135. 'node_attrs': options['node_attrs'], 'edge_attrs': options['edge_attrs']}
  136. ged_mat = np.zeros((len(graphs), len(graphs)))
  137. if parallel:
  138. len_itr = int(len(graphs) * (len(graphs) - 1) / 2)
  139. ged_vec = [0 for i in range(len_itr)]
  140. n_edit_operations = [0 for i in range(len_itr)]
  141. itr = combinations(range(0, len(graphs)), 2)
  142. n_jobs = multiprocessing.cpu_count()
  143. if len_itr < 100 * n_jobs:
  144. chunksize = int(len_itr / n_jobs) + 1
  145. else:
  146. chunksize = 100
  147. def init_worker(graphs_toshare, ged_env_toshare, listID_toshare):
  148. global G_graphs, G_ged_env, G_listID
  149. G_graphs = graphs_toshare
  150. G_ged_env = ged_env_toshare
  151. G_listID = listID_toshare
  152. do_partial = partial(_wrapper_compute_ged_parallel, neo_options, sort)
  153. pool = Pool(processes=n_jobs, initializer=init_worker, initargs=(graphs, ged_env, listID))
  154. if verbose:
  155. iterator = tqdm(pool.imap_unordered(do_partial, itr, chunksize),
  156. desc='computing GEDs', file=sys.stdout)
  157. else:
  158. iterator = pool.imap_unordered(do_partial, itr, chunksize)
  159. # iterator = pool.imap_unordered(do_partial, itr, chunksize)
  160. for i, j, dis, n_eo_tmp in iterator:
  161. idx_itr = int(len(graphs) * i + j - (i + 1) * (i + 2) / 2)
  162. ged_vec[idx_itr] = dis
  163. ged_mat[i][j] = dis
  164. ged_mat[j][i] = dis
  165. n_edit_operations[idx_itr] = n_eo_tmp
  166. # print('\n-------------------------------------------')
  167. # print(i, j, idx_itr, dis)
  168. pool.close()
  169. pool.join()
  170. else:
  171. ged_vec = []
  172. n_edit_operations = []
  173. if verbose:
  174. iterator = tqdm(range(len(graphs)), desc='computing GEDs', file=sys.stdout)
  175. else:
  176. iterator = range(len(graphs))
  177. for i in iterator:
  178. # for i in range(len(graphs)):
  179. for j in range(i + 1, len(graphs)):
  180. if nx.number_of_nodes(graphs[i]) <= nx.number_of_nodes(graphs[j]) or not sort:
  181. dis, pi_forward, pi_backward = _compute_ged(ged_env, listID[i], listID[j], graphs[i], graphs[j])
  182. else:
  183. dis, pi_backward, pi_forward = _compute_ged(ged_env, listID[j], listID[i], graphs[j], graphs[i])
  184. ged_vec.append(dis)
  185. ged_mat[i][j] = dis
  186. ged_mat[j][i] = dis
  187. n_eo_tmp = get_nb_edit_operations(graphs[i], graphs[j], pi_forward, pi_backward, **neo_options)
  188. n_edit_operations.append(n_eo_tmp)
  189. return ged_vec, ged_mat, n_edit_operations
  190. def _wrapper_compute_ged_parallel(options, sort, itr):
  191. i = itr[0]
  192. j = itr[1]
  193. dis, n_eo_tmp = _compute_ged_parallel(G_ged_env, G_listID[i], G_listID[j], G_graphs[i], G_graphs[j], options, sort)
  194. return i, j, dis, n_eo_tmp
  195. def _compute_ged_parallel(env, gid1, gid2, g1, g2, options, sort):
  196. if nx.number_of_nodes(g1) <= nx.number_of_nodes(g2) or not sort:
  197. dis, pi_forward, pi_backward = _compute_ged(env, gid1, gid2, g1, g2)
  198. else:
  199. dis, pi_backward, pi_forward = _compute_ged(env, gid2, gid1, g2, g1)
  200. n_eo_tmp = get_nb_edit_operations(g1, g2, pi_forward, pi_backward, **options) # [0,0,0,0,0,0]
  201. return dis, n_eo_tmp
  202. def _compute_ged(env, gid1, gid2, g1, g2):
  203. env.run_method(gid1, gid2)
  204. pi_forward = env.get_forward_map(gid1, gid2)
  205. pi_backward = env.get_backward_map(gid1, gid2)
  206. upper = env.get_upper_bound(gid1, gid2)
  207. dis = upper
  208. # make the map label correct (label remove map as np.inf)
  209. nodes1 = [n for n in g1.nodes()]
  210. nodes2 = [n for n in g2.nodes()]
  211. nb1 = nx.number_of_nodes(g1)
  212. nb2 = nx.number_of_nodes(g2)
  213. pi_forward = [nodes2[pi] if pi < nb2 else np.inf for pi in pi_forward]
  214. pi_backward = [nodes1[pi] if pi < nb1 else np.inf for pi in pi_backward]
  215. return dis, pi_forward, pi_backward
  216. def get_nb_edit_operations(g1, g2, forward_map, backward_map, edit_cost=None, is_cml=False, **kwargs):
  217. if is_cml:
  218. if edit_cost == 'CONSTANT':
  219. node_label_costs = kwargs.get('node_label_costs')
  220. edge_label_costs = kwargs.get('edge_label_costs')
  221. node_labels = kwargs.get('node_labels', [])
  222. edge_labels = kwargs.get('edge_labels', [])
  223. return get_nb_edit_operations_symbolic_cml(g1, g2, forward_map, backward_map,
  224. node_labels=node_labels, edge_labels=edge_labels)
  225. else:
  226. raise Exception('Edit cost "', edit_cost, '" is not supported.')
  227. else:
  228. if edit_cost == 'LETTER' or edit_cost == 'LETTER2':
  229. return get_nb_edit_operations_letter(g1, g2, forward_map, backward_map)
  230. elif edit_cost == 'NON_SYMBOLIC':
  231. node_attrs = kwargs.get('node_attrs', [])
  232. edge_attrs = kwargs.get('edge_attrs', [])
  233. return get_nb_edit_operations_nonsymbolic(g1, g2, forward_map, backward_map,
  234. node_attrs=node_attrs, edge_attrs=edge_attrs)
  235. elif edit_cost == 'CONSTANT':
  236. node_labels = kwargs.get('node_labels', [])
  237. edge_labels = kwargs.get('edge_labels', [])
  238. return get_nb_edit_operations_symbolic(g1, g2, forward_map, backward_map,
  239. node_labels=node_labels, edge_labels=edge_labels)
  240. else:
  241. return get_nb_edit_operations_symbolic(g1, g2, forward_map, backward_map)
  242. def get_nb_edit_operations_symbolic_cml(g1, g2, forward_map, backward_map,
  243. node_labels=[], edge_labels=[]):
  244. """Compute the number of each edit operations for symbolic-labeled graphs, where the costs are different for each pair of nodes.
  245. Returns
  246. -------
  247. list
  248. A vector of costs bewteen labels, formed in the order of node insertion costs, node deletion costs, node substitition costs, edge insertion costs, edge deletion costs, edge substitition costs. The dummy label is the first label, and the self label costs are not included.
  249. """
  250. # Initialize.
  251. nb_ops_node = np.zeros((1 + len(node_labels), 1 + len(node_labels)))
  252. nb_ops_edge = np.zeros((1 + len(edge_labels), 1 + len(edge_labels)))
  253. # For nodes.
  254. nodes1 = [n for n in g1.nodes()]
  255. for i, map_i in enumerate(forward_map):
  256. label1 = tuple(g1.nodes[nodes1[i]].items()) # @todo: order and faster
  257. idx_label1 = node_labels.index(label1) # @todo: faster
  258. if map_i == np.inf: # deletions.
  259. nb_ops_node[0, idx_label1 + 1] += 1
  260. else: # substitutions.
  261. label2 = tuple(g2.nodes[map_i].items())
  262. if label1 != label2:
  263. idx_label2 = node_labels.index(label2) # @todo: faster
  264. nb_ops_node[idx_label1 + 1, idx_label2 + 1] += 1
  265. # insertions.
  266. nodes2 = [n for n in g2.nodes()]
  267. for i, map_i in enumerate(backward_map):
  268. if map_i == np.inf:
  269. label = tuple(g2.nodes[nodes2[i]].items())
  270. idx_label = node_labels.index(label) # @todo: faster
  271. nb_ops_node[idx_label + 1, 0] += 1
  272. # For edges.
  273. edges1 = [e for e in g1.edges()]
  274. edges2_marked = []
  275. for nf1, nt1 in edges1:
  276. label1 = tuple(g1.edges[(nf1, nt1)].items())
  277. idx_label1 = edge_labels.index(label1) # @todo: faster
  278. idxf1 = nodes1.index(nf1) # @todo: faster
  279. idxt1 = nodes1.index(nt1) # @todo: faster
  280. # At least one of the nodes is removed, thus the edge is removed.
  281. if forward_map[idxf1] == np.inf or forward_map[idxt1] == np.inf:
  282. nb_ops_edge[0, idx_label1 + 1] += 1
  283. # corresponding edge is in g2.
  284. else:
  285. nf2, nt2 = forward_map[idxf1], forward_map[idxt1]
  286. if (nf2, nt2) in g2.edges():
  287. edges2_marked.append((nf2, nt2))
  288. # If edge labels are different.
  289. label2 = tuple(g2.edges[(nf2, nt2)].items())
  290. if label1 != label2:
  291. idx_label2 = edge_labels.index(label2) # @todo: faster
  292. nb_ops_edge[idx_label1 + 1, idx_label2 + 1] += 1
  293. # Switch nf2 and nt2, for directed graphs.
  294. elif (nt2, nf2) in g2.edges():
  295. edges2_marked.append((nt2, nf2))
  296. # If edge labels are different.
  297. label2 = tuple(g2.edges[(nt2, nf2)].items())
  298. if label1 != label2:
  299. idx_label2 = edge_labels.index(label2) # @todo: faster
  300. nb_ops_edge[idx_label1 + 1, idx_label2 + 1] += 1
  301. # Corresponding nodes are in g2, however the edge is removed.
  302. else:
  303. nb_ops_edge[0, idx_label1 + 1] += 1
  304. # insertions.
  305. for e in g2.edges():
  306. if e not in edges2_marked:
  307. label = tuple(g2.edges[e].items())
  308. idx_label = edge_labels.index(label) # @todo: faster
  309. nb_ops_edge[idx_label + 1, 0] += 1
  310. # Reform the costs into a vector.
  311. cost_vector = []
  312. # Add node insertion costs.
  313. for i in range(1, len(nb_ops_node)):
  314. cost_vector.append(nb_ops_node[i, 0])
  315. # Add node deletion costs.
  316. for i in range(1, len(nb_ops_node)):
  317. cost_vector.append(nb_ops_node[0, i])
  318. # Add node substitution costs.
  319. for i in range(1, len(nb_ops_node)):
  320. for j in range(i + 1, len(nb_ops_node)):
  321. cost_vector.append(nb_ops_node[i, j])
  322. # Add edge insertion costs.
  323. for i in range(1, len(nb_ops_edge)):
  324. cost_vector.append(nb_ops_edge[i, 0])
  325. # Add edge deletion costs.
  326. for i in range(1, len(nb_ops_edge)):
  327. cost_vector.append(nb_ops_edge[0, i])
  328. # Add edge substitution costs.
  329. for i in range(1, len(nb_ops_edge)):
  330. for j in range(i + 1, len(nb_ops_edge)):
  331. cost_vector.append(nb_ops_edge[i, j])
  332. return cost_vector
  333. def get_nb_edit_operations_symbolic(g1, g2, forward_map, backward_map,
  334. node_labels=[], edge_labels=[]):
  335. """Compute the number of each edit operations for symbolic-labeled graphs.
  336. """
  337. n_vi = 0
  338. n_vr = 0
  339. n_vs = 0
  340. n_ei = 0
  341. n_er = 0
  342. n_es = 0
  343. nodes1 = [n for n in g1.nodes()]
  344. for i, map_i in enumerate(forward_map):
  345. if map_i == np.inf:
  346. n_vr += 1
  347. else:
  348. for nl in node_labels:
  349. label1 = g1.nodes[nodes1[i]][nl]
  350. label2 = g2.nodes[map_i][nl]
  351. if label1 != label2:
  352. n_vs += 1
  353. break
  354. for map_i in backward_map:
  355. if map_i == np.inf:
  356. n_vi += 1
  357. # idx_nodes1 = range(0, len(node1))
  358. edges1 = [e for e in g1.edges()]
  359. nb_edges2_cnted = 0
  360. for n1, n2 in edges1:
  361. idx1 = nodes1.index(n1)
  362. idx2 = nodes1.index(n2)
  363. # one of the nodes is removed, thus the edge is removed.
  364. if forward_map[idx1] == np.inf or forward_map[idx2] == np.inf:
  365. n_er += 1
  366. # corresponding edge is in g2.
  367. elif (forward_map[idx1], forward_map[idx2]) in g2.edges():
  368. nb_edges2_cnted += 1
  369. # edge labels are different.
  370. for el in edge_labels:
  371. label1 = g2.edges[((forward_map[idx1], forward_map[idx2]))][el]
  372. label2 = g1.edges[(n1, n2)][el]
  373. if label1 != label2:
  374. n_es += 1
  375. break
  376. elif (forward_map[idx2], forward_map[idx1]) in g2.edges():
  377. nb_edges2_cnted += 1
  378. # edge labels are different.
  379. for el in edge_labels:
  380. label1 = g2.edges[((forward_map[idx2], forward_map[idx1]))][el]
  381. label2 = g1.edges[(n1, n2)][el]
  382. if label1 != label2:
  383. n_es += 1
  384. break
  385. # corresponding nodes are in g2, however the edge is removed.
  386. else:
  387. n_er += 1
  388. n_ei = nx.number_of_edges(g2) - nb_edges2_cnted
  389. return n_vi, n_vr, n_vs, n_ei, n_er, n_es
  390. def get_nb_edit_operations_letter(g1, g2, forward_map, backward_map):
  391. """Compute the number of each edit operations.
  392. """
  393. n_vi = 0
  394. n_vr = 0
  395. n_vs = 0
  396. sod_vs = 0
  397. n_ei = 0
  398. n_er = 0
  399. nodes1 = [n for n in g1.nodes()]
  400. for i, map_i in enumerate(forward_map):
  401. if map_i == np.inf:
  402. n_vr += 1
  403. else:
  404. n_vs += 1
  405. diff_x = float(g1.nodes[nodes1[i]]['x']) - float(g2.nodes[map_i]['x'])
  406. diff_y = float(g1.nodes[nodes1[i]]['y']) - float(g2.nodes[map_i]['y'])
  407. sod_vs += np.sqrt(np.square(diff_x) + np.square(diff_y))
  408. for map_i in backward_map:
  409. if map_i == np.inf:
  410. n_vi += 1
  411. # idx_nodes1 = range(0, len(node1))
  412. edges1 = [e for e in g1.edges()]
  413. nb_edges2_cnted = 0
  414. for n1, n2 in edges1:
  415. idx1 = nodes1.index(n1)
  416. idx2 = nodes1.index(n2)
  417. # one of the nodes is removed, thus the edge is removed.
  418. if forward_map[idx1] == np.inf or forward_map[idx2] == np.inf:
  419. n_er += 1
  420. # corresponding edge is in g2. Edge label is not considered.
  421. elif (forward_map[idx1], forward_map[idx2]) in g2.edges() or \
  422. (forward_map[idx2], forward_map[idx1]) in g2.edges():
  423. nb_edges2_cnted += 1
  424. # corresponding nodes are in g2, however the edge is removed.
  425. else:
  426. n_er += 1
  427. n_ei = nx.number_of_edges(g2) - nb_edges2_cnted
  428. return n_vi, n_vr, n_vs, sod_vs, n_ei, n_er
  429. def get_nb_edit_operations_nonsymbolic(g1, g2, forward_map, backward_map,
  430. node_attrs=[], edge_attrs=[]):
  431. """Compute the number of each edit operations.
  432. """
  433. n_vi = 0
  434. n_vr = 0
  435. n_vs = 0
  436. sod_vs = 0
  437. n_ei = 0
  438. n_er = 0
  439. n_es = 0
  440. sod_es = 0
  441. nodes1 = [n for n in g1.nodes()]
  442. for i, map_i in enumerate(forward_map):
  443. if map_i == np.inf:
  444. n_vr += 1
  445. else:
  446. n_vs += 1
  447. sum_squares = 0
  448. for a_name in node_attrs:
  449. diff = float(g1.nodes[nodes1[i]][a_name]) - float(g2.nodes[map_i][a_name])
  450. sum_squares += np.square(diff)
  451. sod_vs += np.sqrt(sum_squares)
  452. for map_i in backward_map:
  453. if map_i == np.inf:
  454. n_vi += 1
  455. # idx_nodes1 = range(0, len(node1))
  456. edges1 = [e for e in g1.edges()]
  457. for n1, n2 in edges1:
  458. idx1 = nodes1.index(n1)
  459. idx2 = nodes1.index(n2)
  460. n1_g2 = forward_map[idx1]
  461. n2_g2 = forward_map[idx2]
  462. # one of the nodes is removed, thus the edge is removed.
  463. if n1_g2 == np.inf or n2_g2 == np.inf:
  464. n_er += 1
  465. # corresponding edge is in g2.
  466. elif (n1_g2, n2_g2) in g2.edges():
  467. n_es += 1
  468. sum_squares = 0
  469. for a_name in edge_attrs:
  470. diff = float(g1.edges[n1, n2][a_name]) - float(g2.edges[n1_g2, n2_g2][a_name])
  471. sum_squares += np.square(diff)
  472. sod_es += np.sqrt(sum_squares)
  473. elif (n2_g2, n1_g2) in g2.edges():
  474. n_es += 1
  475. sum_squares = 0
  476. for a_name in edge_attrs:
  477. diff = float(g1.edges[n2, n1][a_name]) - float(g2.edges[n2_g2, n1_g2][a_name])
  478. sum_squares += np.square(diff)
  479. sod_es += np.sqrt(sum_squares)
  480. # corresponding nodes are in g2, however the edge is removed.
  481. else:
  482. n_er += 1
  483. n_ei = nx.number_of_edges(g2) - n_es
  484. return n_vi, n_vr, sod_vs, n_ei, n_er, sod_es
  485. def ged_options_to_string(options):
  486. opt_str = ' '
  487. for key, val in options.items():
  488. if key == 'initialization_method':
  489. opt_str += '--initialization-method ' + str(val) + ' '
  490. elif key == 'initialization_options':
  491. opt_str += '--initialization-options ' + str(val) + ' '
  492. elif key == 'lower_bound_method':
  493. opt_str += '--lower-bound-method ' + str(val) + ' '
  494. elif key == 'random_substitution_ratio':
  495. opt_str += '--random-substitution-ratio ' + str(val) + ' '
  496. elif key == 'initial_solutions':
  497. opt_str += '--initial-solutions ' + str(val) + ' '
  498. elif key == 'ratio_runs_from_initial_solutions':
  499. opt_str += '--ratio-runs-from-initial-solutions ' + str(val) + ' '
  500. elif key == 'threads':
  501. opt_str += '--threads ' + str(val) + ' '
  502. elif key == 'num_randpost_loops':
  503. opt_str += '--num-randpost-loops ' + str(val) + ' '
  504. elif key == 'max_randpost_retrials':
  505. opt_str += '--maxrandpost-retrials ' + str(val) + ' '
  506. elif key == 'randpost_penalty':
  507. opt_str += '--randpost-penalty ' + str(val) + ' '
  508. elif key == 'randpost_decay':
  509. opt_str += '--randpost-decay ' + str(val) + ' '
  510. elif key == 'log':
  511. opt_str += '--log ' + str(val) + ' '
  512. elif key == 'randomness':
  513. opt_str += '--randomness ' + str(val) + ' '
  514. # if not isinstance(val, list):
  515. # opt_str += '--' + key.replace('_', '-') + ' '
  516. # if val == False:
  517. # val_str = 'FALSE'
  518. # else:
  519. # val_str = str(val)
  520. # opt_str += val_str + ' '
  521. return opt_str

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