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.

median_preimage_generator_cml.py 48 kB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. Created on Tue Jun 16 16:04:46 2020
  5. @author: ljia
  6. """
  7. import numpy as np
  8. import time
  9. import random
  10. import multiprocessing
  11. import networkx as nx
  12. import cvxpy as cp
  13. import itertools
  14. from gklearn.preimage import PreimageGenerator
  15. from gklearn.preimage.utils import compute_k_dis
  16. from gklearn.ged.util import compute_geds_cml
  17. from gklearn.ged.env import GEDEnv
  18. from gklearn.ged.median import MedianGraphEstimatorPy
  19. from gklearn.ged.median import constant_node_costs, mge_options_to_string
  20. from gklearn.utils import Timer, SpecialLabel
  21. from gklearn.utils.utils import get_graph_kernel_by_name
  22. class MedianPreimageGeneratorCML(PreimageGenerator):
  23. """Generator median preimages by cost matrices learning using the pure Python version of GEDEnv. Works only for symbolic labeled graphs.
  24. """
  25. def __init__(self, dataset=None):
  26. PreimageGenerator.__init__(self, dataset=dataset)
  27. # arguments to set.
  28. self.__mge = None
  29. self.__ged_options = {}
  30. self.__mge_options = {}
  31. # self.__fit_method = 'k-graphs'
  32. self.__init_method = 'random'
  33. self.__init_ecc = None
  34. self.__parallel = True
  35. self.__n_jobs = multiprocessing.cpu_count()
  36. self.__ds_name = None
  37. self.__time_limit_in_sec = 0
  38. self.__max_itrs = 100
  39. self.__max_itrs_without_update = 3
  40. self.__epsilon_residual = 0.01
  41. self.__epsilon_ec = 0.1
  42. self.__allow_zeros = True
  43. # self.__triangle_rule = True
  44. # values to compute.
  45. self.__runtime_optimize_ec = None
  46. self.__runtime_generate_preimage = None
  47. self.__runtime_total = None
  48. self.__set_median = None
  49. self.__gen_median = None
  50. self.__best_from_dataset = None
  51. self.__sod_set_median = None
  52. self.__sod_gen_median = None
  53. self.__k_dis_set_median = None
  54. self.__k_dis_gen_median = None
  55. self.__k_dis_dataset = None
  56. self.__itrs = 0
  57. self.__converged = False
  58. self.__num_updates_ecc = 0
  59. self.__node_label_costs = None
  60. self.__edge_label_costs = None
  61. # values that can be set or to be computed.
  62. self.__edit_cost_constants = []
  63. self.__gram_matrix_unnorm = None
  64. self.__runtime_precompute_gm = None
  65. def set_options(self, **kwargs):
  66. self._kernel_options = kwargs.get('kernel_options', {})
  67. self._graph_kernel = kwargs.get('graph_kernel', None)
  68. self._verbose = kwargs.get('verbose', 2)
  69. self.__ged_options = kwargs.get('ged_options', {})
  70. self.__mge_options = kwargs.get('mge_options', {})
  71. # self.__fit_method = kwargs.get('fit_method', 'k-graphs')
  72. self.__init_method = kwargs.get('init_method', 'random')
  73. self.__init_ecc = kwargs.get('init_ecc', None)
  74. self.__edit_cost_constants = kwargs.get('edit_cost_constants', [])
  75. self.__parallel = kwargs.get('parallel', True)
  76. self.__n_jobs = kwargs.get('n_jobs', multiprocessing.cpu_count())
  77. self.__ds_name = kwargs.get('ds_name', None)
  78. self.__time_limit_in_sec = kwargs.get('time_limit_in_sec', 0)
  79. self.__max_itrs = kwargs.get('max_itrs', 100)
  80. self.__max_itrs_without_update = kwargs.get('max_itrs_without_update', 3)
  81. self.__epsilon_residual = kwargs.get('epsilon_residual', 0.01)
  82. self.__epsilon_ec = kwargs.get('epsilon_ec', 0.1)
  83. self.__gram_matrix_unnorm = kwargs.get('gram_matrix_unnorm', None)
  84. self.__runtime_precompute_gm = kwargs.get('runtime_precompute_gm', None)
  85. self.__allow_zeros = kwargs.get('allow_zeros', True)
  86. # self.__triangle_rule = kwargs.get('triangle_rule', True)
  87. def run(self):
  88. self._graph_kernel = get_graph_kernel_by_name(self._kernel_options['name'],
  89. node_labels=self._dataset.node_labels,
  90. edge_labels=self._dataset.edge_labels,
  91. node_attrs=self._dataset.node_attrs,
  92. edge_attrs=self._dataset.edge_attrs,
  93. ds_infos=self._dataset.get_dataset_infos(keys=['directed']),
  94. kernel_options=self._kernel_options)
  95. # record start time.
  96. start = time.time()
  97. # 1. precompute gram matrix.
  98. if self.__gram_matrix_unnorm is None:
  99. gram_matrix, run_time = self._graph_kernel.compute(self._dataset.graphs, **self._kernel_options)
  100. self.__gram_matrix_unnorm = self._graph_kernel.gram_matrix_unnorm
  101. end_precompute_gm = time.time()
  102. self.__runtime_precompute_gm = end_precompute_gm - start
  103. else:
  104. if self.__runtime_precompute_gm is None:
  105. raise Exception('Parameter "runtime_precompute_gm" must be given when using pre-computed Gram matrix.')
  106. self._graph_kernel.gram_matrix_unnorm = self.__gram_matrix_unnorm
  107. if self._kernel_options['normalize']:
  108. self._graph_kernel.gram_matrix = self._graph_kernel.normalize_gm(np.copy(self.__gram_matrix_unnorm))
  109. else:
  110. self._graph_kernel.gram_matrix = np.copy(self.__gram_matrix_unnorm)
  111. end_precompute_gm = time.time()
  112. start -= self.__runtime_precompute_gm
  113. # if self.__fit_method != 'k-graphs' and self.__fit_method != 'whole-dataset':
  114. # start = time.time()
  115. # self.__runtime_precompute_gm = 0
  116. # end_precompute_gm = start
  117. # 2. optimize edit cost constants.
  118. self.__optimize_edit_cost_vector()
  119. end_optimize_ec = time.time()
  120. self.__runtime_optimize_ec = end_optimize_ec - end_precompute_gm
  121. # 3. compute set median and gen median using optimized edit costs.
  122. if self._verbose >= 2:
  123. print('\nstart computing set median and gen median using optimized edit costs...\n')
  124. self.__gmg_bcu()
  125. end_generate_preimage = time.time()
  126. self.__runtime_generate_preimage = end_generate_preimage - end_optimize_ec
  127. self.__runtime_total = end_generate_preimage - start
  128. if self._verbose >= 2:
  129. print('medians computed.')
  130. print('SOD of the set median: ', self.__sod_set_median)
  131. print('SOD of the generalized median: ', self.__sod_gen_median)
  132. # 4. compute kernel distances to the true median.
  133. if self._verbose >= 2:
  134. print('\nstart computing distances to true median....\n')
  135. self.__compute_distances_to_true_median()
  136. # 5. print out results.
  137. if self._verbose:
  138. print()
  139. print('================================================================================')
  140. print('Finished generation of preimages.')
  141. print('--------------------------------------------------------------------------------')
  142. print('The optimized edit cost constants:', self.__edit_cost_constants)
  143. print('SOD of the set median:', self.__sod_set_median)
  144. print('SOD of the generalized median:', self.__sod_gen_median)
  145. print('Distance in kernel space for set median:', self.__k_dis_set_median)
  146. print('Distance in kernel space for generalized median:', self.__k_dis_gen_median)
  147. print('Minimum distance in kernel space for each graph in median set:', self.__k_dis_dataset)
  148. print('Time to pre-compute Gram matrix:', self.__runtime_precompute_gm)
  149. print('Time to optimize edit costs:', self.__runtime_optimize_ec)
  150. print('Time to generate pre-images:', self.__runtime_generate_preimage)
  151. print('Total time:', self.__runtime_total)
  152. print('Total number of iterations for optimizing:', self.__itrs)
  153. print('Total number of updating edit costs:', self.__num_updates_ecc)
  154. print('Is optimization of edit costs converged:', self.__converged)
  155. print('================================================================================')
  156. print()
  157. def get_results(self):
  158. results = {}
  159. results['edit_cost_constants'] = self.__edit_cost_constants
  160. results['runtime_precompute_gm'] = self.__runtime_precompute_gm
  161. results['runtime_optimize_ec'] = self.__runtime_optimize_ec
  162. results['runtime_generate_preimage'] = self.__runtime_generate_preimage
  163. results['runtime_total'] = self.__runtime_total
  164. results['sod_set_median'] = self.__sod_set_median
  165. results['sod_gen_median'] = self.__sod_gen_median
  166. results['k_dis_set_median'] = self.__k_dis_set_median
  167. results['k_dis_gen_median'] = self.__k_dis_gen_median
  168. results['k_dis_dataset'] = self.__k_dis_dataset
  169. results['itrs'] = self.__itrs
  170. results['converged'] = self.__converged
  171. results['num_updates_ecc'] = self.__num_updates_ecc
  172. results['mge'] = {}
  173. results['mge']['num_decrease_order'] = self.__mge.get_num_times_order_decreased()
  174. results['mge']['num_increase_order'] = self.__mge.get_num_times_order_increased()
  175. results['mge']['num_converged_descents'] = self.__mge.get_num_converged_descents()
  176. return results
  177. def __optimize_edit_cost_vector(self):
  178. """Learn edit cost vector.
  179. """
  180. if self.__init_method == 'random': # random
  181. # Get list of node labels.
  182. nls = self._dataset.get_all_node_labels()
  183. # Generate random costs.
  184. nb_nl = int((len(nls) * (len(nls) - 1)) / 2 + 2 * len(nls))
  185. rand_costs = random.sample(range(1, 10 * nb_nl + 1), nb_nl)
  186. self.__node_label_costs = np.zeros((len(nls) + 1, len(nls) + 1))
  187. # Initialize node label cost matrix, each row/column corresponds to a label, the first label is the dummy label. These is the same setting as in GEDData.
  188. i = 0
  189. # Costs of insertions.
  190. for row in range(1, len(nls) + 1):
  191. self.__node_label_costs[row, 0] = rand_costs[i]
  192. i += 1
  193. # Costs of deletions.
  194. for col in range(1, len(nls) + 1):
  195. self.__node_label_costs[0, col] = rand_costs[i]
  196. i += 1
  197. # Costs of substitutions.
  198. for row in range(1, len(nls) + 1):
  199. for col in range(row + 1, len(nls) + 1):
  200. self.__node_label_costs[row, col] = rand_costs[i]
  201. self.__node_label_costs[col, row] = rand_costs[i]
  202. i += 1
  203. # self.__node_label_costs = {}
  204. # for i, (nl1, nl2) in enumerate(itertools.combinations(nls, 2)):
  205. # self.__node_label_costs[(nl1, nl2)] = rand_costs[i]
  206. # # Add costs for deletion.
  207. # for j, nl in enumerate(nls):
  208. # self.__node_label_costs[(nl1, SpecialLabel.DUMMY)] = rand_costs[i + j]
  209. # # Add costs for insertion.
  210. # for k, nl in enumerate(nls):
  211. # self.__node_label_costs[(SpecialLabel.DUMMY, nl1)] = rand_costs[i + j + k]
  212. # # Add self costs.
  213. # for nl in nls:
  214. # self.__node_label_costs[(nl, nl)] = 0
  215. # self.__node_label_costs[(SpecialLabel.DUMMY, SpecialLabel.DUMMY)] = 0
  216. # Optimize edit cost matrices.
  217. self.__optimize_ecm_by_kernel_distances()
  218. elif self.__fit_method == 'random': # random
  219. if self.__ged_options['edit_cost'] == 'LETTER':
  220. self.__edit_cost_constants = random.sample(range(1, 1000), 3)
  221. self.__edit_cost_constants = [item * 0.001 for item in self.__edit_cost_constants]
  222. elif self.__ged_options['edit_cost'] == 'LETTER2':
  223. random.seed(time.time())
  224. self.__edit_cost_constants = random.sample(range(1, 1000), 5)
  225. self.__edit_cost_constants = [item * 0.01 for item in self.__edit_cost_constants]
  226. elif self.__ged_options['edit_cost'] == 'NON_SYMBOLIC':
  227. self.__edit_cost_constants = random.sample(range(1, 1000), 6)
  228. self.__edit_cost_constants = [item * 0.01 for item in self.__edit_cost_constants]
  229. if self._dataset.node_attrs == []:
  230. self.__edit_cost_constants[2] = 0
  231. if self._dataset.edge_attrs == []:
  232. self.__edit_cost_constants[5] = 0
  233. else:
  234. self.__edit_cost_constants = random.sample(range(1, 1000), 6)
  235. self.__edit_cost_constants = [item * 0.01 for item in self.__edit_cost_constants]
  236. if self._verbose >= 2:
  237. print('edit cost constants used:', self.__edit_cost_constants)
  238. elif self.__fit_method == 'expert': # expert
  239. if self.__init_ecc is None:
  240. if self.__ged_options['edit_cost'] == 'LETTER':
  241. self.__edit_cost_constants = [0.9, 1.7, 0.75]
  242. elif self.__ged_options['edit_cost'] == 'LETTER2':
  243. self.__edit_cost_constants = [0.675, 0.675, 0.75, 0.425, 0.425]
  244. else:
  245. self.__edit_cost_constants = [3, 3, 1, 3, 3, 1]
  246. else:
  247. self.__edit_cost_constants = self.__init_ecc
  248. elif self.__fit_method == 'k-graphs':
  249. if self.__init_ecc is None:
  250. if self.__ged_options['edit_cost'] == 'LETTER':
  251. self.__init_ecc = [0.9, 1.7, 0.75]
  252. elif self.__ged_options['edit_cost'] == 'LETTER2':
  253. self.__init_ecc = [0.675, 0.675, 0.75, 0.425, 0.425]
  254. elif self.__ged_options['edit_cost'] == 'NON_SYMBOLIC':
  255. self.__init_ecc = [0, 0, 1, 1, 1, 0]
  256. if self._dataset.node_attrs == []:
  257. self.__init_ecc[2] = 0
  258. if self._dataset.edge_attrs == []:
  259. self.__init_ecc[5] = 0
  260. else:
  261. self.__init_ecc = [3, 3, 1, 3, 3, 1]
  262. # optimize on the k-graph subset.
  263. self.__optimize_ecm_by_kernel_distances()
  264. elif self.__fit_method == 'whole-dataset':
  265. if self.__init_ecc is None:
  266. if self.__ged_options['edit_cost'] == 'LETTER':
  267. self.__init_ecc = [0.9, 1.7, 0.75]
  268. elif self.__ged_options['edit_cost'] == 'LETTER2':
  269. self.__init_ecc = [0.675, 0.675, 0.75, 0.425, 0.425]
  270. else:
  271. self.__init_ecc = [3, 3, 1, 3, 3, 1]
  272. # optimizeon the whole set.
  273. self.__optimize_ecc_by_kernel_distances()
  274. elif self.__fit_method == 'precomputed':
  275. pass
  276. def __optimize_ecm_by_kernel_distances(self):
  277. # compute distances in feature space.
  278. dis_k_mat, _, _, _ = self._graph_kernel.compute_distance_matrix()
  279. dis_k_vec = []
  280. for i in range(len(dis_k_mat)):
  281. # for j in range(i, len(dis_k_mat)):
  282. for j in range(i + 1, len(dis_k_mat)):
  283. dis_k_vec.append(dis_k_mat[i, j])
  284. dis_k_vec = np.array(dis_k_vec)
  285. # init ged.
  286. if self._verbose >= 2:
  287. print('\ninitial:')
  288. time0 = time.time()
  289. graphs = [self.__clean_graph(g) for g in self._dataset.graphs]
  290. self.__edit_cost_constants = self.__init_ecc
  291. options = self.__ged_options.copy()
  292. options['edit_cost_constants'] = self.__edit_cost_constants # @todo
  293. options['node_labels'] = self._dataset.node_labels
  294. options['edge_labels'] = self._dataset.edge_labels
  295. options['node_attrs'] = self._dataset.node_attrs
  296. options['edge_attrs'] = self._dataset.edge_attrs
  297. options['node_label_costs'] = self.__node_label_costs
  298. ged_vec_init, ged_mat, n_edit_operations = compute_geds_cml(graphs, options=options, parallel=self.__parallel, verbose=(self._verbose > 1))
  299. residual_list = [np.sqrt(np.sum(np.square(np.array(ged_vec_init) - dis_k_vec)))]
  300. time_list = [time.time() - time0]
  301. edit_cost_list = [self.__init_ecc]
  302. nb_cost_mat = np.array(n_edit_operations)
  303. nb_cost_mat_list = [nb_cost_mat]
  304. if self._verbose >= 2:
  305. print('Current edit cost constants:', self.__edit_cost_constants)
  306. print('Residual list:', residual_list)
  307. # run iteration from initial edit costs.
  308. self.__converged = False
  309. itrs_without_update = 0
  310. self.__itrs = 0
  311. self.__num_updates_ecc = 0
  312. timer = Timer(self.__time_limit_in_sec)
  313. while not self.__termination_criterion_met(self.__converged, timer, self.__itrs, itrs_without_update):
  314. if self._verbose >= 2:
  315. print('\niteration', self.__itrs + 1)
  316. time0 = time.time()
  317. # "fit" geds to distances in feature space by tuning edit costs using theLeast Squares Method.
  318. # np.savez('results/xp_fit_method/fit_data_debug' + str(self.__itrs) + '.gm',
  319. # nb_cost_mat=nb_cost_mat, dis_k_vec=dis_k_vec,
  320. # n_edit_operations=n_edit_operations, ged_vec_init=ged_vec_init,
  321. # ged_mat=ged_mat)
  322. self.__edit_cost_constants, _ = self.__update_ecc(nb_cost_mat, dis_k_vec)
  323. for i in range(len(self.__edit_cost_constants)):
  324. if -1e-9 <= self.__edit_cost_constants[i] <= 1e-9:
  325. self.__edit_cost_constants[i] = 0
  326. if self.__edit_cost_constants[i] < 0:
  327. raise ValueError('The edit cost is negative.')
  328. # for i in range(len(self.__edit_cost_constants)):
  329. # if self.__edit_cost_constants[i] < 0:
  330. # self.__edit_cost_constants[i] = 0
  331. # compute new GEDs and numbers of edit operations.
  332. options = self.__ged_options.copy() # np.array([self.__edit_cost_constants[0], self.__edit_cost_constants[1], 0.75])
  333. options['edit_cost_constants'] = self.__edit_cost_constants # @todo
  334. options['node_labels'] = self._dataset.node_labels
  335. options['edge_labels'] = self._dataset.edge_labels
  336. options['node_attrs'] = self._dataset.node_attrs
  337. options['edge_attrs'] = self._dataset.edge_attrs
  338. ged_vec, ged_mat, n_edit_operations = compute_geds_cml(graphs, options=options, parallel=self.__parallel, verbose=(self._verbose > 1))
  339. residual_list.append(np.sqrt(np.sum(np.square(np.array(ged_vec) - dis_k_vec))))
  340. time_list.append(time.time() - time0)
  341. edit_cost_list.append(self.__edit_cost_constants)
  342. nb_cost_mat = np.array(n_edit_operations)
  343. nb_cost_mat_list.append(nb_cost_mat)
  344. # check convergency.
  345. ec_changed = False
  346. for i, cost in enumerate(self.__edit_cost_constants):
  347. if cost == 0:
  348. if edit_cost_list[-2][i] > self.__epsilon_ec:
  349. ec_changed = True
  350. break
  351. elif abs(cost - edit_cost_list[-2][i]) / cost > self.__epsilon_ec:
  352. ec_changed = True
  353. break
  354. # if abs(cost - edit_cost_list[-2][i]) > self.__epsilon_ec:
  355. # ec_changed = True
  356. # break
  357. residual_changed = False
  358. if residual_list[-1] == 0:
  359. if residual_list[-2] > self.__epsilon_residual:
  360. residual_changed = True
  361. elif abs(residual_list[-1] - residual_list[-2]) / residual_list[-1] > self.__epsilon_residual:
  362. residual_changed = True
  363. self.__converged = not (ec_changed or residual_changed)
  364. if self.__converged:
  365. itrs_without_update += 1
  366. else:
  367. itrs_without_update = 0
  368. self.__num_updates_ecc += 1
  369. # print current states.
  370. if self._verbose >= 2:
  371. print()
  372. print('-------------------------------------------------------------------------')
  373. print('States of iteration', self.__itrs + 1)
  374. print('-------------------------------------------------------------------------')
  375. # print('Time spend:', self.__runtime_optimize_ec)
  376. print('Total number of iterations for optimizing:', self.__itrs + 1)
  377. print('Total number of updating edit costs:', self.__num_updates_ecc)
  378. print('Was optimization of edit costs converged:', self.__converged)
  379. print('Did edit costs change:', ec_changed)
  380. print('Did residual change:', residual_changed)
  381. print('Iterations without update:', itrs_without_update)
  382. print('Current edit cost constants:', self.__edit_cost_constants)
  383. print('Residual list:', residual_list)
  384. print('-------------------------------------------------------------------------')
  385. self.__itrs += 1
  386. def __termination_criterion_met(self, converged, timer, itr, itrs_without_update):
  387. if timer.expired() or (itr >= self.__max_itrs if self.__max_itrs >= 0 else False):
  388. # if self.__state == AlgorithmState.TERMINATED:
  389. # self.__state = AlgorithmState.INITIALIZED
  390. return True
  391. return converged or (itrs_without_update > self.__max_itrs_without_update if self.__max_itrs_without_update >= 0 else False)
  392. def __update_ecc(self, nb_cost_mat, dis_k_vec, rw_constraints='inequality'):
  393. # if self.__ds_name == 'Letter-high':
  394. if self.__ged_options['edit_cost'] == 'LETTER':
  395. raise Exception('Cannot compute for cost "LETTER".')
  396. pass
  397. # # method 1: set alpha automatically, just tune c_vir and c_eir by
  398. # # LMS using cvxpy.
  399. # alpha = 0.5
  400. # coeff = 100 # np.max(alpha * nb_cost_mat[:,4] / dis_k_vec)
  401. ## if np.count_nonzero(nb_cost_mat[:,4]) == 0:
  402. ## alpha = 0.75
  403. ## else:
  404. ## alpha = np.min([dis_k_vec / c_vs for c_vs in nb_cost_mat[:,4] if c_vs != 0])
  405. ## alpha = alpha * 0.99
  406. # param_vir = alpha * (nb_cost_mat[:,0] + nb_cost_mat[:,1])
  407. # param_eir = (1 - alpha) * (nb_cost_mat[:,4] + nb_cost_mat[:,5])
  408. # nb_cost_mat_new = np.column_stack((param_vir, param_eir))
  409. # dis_new = coeff * dis_k_vec - alpha * nb_cost_mat[:,3]
  410. #
  411. # x = cp.Variable(nb_cost_mat_new.shape[1])
  412. # cost = cp.sum_squares(nb_cost_mat_new * x - dis_new)
  413. # constraints = [x >= [0.0 for i in range(nb_cost_mat_new.shape[1])]]
  414. # prob = cp.Problem(cp.Minimize(cost), constraints)
  415. # prob.solve()
  416. # edit_costs_new = x.value
  417. # edit_costs_new = np.array([edit_costs_new[0], edit_costs_new[1], alpha])
  418. # residual = np.sqrt(prob.value)
  419. # # method 2: tune c_vir, c_eir and alpha by nonlinear programming by
  420. # # scipy.optimize.minimize.
  421. # w0 = nb_cost_mat[:,0] + nb_cost_mat[:,1]
  422. # w1 = nb_cost_mat[:,4] + nb_cost_mat[:,5]
  423. # w2 = nb_cost_mat[:,3]
  424. # w3 = dis_k_vec
  425. # func_min = lambda x: np.sum((w0 * x[0] * x[3] + w1 * x[1] * (1 - x[2]) \
  426. # + w2 * x[2] - w3 * x[3]) ** 2)
  427. # bounds = ((0, None), (0., None), (0.5, 0.5), (0, None))
  428. # res = minimize(func_min, [0.9, 1.7, 0.75, 10], bounds=bounds)
  429. # edit_costs_new = res.x[0:3]
  430. # residual = res.fun
  431. # method 3: tune c_vir, c_eir and alpha by nonlinear programming using cvxpy.
  432. # # method 4: tune c_vir, c_eir and alpha by QP function
  433. # # scipy.optimize.least_squares. An initial guess is required.
  434. # w0 = nb_cost_mat[:,0] + nb_cost_mat[:,1]
  435. # w1 = nb_cost_mat[:,4] + nb_cost_mat[:,5]
  436. # w2 = nb_cost_mat[:,3]
  437. # w3 = dis_k_vec
  438. # func = lambda x: (w0 * x[0] * x[3] + w1 * x[1] * (1 - x[2]) \
  439. # + w2 * x[2] - w3 * x[3]) ** 2
  440. # res = optimize.root(func, [0.9, 1.7, 0.75, 100])
  441. # edit_costs_new = res.x
  442. # residual = None
  443. elif self.__ged_options['edit_cost'] == 'LETTER2':
  444. # # 1. if c_vi != c_vr, c_ei != c_er.
  445. # nb_cost_mat_new = nb_cost_mat[:,[0,1,3,4,5]]
  446. # x = cp.Variable(nb_cost_mat_new.shape[1])
  447. # cost_fun = cp.sum_squares(nb_cost_mat_new @ x - dis_k_vec)
  448. ## # 1.1 no constraints.
  449. ## constraints = [x >= [0.0 for i in range(nb_cost_mat_new.shape[1])]]
  450. # # 1.2 c_vs <= c_vi + c_vr.
  451. # constraints = [x >= [0.0 for i in range(nb_cost_mat_new.shape[1])],
  452. # np.array([1.0, 1.0, -1.0, 0.0, 0.0]).T@x >= 0.0]
  453. ## # 2. if c_vi == c_vr, c_ei == c_er.
  454. ## nb_cost_mat_new = nb_cost_mat[:,[0,3,4]]
  455. ## nb_cost_mat_new[:,0] += nb_cost_mat[:,1]
  456. ## nb_cost_mat_new[:,2] += nb_cost_mat[:,5]
  457. ## x = cp.Variable(nb_cost_mat_new.shape[1])
  458. ## cost_fun = cp.sum_squares(nb_cost_mat_new @ x - dis_k_vec)
  459. ## # 2.1 no constraints.
  460. ## constraints = [x >= [0.0 for i in range(nb_cost_mat_new.shape[1])]]
  461. ### # 2.2 c_vs <= c_vi + c_vr.
  462. ### constraints = [x >= [0.0 for i in range(nb_cost_mat_new.shape[1])],
  463. ### np.array([2.0, -1.0, 0.0]).T@x >= 0.0]
  464. #
  465. # prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  466. # prob.solve()
  467. # edit_costs_new = [x.value[0], x.value[0], x.value[1], x.value[2], x.value[2]]
  468. # edit_costs_new = np.array(edit_costs_new)
  469. # residual = np.sqrt(prob.value)
  470. if not self.__triangle_rule and self.__allow_zeros:
  471. nb_cost_mat_new = nb_cost_mat[:,[0,1,3,4,5]]
  472. x = cp.Variable(nb_cost_mat_new.shape[1])
  473. cost_fun = cp.sum_squares(nb_cost_mat_new @ x - dis_k_vec)
  474. constraints = [x >= [0.0 for i in range(nb_cost_mat_new.shape[1])],
  475. np.array([1.0, 0.0, 0.0, 0.0, 0.0]).T@x >= 0.01,
  476. np.array([0.0, 1.0, 0.0, 0.0, 0.0]).T@x >= 0.01,
  477. np.array([0.0, 0.0, 0.0, 1.0, 0.0]).T@x >= 0.01,
  478. np.array([0.0, 0.0, 0.0, 0.0, 1.0]).T@x >= 0.01]
  479. prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  480. self.__execute_cvx(prob)
  481. edit_costs_new = x.value
  482. residual = np.sqrt(prob.value)
  483. elif self.__triangle_rule and self.__allow_zeros:
  484. nb_cost_mat_new = nb_cost_mat[:,[0,1,3,4,5]]
  485. x = cp.Variable(nb_cost_mat_new.shape[1])
  486. cost_fun = cp.sum_squares(nb_cost_mat_new @ x - dis_k_vec)
  487. constraints = [x >= [0.0 for i in range(nb_cost_mat_new.shape[1])],
  488. np.array([1.0, 0.0, 0.0, 0.0, 0.0]).T@x >= 0.01,
  489. np.array([0.0, 1.0, 0.0, 0.0, 0.0]).T@x >= 0.01,
  490. np.array([0.0, 0.0, 0.0, 1.0, 0.0]).T@x >= 0.01,
  491. np.array([0.0, 0.0, 0.0, 0.0, 1.0]).T@x >= 0.01,
  492. np.array([1.0, 1.0, -1.0, 0.0, 0.0]).T@x >= 0.0]
  493. prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  494. self.__execute_cvx(prob)
  495. edit_costs_new = x.value
  496. residual = np.sqrt(prob.value)
  497. elif not self.__triangle_rule and not self.__allow_zeros:
  498. nb_cost_mat_new = nb_cost_mat[:,[0,1,3,4,5]]
  499. x = cp.Variable(nb_cost_mat_new.shape[1])
  500. cost_fun = cp.sum_squares(nb_cost_mat_new @ x - dis_k_vec)
  501. constraints = [x >= [0.01 for i in range(nb_cost_mat_new.shape[1])]]
  502. prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  503. prob.solve()
  504. edit_costs_new = x.value
  505. residual = np.sqrt(prob.value)
  506. # elif method == 'inequality_modified':
  507. # # c_vs <= c_vi + c_vr.
  508. # nb_cost_mat_new = nb_cost_mat[:,[0,1,3,4,5]]
  509. # x = cp.Variable(nb_cost_mat_new.shape[1])
  510. # cost_fun = cp.sum_squares(nb_cost_mat_new @ x - dis_k_vec)
  511. # constraints = [x >= [0.0 for i in range(nb_cost_mat_new.shape[1])],
  512. # np.array([1.0, 1.0, -1.0, 0.0, 0.0]).T@x >= 0.0]
  513. # prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  514. # prob.solve()
  515. # # use same costs for insertion and removal rather than the fitted costs.
  516. # edit_costs_new = [x.value[0], x.value[0], x.value[1], x.value[2], x.value[2]]
  517. # edit_costs_new = np.array(edit_costs_new)
  518. # residual = np.sqrt(prob.value)
  519. elif self.__triangle_rule and not self.__allow_zeros:
  520. # c_vs <= c_vi + c_vr.
  521. nb_cost_mat_new = nb_cost_mat[:,[0,1,3,4,5]]
  522. x = cp.Variable(nb_cost_mat_new.shape[1])
  523. cost_fun = cp.sum_squares(nb_cost_mat_new @ x - dis_k_vec)
  524. constraints = [x >= [0.01 for i in range(nb_cost_mat_new.shape[1])],
  525. np.array([1.0, 1.0, -1.0, 0.0, 0.0]).T@x >= 0.0]
  526. prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  527. self.__execute_cvx(prob)
  528. edit_costs_new = x.value
  529. residual = np.sqrt(prob.value)
  530. elif rw_constraints == '2constraints': # @todo: rearrange it later.
  531. # c_vs <= c_vi + c_vr and c_vi == c_vr, c_ei == c_er.
  532. nb_cost_mat_new = nb_cost_mat[:,[0,1,3,4,5]]
  533. x = cp.Variable(nb_cost_mat_new.shape[1])
  534. cost_fun = cp.sum_squares(nb_cost_mat_new @ x - dis_k_vec)
  535. constraints = [x >= [0.01 for i in range(nb_cost_mat_new.shape[1])],
  536. np.array([1.0, 1.0, -1.0, 0.0, 0.0]).T@x >= 0.0,
  537. np.array([1.0, -1.0, 0.0, 0.0, 0.0]).T@x == 0.0,
  538. np.array([0.0, 0.0, 0.0, 1.0, -1.0]).T@x == 0.0]
  539. prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  540. prob.solve()
  541. edit_costs_new = x.value
  542. residual = np.sqrt(prob.value)
  543. elif self.__ged_options['edit_cost'] == 'NON_SYMBOLIC':
  544. is_n_attr = np.count_nonzero(nb_cost_mat[:,2])
  545. is_e_attr = np.count_nonzero(nb_cost_mat[:,5])
  546. if self.__ds_name == 'SYNTHETICnew': # @todo: rearrenge this later.
  547. # nb_cost_mat_new = nb_cost_mat[:,[0,1,2,3,4]]
  548. nb_cost_mat_new = nb_cost_mat[:,[2,3,4]]
  549. x = cp.Variable(nb_cost_mat_new.shape[1])
  550. cost_fun = cp.sum_squares(nb_cost_mat_new @ x - dis_k_vec)
  551. # constraints = [x >= [0.0 for i in range(nb_cost_mat_new.shape[1])],
  552. # np.array([0.0, 0.0, 0.0, 1.0, -1.0]).T@x == 0.0]
  553. # constraints = [x >= [0.0001 for i in range(nb_cost_mat_new.shape[1])]]
  554. constraints = [x >= [0.0001 for i in range(nb_cost_mat_new.shape[1])],
  555. np.array([0.0, 1.0, -1.0]).T@x == 0.0]
  556. prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  557. prob.solve()
  558. # print(x.value)
  559. edit_costs_new = np.concatenate((np.array([0.0, 0.0]), x.value,
  560. np.array([0.0])))
  561. residual = np.sqrt(prob.value)
  562. elif not self.__triangle_rule and self.__allow_zeros:
  563. if is_n_attr and is_e_attr:
  564. nb_cost_mat_new = nb_cost_mat[:,[0,1,2,3,4,5]]
  565. x = cp.Variable(nb_cost_mat_new.shape[1])
  566. cost_fun = cp.sum_squares(nb_cost_mat_new @ x - dis_k_vec)
  567. constraints = [x >= [0.0 for i in range(nb_cost_mat_new.shape[1])],
  568. np.array([1.0, 0.0, 0.0, 0.0, 0.0, 0.0]).T@x >= 0.01,
  569. np.array([0.0, 1.0, 0.0, 0.0, 0.0, 0.0]).T@x >= 0.01,
  570. np.array([0.0, 0.0, 0.0, 1.0, 0.0, 0.0]).T@x >= 0.01,
  571. np.array([0.0, 0.0, 0.0, 0.0, 1.0, 0.0]).T@x >= 0.01]
  572. prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  573. self.__execute_cvx(prob)
  574. edit_costs_new = x.value
  575. residual = np.sqrt(prob.value)
  576. elif is_n_attr and not is_e_attr:
  577. nb_cost_mat_new = nb_cost_mat[:,[0,1,2,3,4]]
  578. x = cp.Variable(nb_cost_mat_new.shape[1])
  579. cost_fun = cp.sum_squares(nb_cost_mat_new @ x - dis_k_vec)
  580. constraints = [x >= [0.0 for i in range(nb_cost_mat_new.shape[1])],
  581. np.array([1.0, 0.0, 0.0, 0.0, 0.0]).T@x >= 0.01,
  582. np.array([0.0, 1.0, 0.0, 0.0, 0.0]).T@x >= 0.01,
  583. np.array([0.0, 0.0, 0.0, 1.0, 0.0]).T@x >= 0.01,
  584. np.array([0.0, 0.0, 0.0, 0.0, 1.0]).T@x >= 0.01]
  585. prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  586. self.__execute_cvx(prob)
  587. edit_costs_new = np.concatenate((x.value, np.array([0.0])))
  588. residual = np.sqrt(prob.value)
  589. elif not is_n_attr and is_e_attr:
  590. nb_cost_mat_new = nb_cost_mat[:,[0,1,3,4,5]]
  591. x = cp.Variable(nb_cost_mat_new.shape[1])
  592. cost_fun = cp.sum_squares(nb_cost_mat_new @ x - dis_k_vec)
  593. constraints = [x >= [0.0 for i in range(nb_cost_mat_new.shape[1])],
  594. np.array([1.0, 0.0, 0.0, 0.0, 0.0]).T@x >= 0.01,
  595. np.array([0.0, 1.0, 0.0, 0.0, 0.0]).T@x >= 0.01,
  596. np.array([0.0, 0.0, 1.0, 0.0, 0.0]).T@x >= 0.01,
  597. np.array([0.0, 0.0, 0.0, 1.0, 0.0]).T@x >= 0.01]
  598. prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  599. self.__execute_cvx(prob)
  600. edit_costs_new = np.concatenate((x.value[0:2], np.array([0.0]), x.value[2:]))
  601. residual = np.sqrt(prob.value)
  602. else:
  603. nb_cost_mat_new = nb_cost_mat[:,[0,1,3,4]]
  604. x = cp.Variable(nb_cost_mat_new.shape[1])
  605. cost_fun = cp.sum_squares(nb_cost_mat_new @ x - dis_k_vec)
  606. constraints = [x >= [0.01 for i in range(nb_cost_mat_new.shape[1])]]
  607. prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  608. self.__execute_cvx(prob)
  609. edit_costs_new = np.concatenate((x.value[0:2], np.array([0.0]),
  610. x.value[2:], np.array([0.0])))
  611. residual = np.sqrt(prob.value)
  612. elif self.__triangle_rule and self.__allow_zeros:
  613. if is_n_attr and is_e_attr:
  614. nb_cost_mat_new = nb_cost_mat[:,[0,1,2,3,4,5]]
  615. x = cp.Variable(nb_cost_mat_new.shape[1])
  616. cost_fun = cp.sum_squares(nb_cost_mat_new @ x - dis_k_vec)
  617. constraints = [x >= [0.0 for i in range(nb_cost_mat_new.shape[1])],
  618. np.array([1.0, 0.0, 0.0, 0.0, 0.0, 0.0]).T@x >= 0.01,
  619. np.array([0.0, 1.0, 0.0, 0.0, 0.0, 0.0]).T@x >= 0.01,
  620. np.array([0.0, 0.0, 0.0, 1.0, 0.0, 0.0]).T@x >= 0.01,
  621. np.array([0.0, 0.0, 0.0, 0.0, 1.0, 0.0]).T@x >= 0.01,
  622. np.array([1.0, 1.0, -1.0, 0.0, 0.0, 0.0]).T@x >= 0.0,
  623. np.array([0.0, 0.0, 0.0, 1.0, 1.0, -1.0]).T@x >= 0.0]
  624. prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  625. self.__execute_cvx(prob)
  626. edit_costs_new = x.value
  627. residual = np.sqrt(prob.value)
  628. elif is_n_attr and not is_e_attr:
  629. nb_cost_mat_new = nb_cost_mat[:,[0,1,2,3,4]]
  630. x = cp.Variable(nb_cost_mat_new.shape[1])
  631. cost_fun = cp.sum_squares(nb_cost_mat_new @ x - dis_k_vec)
  632. constraints = [x >= [0.0 for i in range(nb_cost_mat_new.shape[1])],
  633. np.array([1.0, 0.0, 0.0, 0.0, 0.0]).T@x >= 0.01,
  634. np.array([0.0, 1.0, 0.0, 0.0, 0.0]).T@x >= 0.01,
  635. np.array([0.0, 0.0, 0.0, 1.0, 0.0]).T@x >= 0.01,
  636. np.array([0.0, 0.0, 0.0, 0.0, 1.0]).T@x >= 0.01,
  637. np.array([1.0, 1.0, -1.0, 0.0, 0.0]).T@x >= 0.0]
  638. prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  639. self.__execute_cvx(prob)
  640. edit_costs_new = np.concatenate((x.value, np.array([0.0])))
  641. residual = np.sqrt(prob.value)
  642. elif not is_n_attr and is_e_attr:
  643. nb_cost_mat_new = nb_cost_mat[:,[0,1,3,4,5]]
  644. x = cp.Variable(nb_cost_mat_new.shape[1])
  645. cost_fun = cp.sum_squares(nb_cost_mat_new @ x - dis_k_vec)
  646. constraints = [x >= [0.0 for i in range(nb_cost_mat_new.shape[1])],
  647. np.array([1.0, 0.0, 0.0, 0.0, 0.0]).T@x >= 0.01,
  648. np.array([0.0, 1.0, 0.0, 0.0, 0.0]).T@x >= 0.01,
  649. np.array([0.0, 0.0, 1.0, 0.0, 0.0]).T@x >= 0.01,
  650. np.array([0.0, 0.0, 0.0, 1.0, 0.0]).T@x >= 0.01,
  651. np.array([0.0, 0.0, 1.0, 1.0, -1.0]).T@x >= 0.0]
  652. prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  653. self.__execute_cvx(prob)
  654. edit_costs_new = np.concatenate((x.value[0:2], np.array([0.0]), x.value[2:]))
  655. residual = np.sqrt(prob.value)
  656. else:
  657. nb_cost_mat_new = nb_cost_mat[:,[0,1,3,4]]
  658. x = cp.Variable(nb_cost_mat_new.shape[1])
  659. cost_fun = cp.sum_squares(nb_cost_mat_new @ x - dis_k_vec)
  660. constraints = [x >= [0.01 for i in range(nb_cost_mat_new.shape[1])]]
  661. prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  662. self.__execute_cvx(prob)
  663. edit_costs_new = np.concatenate((x.value[0:2], np.array([0.0]),
  664. x.value[2:], np.array([0.0])))
  665. residual = np.sqrt(prob.value)
  666. elif not self.__triangle_rule and not self.__allow_zeros:
  667. if is_n_attr and is_e_attr:
  668. nb_cost_mat_new = nb_cost_mat[:,[0,1,2,3,4,5]]
  669. x = cp.Variable(nb_cost_mat_new.shape[1])
  670. cost_fun = cp.sum_squares(nb_cost_mat_new @ x - dis_k_vec)
  671. constraints = [x >= [0.01 for i in range(nb_cost_mat_new.shape[1])]]
  672. prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  673. self.__execute_cvx(prob)
  674. edit_costs_new = x.value
  675. residual = np.sqrt(prob.value)
  676. elif is_n_attr and not is_e_attr:
  677. nb_cost_mat_new = nb_cost_mat[:,[0,1,2,3,4]]
  678. x = cp.Variable(nb_cost_mat_new.shape[1])
  679. cost_fun = cp.sum_squares(nb_cost_mat_new @ x - dis_k_vec)
  680. constraints = [x >= [0.01 for i in range(nb_cost_mat_new.shape[1])]]
  681. prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  682. self.__execute_cvx(prob)
  683. edit_costs_new = np.concatenate((x.value, np.array([0.0])))
  684. residual = np.sqrt(prob.value)
  685. elif not is_n_attr and is_e_attr:
  686. nb_cost_mat_new = nb_cost_mat[:,[0,1,3,4,5]]
  687. x = cp.Variable(nb_cost_mat_new.shape[1])
  688. cost_fun = cp.sum_squares(nb_cost_mat_new @ x - dis_k_vec)
  689. constraints = [x >= [0.01 for i in range(nb_cost_mat_new.shape[1])]]
  690. prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  691. self.__execute_cvx(prob)
  692. edit_costs_new = np.concatenate((x.value[0:2], np.array([0.0]), x.value[2:]))
  693. residual = np.sqrt(prob.value)
  694. else:
  695. nb_cost_mat_new = nb_cost_mat[:,[0,1,3,4]]
  696. x = cp.Variable(nb_cost_mat_new.shape[1])
  697. cost_fun = cp.sum_squares(nb_cost_mat_new @ x - dis_k_vec)
  698. constraints = [x >= [0.01 for i in range(nb_cost_mat_new.shape[1])]]
  699. prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  700. self.__execute_cvx(prob)
  701. edit_costs_new = np.concatenate((x.value[0:2], np.array([0.0]),
  702. x.value[2:], np.array([0.0])))
  703. residual = np.sqrt(prob.value)
  704. elif self.__triangle_rule and not self.__allow_zeros:
  705. # c_vs <= c_vi + c_vr.
  706. if is_n_attr and is_e_attr:
  707. nb_cost_mat_new = nb_cost_mat[:,[0,1,2,3,4,5]]
  708. x = cp.Variable(nb_cost_mat_new.shape[1])
  709. cost_fun = cp.sum_squares(nb_cost_mat_new @ x - dis_k_vec)
  710. constraints = [x >= [0.01 for i in range(nb_cost_mat_new.shape[1])],
  711. np.array([1.0, 1.0, -1.0, 0.0, 0.0, 0.0]).T@x >= 0.0,
  712. np.array([0.0, 0.0, 0.0, 1.0, 1.0, -1.0]).T@x >= 0.0]
  713. prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  714. self.__execute_cvx(prob)
  715. edit_costs_new = x.value
  716. residual = np.sqrt(prob.value)
  717. elif is_n_attr and not is_e_attr:
  718. nb_cost_mat_new = nb_cost_mat[:,[0,1,2,3,4]]
  719. x = cp.Variable(nb_cost_mat_new.shape[1])
  720. cost_fun = cp.sum_squares(nb_cost_mat_new @ x - dis_k_vec)
  721. constraints = [x >= [0.01 for i in range(nb_cost_mat_new.shape[1])],
  722. np.array([1.0, 1.0, -1.0, 0.0, 0.0]).T@x >= 0.0]
  723. prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  724. self.__execute_cvx(prob)
  725. edit_costs_new = np.concatenate((x.value, np.array([0.0])))
  726. residual = np.sqrt(prob.value)
  727. elif not is_n_attr and is_e_attr:
  728. nb_cost_mat_new = nb_cost_mat[:,[0,1,3,4,5]]
  729. x = cp.Variable(nb_cost_mat_new.shape[1])
  730. cost_fun = cp.sum_squares(nb_cost_mat_new @ x - dis_k_vec)
  731. constraints = [x >= [0.01 for i in range(nb_cost_mat_new.shape[1])],
  732. np.array([0.0, 0.0, 1.0, 1.0, -1.0]).T@x >= 0.0]
  733. prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  734. self.__execute_cvx(prob)
  735. edit_costs_new = np.concatenate((x.value[0:2], np.array([0.0]), x.value[2:]))
  736. residual = np.sqrt(prob.value)
  737. else:
  738. nb_cost_mat_new = nb_cost_mat[:,[0,1,3,4]]
  739. x = cp.Variable(nb_cost_mat_new.shape[1])
  740. cost_fun = cp.sum_squares(nb_cost_mat_new @ x - dis_k_vec)
  741. constraints = [x >= [0.01 for i in range(nb_cost_mat_new.shape[1])]]
  742. prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  743. self.__execute_cvx(prob)
  744. edit_costs_new = np.concatenate((x.value[0:2], np.array([0.0]),
  745. x.value[2:], np.array([0.0])))
  746. residual = np.sqrt(prob.value)
  747. elif self.__ged_options['edit_cost'] == 'CONSTANT': # @todo: node/edge may not labeled.
  748. if not self.__triangle_rule and self.__allow_zeros:
  749. x = cp.Variable(nb_cost_mat.shape[1])
  750. cost_fun = cp.sum_squares(nb_cost_mat @ x - dis_k_vec)
  751. constraints = [x >= [0.0 for i in range(nb_cost_mat.shape[1])],
  752. np.array([1.0, 0.0, 0.0, 0.0, 0.0, 0.0]).T@x >= 0.01,
  753. np.array([0.0, 1.0, 0.0, 0.0, 0.0, 0.0]).T@x >= 0.01,
  754. np.array([0.0, 0.0, 0.0, 1.0, 0.0, 0.0]).T@x >= 0.01,
  755. np.array([0.0, 0.0, 0.0, 0.0, 1.0, 0.0]).T@x >= 0.01]
  756. prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  757. self.__execute_cvx(prob)
  758. edit_costs_new = x.value
  759. residual = np.sqrt(prob.value)
  760. elif self.__triangle_rule and self.__allow_zeros:
  761. x = cp.Variable(nb_cost_mat.shape[1])
  762. cost_fun = cp.sum_squares(nb_cost_mat @ x - dis_k_vec)
  763. constraints = [x >= [0.0 for i in range(nb_cost_mat.shape[1])],
  764. np.array([1.0, 0.0, 0.0, 0.0, 0.0, 0.0]).T@x >= 0.01,
  765. np.array([0.0, 1.0, 0.0, 0.0, 0.0, 0.0]).T@x >= 0.01,
  766. np.array([0.0, 0.0, 0.0, 1.0, 0.0, 0.0]).T@x >= 0.01,
  767. np.array([0.0, 0.0, 0.0, 0.0, 1.0, 0.0]).T@x >= 0.01,
  768. np.array([1.0, 1.0, -1.0, 0.0, 0.0, 0.0]).T@x >= 0.0,
  769. np.array([0.0, 0.0, 0.0, 1.0, 1.0, -1.0]).T@x >= 0.0]
  770. prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  771. self.__execute_cvx(prob)
  772. edit_costs_new = x.value
  773. residual = np.sqrt(prob.value)
  774. elif not self.__triangle_rule and not self.__allow_zeros:
  775. x = cp.Variable(nb_cost_mat.shape[1])
  776. cost_fun = cp.sum_squares(nb_cost_mat @ x - dis_k_vec)
  777. constraints = [x >= [0.01 for i in range(nb_cost_mat.shape[1])]]
  778. prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  779. self.__execute_cvx(prob)
  780. edit_costs_new = x.value
  781. residual = np.sqrt(prob.value)
  782. elif self.__triangle_rule and not self.__allow_zeros:
  783. x = cp.Variable(nb_cost_mat.shape[1])
  784. cost_fun = cp.sum_squares(nb_cost_mat @ x - dis_k_vec)
  785. constraints = [x >= [0.01 for i in range(nb_cost_mat.shape[1])],
  786. np.array([1.0, 1.0, -1.0, 0.0, 0.0, 0.0]).T@x >= 0.0,
  787. np.array([0.0, 0.0, 0.0, 1.0, 1.0, -1.0]).T@x >= 0.0]
  788. prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  789. self.__execute_cvx(prob)
  790. edit_costs_new = x.value
  791. residual = np.sqrt(prob.value)
  792. else:
  793. raise Exception('The edit cost "', self.__ged_options['edit_cost'], '" is not supported for update progress.')
  794. # # method 1: simple least square method.
  795. # edit_costs_new, residual, _, _ = np.linalg.lstsq(nb_cost_mat, dis_k_vec,
  796. # rcond=None)
  797. # # method 2: least square method with x_i >= 0.
  798. # edit_costs_new, residual = optimize.nnls(nb_cost_mat, dis_k_vec)
  799. # method 3: solve as a quadratic program with constraints.
  800. # P = np.dot(nb_cost_mat.T, nb_cost_mat)
  801. # q_T = -2 * np.dot(dis_k_vec.T, nb_cost_mat)
  802. # G = -1 * np.identity(nb_cost_mat.shape[1])
  803. # h = np.array([0 for i in range(nb_cost_mat.shape[1])])
  804. # A = np.array([1 for i in range(nb_cost_mat.shape[1])])
  805. # b = 1
  806. # x = cp.Variable(nb_cost_mat.shape[1])
  807. # prob = cp.Problem(cp.Minimize(cp.quad_form(x, P) + q_T@x),
  808. # [G@x <= h])
  809. # prob.solve()
  810. # edit_costs_new = x.value
  811. # residual = prob.value - np.dot(dis_k_vec.T, dis_k_vec)
  812. # G = -1 * np.identity(nb_cost_mat.shape[1])
  813. # h = np.array([0 for i in range(nb_cost_mat.shape[1])])
  814. x = cp.Variable(nb_cost_mat.shape[1])
  815. cost_fun = cp.sum_squares(nb_cost_mat @ x - dis_k_vec)
  816. constraints = [x >= [0.0 for i in range(nb_cost_mat.shape[1])],
  817. # np.array([1.0, 1.0, -1.0, 0.0, 0.0]).T@x >= 0.0]
  818. np.array([1.0, 1.0, -1.0, 0.0, 0.0, 0.0]).T@x >= 0.0,
  819. np.array([0.0, 0.0, 0.0, 1.0, 1.0, -1.0]).T@x >= 0.0]
  820. prob = cp.Problem(cp.Minimize(cost_fun), constraints)
  821. self.__execute_cvx(prob)
  822. edit_costs_new = x.value
  823. residual = np.sqrt(prob.value)
  824. # method 4:
  825. return edit_costs_new, residual
  826. def __execute_cvx(self, prob):
  827. try:
  828. prob.solve(verbose=(self._verbose>=2))
  829. except MemoryError as error0:
  830. if self._verbose >= 2:
  831. print('\nUsing solver "OSQP" caused a memory error.')
  832. print('the original error message is\n', error0)
  833. print('solver status: ', prob.status)
  834. print('trying solver "CVXOPT" instead...\n')
  835. try:
  836. prob.solve(solver=cp.CVXOPT, verbose=(self._verbose>=2))
  837. except Exception as error1:
  838. if self._verbose >= 2:
  839. print('\nAn error occured when using solver "CVXOPT".')
  840. print('the original error message is\n', error1)
  841. print('solver status: ', prob.status)
  842. print('trying solver "MOSEK" instead. Notice this solver is commercial and a lisence is required.\n')
  843. prob.solve(solver=cp.MOSEK, verbose=(self._verbose>=2))
  844. else:
  845. if self._verbose >= 2:
  846. print('solver status: ', prob.status)
  847. else:
  848. if self._verbose >= 2:
  849. print('solver status: ', prob.status)
  850. if self._verbose >= 2:
  851. print()
  852. def __gmg_bcu(self):
  853. """
  854. The local search algorithm based on block coordinate update (BCU) for estimating a generalized median graph (GMG).
  855. Returns
  856. -------
  857. None.
  858. """
  859. # Set up the ged environment.
  860. ged_env = GEDEnv() # @todo: maybe create a ged_env as a private varible.
  861. # gedlibpy.restart_env()
  862. ged_env.set_edit_cost(self.__ged_options['edit_cost'], edit_cost_constants=self.__edit_cost_constants)
  863. graphs = [self.__clean_graph(g) for g in self._dataset.graphs]
  864. for g in graphs:
  865. ged_env.add_nx_graph(g, '')
  866. graph_ids = ged_env.get_all_graph_ids()
  867. set_median_id = ged_env.add_graph('set_median')
  868. gen_median_id = ged_env.add_graph('gen_median')
  869. ged_env.init(init_type=self.__ged_options['init_option'])
  870. # Set up the madian graph estimator.
  871. self.__mge = MedianGraphEstimatorPy(ged_env, constant_node_costs(self.__ged_options['edit_cost']))
  872. self.__mge.set_refine_method(self.__ged_options['method'], self.__ged_options)
  873. options = self.__mge_options.copy()
  874. if not 'seed' in options:
  875. options['seed'] = int(round(time.time() * 1000)) # @todo: may not work correctly for possible parallel usage.
  876. options['parallel'] = self.__parallel
  877. # Select the GED algorithm.
  878. self.__mge.set_options(mge_options_to_string(options))
  879. self.__mge.set_label_names(node_labels=self._dataset.node_labels,
  880. edge_labels=self._dataset.edge_labels,
  881. node_attrs=self._dataset.node_attrs,
  882. edge_attrs=self._dataset.edge_attrs)
  883. ged_options = self.__ged_options.copy()
  884. if self.__parallel:
  885. ged_options['threads'] = 1
  886. self.__mge.set_init_method(ged_options['method'], ged_options)
  887. self.__mge.set_descent_method(ged_options['method'], ged_options)
  888. # Run the estimator.
  889. self.__mge.run(graph_ids, set_median_id, gen_median_id)
  890. # Get SODs.
  891. self.__sod_set_median = self.__mge.get_sum_of_distances('initialized')
  892. self.__sod_gen_median = self.__mge.get_sum_of_distances('converged')
  893. # Get median graphs.
  894. self.__set_median = ged_env.get_nx_graph(set_median_id)
  895. self.__gen_median = ged_env.get_nx_graph(gen_median_id)
  896. def __compute_distances_to_true_median(self):
  897. # compute distance in kernel space for set median.
  898. kernels_to_sm, _ = self._graph_kernel.compute(self.__set_median, self._dataset.graphs, **self._kernel_options)
  899. kernel_sm, _ = self._graph_kernel.compute(self.__set_median, self.__set_median, **self._kernel_options)
  900. if self._kernel_options['normalize']:
  901. kernels_to_sm = [kernels_to_sm[i] / np.sqrt(self.__gram_matrix_unnorm[i, i] * kernel_sm) for i in range(len(kernels_to_sm))] # normalize
  902. kernel_sm = 1
  903. # @todo: not correct kernel value
  904. gram_with_sm = np.concatenate((np.array([kernels_to_sm]), np.copy(self._graph_kernel.gram_matrix)), axis=0)
  905. gram_with_sm = np.concatenate((np.array([[kernel_sm] + kernels_to_sm]).T, gram_with_sm), axis=1)
  906. self.__k_dis_set_median = compute_k_dis(0, range(1, 1+len(self._dataset.graphs)),
  907. [1 / len(self._dataset.graphs)] * len(self._dataset.graphs),
  908. gram_with_sm, withterm3=False)
  909. # compute distance in kernel space for generalized median.
  910. kernels_to_gm, _ = self._graph_kernel.compute(self.__gen_median, self._dataset.graphs, **self._kernel_options)
  911. kernel_gm, _ = self._graph_kernel.compute(self.__gen_median, self.__gen_median, **self._kernel_options)
  912. if self._kernel_options['normalize']:
  913. kernels_to_gm = [kernels_to_gm[i] / np.sqrt(self.__gram_matrix_unnorm[i, i] * kernel_gm) for i in range(len(kernels_to_gm))] # normalize
  914. kernel_gm = 1
  915. gram_with_gm = np.concatenate((np.array([kernels_to_gm]), np.copy(self._graph_kernel.gram_matrix)), axis=0)
  916. gram_with_gm = np.concatenate((np.array([[kernel_gm] + kernels_to_gm]).T, gram_with_gm), axis=1)
  917. self.__k_dis_gen_median = compute_k_dis(0, range(1, 1+len(self._dataset.graphs)),
  918. [1 / len(self._dataset.graphs)] * len(self._dataset.graphs),
  919. gram_with_gm, withterm3=False)
  920. # compute distance in kernel space for each graph in median set.
  921. k_dis_median_set = []
  922. for idx in range(len(self._dataset.graphs)):
  923. k_dis_median_set.append(compute_k_dis(idx+1, range(1, 1+len(self._dataset.graphs)),
  924. [1 / len(self._dataset.graphs)] * len(self._dataset.graphs),
  925. gram_with_gm, withterm3=False))
  926. idx_k_dis_median_set_min = np.argmin(k_dis_median_set)
  927. self.__k_dis_dataset = k_dis_median_set[idx_k_dis_median_set_min]
  928. self.__best_from_dataset = self._dataset.graphs[idx_k_dis_median_set_min].copy()
  929. if self._verbose >= 2:
  930. print()
  931. print('distance in kernel space for set median:', self.__k_dis_set_median)
  932. print('distance in kernel space for generalized median:', self.__k_dis_gen_median)
  933. print('minimum distance in kernel space for each graph in median set:', self.__k_dis_dataset)
  934. print('distance in kernel space for each graph in median set:', k_dis_median_set)
  935. # def __clean_graph(self, G, node_labels=[], edge_labels=[], node_attrs=[], edge_attrs=[]):
  936. def __clean_graph(self, G): # @todo: this may not be needed when datafile is updated.
  937. """
  938. Cleans node and edge labels and attributes of the given graph.
  939. """
  940. G_new = nx.Graph(**G.graph)
  941. for nd, attrs in G.nodes(data=True):
  942. G_new.add_node(str(nd)) # @todo: should we keep this as str()?
  943. for l_name in self._dataset.node_labels:
  944. G_new.nodes[str(nd)][l_name] = str(attrs[l_name])
  945. for a_name in self._dataset.node_attrs:
  946. G_new.nodes[str(nd)][a_name] = str(attrs[a_name])
  947. for nd1, nd2, attrs in G.edges(data=True):
  948. G_new.add_edge(str(nd1), str(nd2))
  949. for l_name in self._dataset.edge_labels:
  950. G_new.edges[str(nd1), str(nd2)][l_name] = str(attrs[l_name])
  951. for a_name in self._dataset.edge_attrs:
  952. G_new.edges[str(nd1), str(nd2)][a_name] = str(attrs[a_name])
  953. return G_new
  954. @property
  955. def mge(self):
  956. return self.__mge
  957. @property
  958. def ged_options(self):
  959. return self.__ged_options
  960. @ged_options.setter
  961. def ged_options(self, value):
  962. self.__ged_options = value
  963. @property
  964. def mge_options(self):
  965. return self.__mge_options
  966. @mge_options.setter
  967. def mge_options(self, value):
  968. self.__mge_options = value
  969. @property
  970. def fit_method(self):
  971. return self.__fit_method
  972. @fit_method.setter
  973. def fit_method(self, value):
  974. self.__fit_method = value
  975. @property
  976. def init_ecc(self):
  977. return self.__init_ecc
  978. @init_ecc.setter
  979. def init_ecc(self, value):
  980. self.__init_ecc = value
  981. @property
  982. def set_median(self):
  983. return self.__set_median
  984. @property
  985. def gen_median(self):
  986. return self.__gen_median
  987. @property
  988. def best_from_dataset(self):
  989. return self.__best_from_dataset
  990. @property
  991. def gram_matrix_unnorm(self):
  992. return self.__gram_matrix_unnorm
  993. @gram_matrix_unnorm.setter
  994. def gram_matrix_unnorm(self, value):
  995. self.__gram_matrix_unnorm = value

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