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.

lsape_based_method.py 8.7 kB

5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. Created on Thu Jun 18 16:01:24 2020
  5. @author: ljia
  6. """
  7. import numpy as np
  8. import networkx as nx
  9. from gklearn.ged.methods import GEDMethod
  10. from gklearn.ged.util import LSAPESolver, misc
  11. from gklearn.ged.env import NodeMap
  12. class LSAPEBasedMethod(GEDMethod):
  13. def __init__(self, ged_data):
  14. super().__init__(ged_data)
  15. self._lsape_model = None # @todo: LSAPESolver::ECBP
  16. self._greedy_method = None # @todo: LSAPESolver::BASIC
  17. self._compute_lower_bound = True
  18. self._solve_optimally = True
  19. self._num_threads = 1
  20. self._centrality_method = 'NODE' # @todo
  21. self._centrality_weight = 0.7
  22. self._centralities = {}
  23. self._max_num_solutions = 1
  24. def populate_instance_and_run_as_util(self, g, h): #, lsape_instance):
  25. """
  26. /*!
  27. * @brief Runs the method with options specified by set_options() and provides access to constructed LSAPE instance.
  28. * @param[in] g Input graph.
  29. * @param[in] h Input graph.
  30. * @param[out] result Result variable.
  31. * @param[out] lsape_instance LSAPE instance.
  32. */
  33. """
  34. result = {'node_maps': [], 'lower_bound': 0, 'upper_bound': np.inf}
  35. # Populate the LSAPE instance and set up the solver.
  36. nb1, nb2 = nx.number_of_nodes(g), nx.number_of_nodes(h)
  37. lsape_instance = np.ones((nb1 + nb2, nb1 + nb2)) * np.inf
  38. # lsape_instance = np.empty((nx.number_of_nodes(g) + 1, nx.number_of_nodes(h) + 1))
  39. self.populate_instance(g, h, lsape_instance)
  40. # nb1, nb2 = nx.number_of_nodes(g), nx.number_of_nodes(h)
  41. # lsape_instance_new = np.empty((nb1 + nb2, nb1 + nb2)) * np.inf
  42. # lsape_instance_new[nb1:, nb2:] = 0
  43. # lsape_instance_new[0:nb1, 0:nb2] = lsape_instance[0:nb1, 0:nb2]
  44. # for i in range(nb1): # all u's neighbor
  45. # lsape_instance_new[i, nb2 + i] = lsape_instance[i, nb2]
  46. # for i in range(nb2): # all u's neighbor
  47. # lsape_instance_new[nb1 + i, i] = lsape_instance[nb2, i]
  48. # lsape_solver = LSAPESolver(lsape_instance_new)
  49. lsape_solver = LSAPESolver(lsape_instance)
  50. # Solve the LSAPE instance.
  51. if self._solve_optimally:
  52. lsape_solver.set_model(self._lsape_model)
  53. else:
  54. lsape_solver.set_greedy_method(self._greedy_method)
  55. lsape_solver.solve(self._max_num_solutions)
  56. # Compute and store lower and upper bound.
  57. if self._compute_lower_bound and self._solve_optimally:
  58. result['lower_bound'] = lsape_solver.minimal_cost() * self._lsape_lower_bound_scaling_factor(g, h) # @todo: test
  59. for solution_id in range(0, lsape_solver.num_solutions()):
  60. result['node_maps'].append(NodeMap(nx.number_of_nodes(g), nx.number_of_nodes(h)))
  61. misc.construct_node_map_from_solver(lsape_solver, result['node_maps'][-1], solution_id)
  62. self._ged_data.compute_induced_cost(g, h, result['node_maps'][-1])
  63. # Add centralities and reoptimize.
  64. if self._centrality_weight > 0 and self._centrality_method != 'NODE':
  65. print('This is not implemented.')
  66. pass # @todo
  67. # Sort the node maps and set the upper bound.
  68. if len(result['node_maps']) > 1 or len(result['node_maps']) > self._max_num_solutions:
  69. print('This is not implemented.') # @todo:
  70. pass
  71. if len(result['node_maps']) == 0:
  72. result['upper_bound'] = np.inf
  73. else:
  74. result['upper_bound'] = result['node_maps'][0].induced_cost()
  75. return result
  76. def populate_instance(self, g, h, lsape_instance):
  77. """
  78. /*!
  79. * @brief Populates the LSAPE instance.
  80. * @param[in] g Input graph.
  81. * @param[in] h Input graph.
  82. * @param[out] lsape_instance LSAPE instance.
  83. */
  84. """
  85. if not self._initialized:
  86. pass
  87. # @todo: if (not this->initialized_) {
  88. self._lsape_populate_instance(g, h, lsape_instance)
  89. lsape_instance[nx.number_of_nodes(g):, nx.number_of_nodes(h):] = 0
  90. # lsape_instance[nx.number_of_nodes(g), nx.number_of_nodes(h)] = 0
  91. ###########################################################################
  92. # Member functions inherited from GEDMethod.
  93. ###########################################################################
  94. def _ged_init(self):
  95. self._lsape_pre_graph_init(False)
  96. for graph in self._ged_data._graphs:
  97. self._init_graph(graph)
  98. self._lsape_init()
  99. def _ged_run(self, g, h):
  100. # lsape_instance = np.empty((0, 0))
  101. result = self.populate_instance_and_run_as_util(g, h) # , lsape_instance)
  102. return result
  103. def _ged_parse_option(self, option, arg):
  104. is_valid_option = False
  105. if option == 'threads': # @todo: try.. catch...
  106. self._num_threads = arg
  107. is_valid_option = True
  108. elif option == 'lsape_model':
  109. self._lsape_model = arg # @todo
  110. is_valid_option = True
  111. elif option == 'greedy_method':
  112. self._greedy_method = arg # @todo
  113. is_valid_option = True
  114. elif option == 'optimal':
  115. self._solve_optimally = arg # @todo
  116. is_valid_option = True
  117. elif option == 'centrality_method':
  118. self._centrality_method = arg # @todo
  119. is_valid_option = True
  120. elif option == 'centrality_weight':
  121. self._centrality_weight = arg # @todo
  122. is_valid_option = True
  123. elif option == 'max_num_solutions':
  124. if arg == 'ALL':
  125. self._max_num_solutions = -1
  126. else:
  127. self._max_num_solutions = arg # @todo
  128. is_valid_option = True
  129. is_valid_option = is_valid_option or self._lsape_parse_option(option, arg)
  130. is_valid_option = True # @todo: this is not in the C++ code.
  131. return is_valid_option
  132. def _ged_set_default_options(self):
  133. self._lsape_model = None # @todo: LSAPESolver::ECBP
  134. self._greedy_method = None # @todo: LSAPESolver::BASIC
  135. self._solve_optimally = True
  136. self._num_threads = 1
  137. self._centrality_method = 'NODE' # @todo
  138. self._centrality_weight = 0.7
  139. self._max_num_solutions = 1
  140. ###########################################################################
  141. # Private helper member functions.
  142. ###########################################################################
  143. def _init_graph(self, graph):
  144. if self._centrality_method != 'NODE':
  145. self._init_centralities(graph) # @todo
  146. self._lsape_init_graph(graph)
  147. ###########################################################################
  148. # Virtual member functions to be overridden by derived classes.
  149. ###########################################################################
  150. def _lsape_init(self):
  151. """
  152. /*!
  153. * @brief Initializes the method after initializing the global variables for the graphs.
  154. * @note Must be overridden by derived classes of ged::LSAPEBasedMethod that require custom initialization.
  155. */
  156. """
  157. pass
  158. def _lsape_parse_option(self, option, arg):
  159. """
  160. /*!
  161. * @brief Parses one option that is not among the ones shared by all derived classes of ged::LSAPEBasedMethod.
  162. * @param[in] option The name of the option.
  163. * @param[in] arg The argument of the option.
  164. * @return Returns true if @p option is a valid option name for the method and false otherwise.
  165. * @note Must be overridden by derived classes of ged::LSAPEBasedMethod that have options that are not among the ones shared by all derived classes of ged::LSAPEBasedMethod.
  166. */
  167. """
  168. return False
  169. def _lsape_set_default_options(self):
  170. """
  171. /*!
  172. * @brief Sets all options that are not among the ones shared by all derived classes of ged::LSAPEBasedMethod to default values.
  173. * @note Must be overridden by derived classes of ged::LSAPEBasedMethod that have options that are not among the ones shared by all derived classes of ged::LSAPEBasedMethod.
  174. */
  175. """
  176. pass
  177. def _lsape_populate_instance(self, g, h, lsape_instance):
  178. """
  179. /*!
  180. * @brief Populates the LSAPE instance.
  181. * @param[in] g Input graph.
  182. * @param[in] h Input graph.
  183. * @param[out] lsape_instance LSAPE instance of size (n + 1) x (m + 1), where n and m are the number of nodes in @p g and @p h. The last row and the last column represent insertion and deletion.
  184. * @note Must be overridden by derived classes of ged::LSAPEBasedMethod.
  185. */
  186. """
  187. pass
  188. def _lsape_init_graph(self, graph):
  189. """
  190. /*!
  191. * @brief Initializes global variables for one graph.
  192. * @param[in] graph Graph for which the global variables have to be initialized.
  193. * @note Must be overridden by derived classes of ged::LSAPEBasedMethod that require to initialize custom global variables.
  194. */
  195. """
  196. pass
  197. def _lsape_pre_graph_init(self, called_at_runtime):
  198. """
  199. /*!
  200. * @brief Initializes the method at runtime or during initialization before initializing the global variables for the graphs.
  201. * @param[in] called_at_runtime Equals @p true if called at runtime and @p false if called during initialization.
  202. * @brief Must be overridden by derived classes of ged::LSAPEBasedMethod that require default initialization at runtime before initializing the global variables for the graphs.
  203. */
  204. """
  205. pass

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