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.

pathfrequency.py 7.1 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. Created on Wed Mar 20 10:12:15 2019
  5. inferring a graph grom path frequency.
  6. @author: ljia
  7. """
  8. #import numpy as np
  9. import networkx as nx
  10. from scipy.spatial.distance import hamming
  11. import itertools
  12. def SISF(K, v):
  13. if output:
  14. return output
  15. else:
  16. return 'no solution'
  17. def SISF_M(K, v):
  18. return output
  19. def GIPF_tree(v_obj, K=1, alphabet=[0, 1]):
  20. if K == 1:
  21. n_graph = v_obj[0] + v_obj[1]
  22. D_T, father_idx = getDynamicTable(n_graph, alphabet)
  23. # get the vector the closest to v_obj.
  24. if v_obj not in D_T:
  25. print('no exact solution')
  26. dis_lim = 1 / len(v_obj) # the possible shortest distance.
  27. dis_min = 1.0 # minimum proportional distance
  28. v_min = v_obj
  29. for vc in D_T:
  30. if vc[0] + vc[1] == n_graph:
  31. # print(vc)
  32. dis = hamming(vc, v_obj)
  33. if dis < dis_min:
  34. dis_min = dis
  35. v_min = vc
  36. if dis_min <= dis_lim:
  37. break
  38. v_obj = v_min
  39. # obtain required graph by traceback procedure.
  40. return getObjectGraph(v_obj, D_T, father_idx, alphabet), v_obj
  41. def GIPF_M(K, v):
  42. return G
  43. def getDynamicTable(n_graph, alphabet=[0, 1]):
  44. # init. When only one node exists.
  45. D_T = {(1, 0, 0, 0, 0, 0): 1, (0, 1, 0, 0, 0, 0): 1, (0, 0, 1, 0, 0, 0): 0,
  46. (0, 0, 0, 1, 0, 0): 0, (0, 0, 0, 0, 1, 0): 0, (0, 0, 0, 0, 0, 1): 0,}
  47. D_T = [(1, 0, 0, 0, 0, 0), (0, 1, 0, 0, 0, 0)]
  48. father_idx = [-1, -1] # index of each vector's father
  49. # add possible vectors.
  50. for idx, v in enumerate(D_T):
  51. if v[0] + v[1] < n_graph:
  52. D_T.append((v[0] + 1, v[1], v[2] + 2, v[3], v[4], v[5]))
  53. D_T.append((v[0] + 1, v[1], v[2], v[3] + 1, v[4] + 1, v[5]))
  54. D_T.append((v[0], v[1] + 1, v[2], v[3] + 1, v[4] + 1, v[5]))
  55. D_T.append((v[0], v[1] + 1, v[2], v[3], v[4], v[5] + 2))
  56. father_idx += [idx, idx, idx, idx]
  57. # D_T = itertools.chain([(1, 0, 0, 0, 0, 0)], [(0, 1, 0, 0, 0, 0)])
  58. # father_idx = itertools.chain([-1], [-1]) # index of each vector's father
  59. # # add possible vectors.
  60. # for idx, v in enumerate(D_T):
  61. # if v[0] + v[1] < n_graph:
  62. # D_T = itertools.chain(D_T, [(v[0] + 1, v[1], v[2] + 2, v[3], v[4], v[5])])
  63. # D_T = itertools.chain(D_T, [(v[0] + 1, v[1], v[2], v[3] + 1, v[4] + 1, v[5])])
  64. # D_T = itertools.chain(D_T, [(v[0], v[1] + 1, v[2], v[3] + 1, v[4] + 1, v[5])])
  65. # D_T = itertools.chain(D_T, [(v[0], v[1] + 1, v[2], v[3], v[4], v[5] + 2)])
  66. # father_idx = itertools.chain(father_idx, [idx, idx, idx, idx])
  67. return D_T, father_idx
  68. def getObjectGraph(v_obj, D_T, father_idx, alphabet=[0, 1]):
  69. g_obj = nx.Graph()
  70. # do vector traceback.
  71. v_tb = [list(v_obj)] # traceback vectors.
  72. v_tb_idx = [D_T.index(v_obj)] # indices of traceback vectors.
  73. while v_tb_idx[-1] > 1:
  74. idx_pre = father_idx[v_tb_idx[-1]]
  75. v_tb_idx.append(idx_pre)
  76. v_tb.append(list(D_T[idx_pre]))
  77. v_tb = v_tb[::-1] # reverse
  78. # v_tb_idx = v_tb_idx[::-1]
  79. # construct tree.
  80. v_c = v_tb[0] # current vector.
  81. if v_c[0] == 1:
  82. g_obj.add_node(0, node_label=alphabet[0])
  83. else:
  84. g_obj.add_node(0, node_label=alphabet[1])
  85. for vct in v_tb[1:]:
  86. if vct[0] - v_c[0] == 1:
  87. if vct[2] - v_c[2] == 2: # transfer 1
  88. label1 = alphabet[0]
  89. label2 = alphabet[0]
  90. else: # transfer 2
  91. label1 = alphabet[1]
  92. label2 = alphabet[0]
  93. else:
  94. if vct[3] - v_c[3] == 1: # transfer 3
  95. label1 = alphabet[0]
  96. label2 = alphabet[1]
  97. else: # transfer 4
  98. label1 = alphabet[1]
  99. label2 = alphabet[1]
  100. for nd, attr in g_obj.nodes(data=True):
  101. if attr['node_label'] == label1:
  102. nb_node = nx.number_of_nodes(g_obj)
  103. g_obj.add_node(nb_node, node_label=label2)
  104. g_obj.add_edge(nd, nb_node)
  105. break
  106. v_c = vct
  107. return g_obj
  108. import random
  109. def hierarchy_pos(G, root=None, width=1., vert_gap = 0.2, vert_loc = 0, xcenter = 0.5):
  110. '''
  111. From Joel's answer at https://stackoverflow.com/a/29597209/2966723.
  112. Licensed under Creative Commons Attribution-Share Alike
  113. If the graph is a tree this will return the positions to plot this in a
  114. hierarchical layout.
  115. G: the graph (must be a tree)
  116. root: the root node of current branch
  117. - if the tree is directed and this is not given,
  118. the root will be found and used
  119. - if the tree is directed and this is given, then
  120. the positions will be just for the descendants of this node.
  121. - if the tree is undirected and not given,
  122. then a random choice will be used.
  123. width: horizontal space allocated for this branch - avoids overlap with other branches
  124. vert_gap: gap between levels of hierarchy
  125. vert_loc: vertical location of root
  126. xcenter: horizontal location of root
  127. '''
  128. if not nx.is_tree(G):
  129. raise TypeError('cannot use hierarchy_pos on a graph that is not a tree')
  130. if root is None:
  131. if isinstance(G, nx.DiGraph):
  132. root = next(iter(nx.topological_sort(G))) #allows back compatibility with nx version 1.11
  133. else:
  134. root = random.choice(list(G.nodes))
  135. def _hierarchy_pos(G, root, width=1., vert_gap = 0.2, vert_loc = 0, xcenter = 0.5, pos = None, parent = None):
  136. '''
  137. see hierarchy_pos docstring for most arguments
  138. pos: a dict saying where all nodes go if they have been assigned
  139. parent: parent of this branch. - only affects it if non-directed
  140. '''
  141. if pos is None:
  142. pos = {root:(xcenter,vert_loc)}
  143. else:
  144. pos[root] = (xcenter, vert_loc)
  145. children = list(G.neighbors(root))
  146. if not isinstance(G, nx.DiGraph) and parent is not None:
  147. children.remove(parent)
  148. if len(children)!=0:
  149. dx = width/len(children)
  150. nextx = xcenter - width/2 - dx/2
  151. for child in children:
  152. nextx += dx
  153. pos = _hierarchy_pos(G,child, width = dx, vert_gap = vert_gap,
  154. vert_loc = vert_loc-vert_gap, xcenter=nextx,
  155. pos=pos, parent = root)
  156. return pos
  157. return _hierarchy_pos(G, root, width, vert_gap, vert_loc, xcenter)
  158. if __name__ == '__main__':
  159. v_obj = (6, 4, 10, 3, 3, 2)
  160. # v_obj = (6, 5, 10, 3, 3, 2)
  161. tree_obj, v_obj = GIPF_tree(v_obj)
  162. print('One closest vector is', v_obj)
  163. # plot
  164. pos = hierarchy_pos(tree_obj, 0)
  165. node_labels = nx.get_node_attributes(tree_obj, 'node_label')
  166. nx.draw(tree_obj, pos=pos, labels=node_labels, with_labels=True)

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