GEDLIB  1.0
ls_based_method.ipp
Go to the documentation of this file.
1 /***************************************************************************
2  * *
3  * Copyright (C) 2018 by David B. Blumenthal *
4  * *
5  * This file is part of GEDLIB. *
6  * *
7  * GEDLIB is free software: you can redistribute it and/or modify it *
8  * under the terms of the GNU Lesser General Public License as published *
9  * by the Free Software Foundation, either version 3 of the License, or *
10  * (at your option) any later version. *
11  * *
12  * GEDLIB is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU Lesser General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU Lesser General Public *
18  * License along with GEDLIB. If not, see <http://www.gnu.org/licenses/>. *
19  * *
20  ***************************************************************************/
21 
27 #ifndef SRC_METHODS_LS_BASED_METHOD_IPP_
28 #define SRC_METHODS_LS_BASED_METHOD_IPP_
29 
30 namespace ged {
31 
32 // === Definitions of destructor and constructor. ===
33 template<class UserNodeLabel, class UserEdgeLabel>
36  delete initialization_method_;
37  delete lower_bound_method_;
38 }
39 
40 template<class UserNodeLabel, class UserEdgeLabel>
43 GEDMethod<UserNodeLabel, UserEdgeLabel>(ged_data),
44 num_threads_{1},
45 initialization_method_{nullptr},
46 initialization_options_(""),
47 lower_bound_method_{nullptr},
48 lower_bound_method_options_(""),
49 random_substitution_ratio_{1.0},
50 num_initial_solutions_{1},
51 ratio_runs_from_initial_solutions_{1.0},
52 num_randpost_loops_{0},
53 max_randpost_retrials_{10},
54 randpost_penalty_{0.0},
55 randpost_decay_{1.0},
56 logfile_name_(""),
57 use_real_randomness_{true} {}
58 
59 // === Definitions of member functions inherited from GEDMethod. ===
60 template<class UserNodeLabel, class UserEdgeLabel>
61 void
64  if (initialization_method_) {
65  initialization_method_->init();
66  }
67  if (lower_bound_method_) {
68  lower_bound_method_->init();
69  }
70  ls_init_();
71 }
72 
73 template<class UserNodeLabel, class UserEdgeLabel>
74 void
76 ged_run_(const GEDGraph & g, const GEDGraph & h, Result & result) {
77 
78  // Initialize the method for the run between g and h.
79  ls_runtime_init_(g, h);
80  // Generate the initial node maps and allocate output node maps.
81  std::vector<NodeMap> initial_node_maps;
82  std::vector<NodeMap> result_node_maps;
83  std::vector<NodeMap> visited_node_maps;
84  double upper_bound{std::numeric_limits<double>::infinity()};
85  double lower_bound{0.0};
86  std::vector<std::vector<double>> counts_matrix(g.num_nodes(), std::vector<double>(h.num_nodes() + 1, 0.0));
87  double skewdness_counts_matrix{1.0};
88  generate_initial_node_maps_(g, h, initial_node_maps, result);
89  for (std::size_t node_map_id = 0; node_map_id < initial_node_maps.size(); node_map_id++) {
90  result_node_maps.emplace_back(g.num_nodes(), h.num_nodes());
91  visited_node_maps.emplace_back(initial_node_maps.at(node_map_id));
92  //result.add_node_map(g.num_nodes(), h.num_nodes());
93  }
94 
95  // Initialize lower bound.
96  if (lower_bound_method_) {
97  Result lower_bound_result;
98  lower_bound_method_->run_as_util(g, h, lower_bound_result);
99  lower_bound = std::max(result.lower_bound(), lower_bound_result.lower_bound());
100  result.set_lower_bound(lower_bound);
101  }
102 
103  // Parallelly run local searches starting at the initial node maps. Stop if the optimal solution has been found or if the desired number of terminated runs has been reached.
104  bool found_optimum{false};
105  std::size_t num_runs_from_initial_solutions{num_runs_from_initial_solutions_()};
106  for (std::size_t loop{0}; loop <= num_randpost_loops_; loop++) {
107  if (found_optimum) {
108  break;
109  }
110  if (loop > 0) {
111  for (NodeMap & node_map : initial_node_maps) {
112  node_map.clear();
113  }
114  generate_node_maps_from_counts_matrix_(g,h,counts_matrix, visited_node_maps, initial_node_maps);
115  }
116  double former_upper_bound = upper_bound;
117  std::size_t terminated_runs{0};
118  std::vector<bool> is_converged_node_map(initial_node_maps.size(), false);
119 #ifdef _OPENMP
120  omp_set_num_threads(num_threads_ - 1);
121 #pragma omp parallel for if(num_threads_ > 1) schedule(dynamic)
122 #endif
123  for (std::size_t node_map_id = 0; node_map_id < initial_node_maps.size(); node_map_id++) {
124  if (not found_optimum and (terminated_runs < num_runs_from_initial_solutions)) {
125  ls_run_from_initial_solution_(g, h, result.lower_bound(), initial_node_maps.at(node_map_id), result_node_maps.at(node_map_id));
126 #pragma omp critical
127  {
128  is_converged_node_map[node_map_id] = true;
129  upper_bound = std::min(upper_bound, result_node_maps.at(node_map_id).induced_cost());
130  found_optimum = (found_optimum or (result.lower_bound() >= upper_bound));
131  terminated_runs++;
132  }
133  }
134  }
135  if (logfile_name_!="" and loop !=0) {
136  std::ofstream result_file(logfile_name_.c_str(), std::ios_base::app);
137  result_file << g.id() << "," << h.id() << "," << loop << "," << skewdness_counts_matrix << "," << (former_upper_bound - upper_bound)/former_upper_bound << "\n" ;
138  result_file.close();
139  }
140  if (not found_optimum and loop < num_randpost_loops_) {
141  skewdness_counts_matrix = update_counts_matrix_and_visited_node_maps_(result_node_maps, is_converged_node_map, upper_bound, lower_bound, visited_node_maps, loop, counts_matrix);
142  }
143  for (NodeMap & node_map : result_node_maps) {
144  result.add_node_map(node_map);
145  node_map.clear();
146  }
147  }
148 
149  // Determine the best node map.
150  result.sort_node_maps_and_set_upper_bound(num_runs_from_initial_solutions_());
151 
152 }
153 
154 template<class UserNodeLabel, class UserEdgeLabel>
155 bool
157 ged_parse_option_(const std::string & option, const std::string & arg) {
158  bool is_valid_option{false};
159  if (option == "initialization-method") {
160  if (arg == "BIPARTITE_ML") {
161  initialization_method_ = new BipartiteML<UserNodeLabel, UserEdgeLabel>(this->ged_data_);
162  }
163  else if (arg == "BIPARTITE") {
164  initialization_method_ = new Bipartite<UserNodeLabel, UserEdgeLabel>(this->ged_data_);
165  }
166  else if (arg == "BRANCH_FAST") {
167  initialization_method_ = new BranchFast<UserNodeLabel, UserEdgeLabel>(this->ged_data_);;
168  }
169  else if (arg == "BRANCH_UNIFORM") {
170  initialization_method_ = new BranchUniform<UserNodeLabel, UserEdgeLabel>(this->ged_data_);
171  }
172  else if (arg == "BRANCH") {
173  initialization_method_ = new Branch<UserNodeLabel, UserEdgeLabel>(this->ged_data_);
174  }
175  else if (arg == "NODE") {
176  initialization_method_ = new Node<UserNodeLabel, UserEdgeLabel>(this->ged_data_);
177  }
178  else if (arg == "RING_ML") {
179  initialization_method_ = new RingML<UserNodeLabel, UserEdgeLabel>(this->ged_data_);
180  }
181  else if (arg == "RING") {
182  initialization_method_ = new Ring<UserNodeLabel, UserEdgeLabel>(this->ged_data_);
183  }
184  else if (arg == "SUBGRAPH") {
185  initialization_method_ = new Subgraph<UserNodeLabel, UserEdgeLabel>(this->ged_data_);
186  }
187  else if (arg == "WALKS") {
188  initialization_method_ = new Walks<UserNodeLabel, UserEdgeLabel>(this->ged_data_);
189  }
190  else if (arg != "RANDOM") {
191  throw Error(std::string("Invalid argument \"") + arg + "\" for option initialization-method. Usage: options = \"[--initialization-method BIPARTITE_ML|BIPARTITE|BRANCH_FAST|BRANCH_UNIFORM|BRANCH|NODE|RING_ML|RING|SUBGRAPH|WALKS|RANDOM] [...]\"");
192  }
193  is_valid_option = true;
194  }
195  else if (option == "randomness") {
196  if (arg == "PSEUDO") {
197  use_real_randomness_ = false;
198  }
199  else if (arg != "REAL") {
200  throw Error(std::string("Invalid argument \"") + arg + "\" for option randomness. Usage: options = \"[--randomness REAL|PSEUDO] [...]\"");
201  }
202  is_valid_option = true;
203  }
204  else if (option == "initialization-options") {
205  std::string initialization_options(arg);
206  std::size_t bad_option_start{initialization_options.find("--threads")};
207  std::size_t next_option_start;
208  if (bad_option_start != std::string::npos) {
209  next_option_start = initialization_options.find("--", bad_option_start + 1);
210  if (next_option_start != std::string::npos) {
211  initialization_options = initialization_options.substr(0, bad_option_start) + initialization_options.substr(next_option_start);
212  }
213  else {
214  initialization_options = initialization_options.substr(0, bad_option_start);
215  }
216  }
217  bad_option_start = initialization_options.find("--max-num-solutions");
218  if (bad_option_start != std::string::npos) {
219  initialization_options = initialization_options.substr(0, bad_option_start);
220  next_option_start = initialization_options.find("--", bad_option_start + 1);
221  if (next_option_start != std::string::npos) {
222  initialization_options = initialization_options.substr(0, bad_option_start) + initialization_options.substr(next_option_start);
223  }
224  else {
225  initialization_options = initialization_options.substr(0, bad_option_start);
226  }
227  }
228  if (initialization_options_ != "") {
229  initialization_options_ += " ";
230  }
231  initialization_options_ += initialization_options;
232  is_valid_option = true;
233  }
234  else if (option == "lower-bound-method") {
235  if (arg == "BRANCH") {
236  lower_bound_method_ = new Branch<UserNodeLabel, UserEdgeLabel>(this->ged_data_);
237  }
238  else if (arg == "BRANCH_FAST") {
239  lower_bound_method_ = new BranchFast<UserNodeLabel, UserEdgeLabel>(this->ged_data_);
240  }
241  else if (arg == "BRANCH_TIGHT") {
242  lower_bound_method_ = new BranchTight<UserNodeLabel, UserEdgeLabel>(this->ged_data_);
243  if (lower_bound_method_options_ != "") {
244  lower_bound_method_options_ += " ";
245  }
246  lower_bound_method_options_ += "--upper-bound NO ";
247  }
248  else if (arg != "NONE") {
249  throw Error(std::string("Invalid argument \"") + arg + "\" for option lower-bound-method. Usage: options = \"[--lower-bound-method BRANCH|BRANCH_FAST|BRANCH_TIGHT|NONE] [...]\"");
250  }
251  is_valid_option = true;
252  }
253  else if (option == "random-substitution-ratio") {
254  try {
255  random_substitution_ratio_ = std::stod(arg);
256  }
257  catch (...) {
258  throw Error(std::string("Invalid argument \"") + arg + "\" for option random-substitution-ratio. Usage: options = \"[--random-substitution-ratio <convertible to double between 0 and 1>]\"");
259  }
260  if (random_substitution_ratio_ < 0 or random_substitution_ratio_ > 1) {
261  throw Error(std::string("Invalid argument \"") + arg + "\" for option random-substitution-ratio. Usage: options = \"[--random-substitution-ratio <convertible to double between 0 and 1>]\"");
262  }
263  is_valid_option = true;
264  }
265  else if (option == "log") {
266  logfile_name_ = arg;
267  is_valid_option = true;
268  }
269  else if (option == "randpost-penalty") {
270  try {
271  randpost_penalty_ = std::stod(arg);
272  }
273  catch (...) {
274  throw Error(std::string("Invalid argument \"") + arg + "\" for option randpost-penalty. Usage: options = \"[--randpost-penalty <convertible to double between 0 and 1>]\"");
275  }
276  if (randpost_penalty_ < 0 or randpost_penalty_ > 1) {
277  throw Error(std::string("Invalid argument \"") + arg + "\" for option randpost-penalty. Usage: options = \"[--randpost-penalty <convertible to double between 0 and 1>]\"");
278  }
279  is_valid_option = true;
280  }
281  else if (option == "randpost-decay") {
282  try {
283  randpost_decay_ = std::stod(arg);
284  }
285  catch (...) {
286  throw Error(std::string("Invalid argument \"") + arg + "\" for option randpost-decay. Usage: options = \"[--randpost-decay <convertible to double between 0 and 1>]\"");
287  }
288  if (randpost_decay_ < 0 or randpost_decay_ > 1) {
289  throw Error(std::string("Invalid argument \"") + arg + "\" for option randpost-decay. Usage: options = \"[--randpost-decay <convertible to double between 0 and 1>]\"");
290  }
291  is_valid_option = true;
292  }
293  else if (option == "initial-solutions") {
294  try {
295  num_initial_solutions_ = std::stoul(arg);
296  }
297  catch (...) {
298  throw Error(std::string("Invalid argument \"") + arg + "\" for option num-initial-solutions. Usage: options = \"[--initial-solutions <convertible to int greater 0>]\"");
299  }
300  if (num_initial_solutions_ <= 0) {
301  throw Error(std::string("Invalid argument \"") + arg + "\" for option num-initial-solutions. Usage: options = \"[--initial-solutions <convertible to int greater 0>]\"");
302  }
303  if (initialization_options_ != "") {
304  initialization_options_ += " ";
305  }
306  initialization_options_ += "--max-num-solutions " + std::to_string(num_initial_solutions_);
307  is_valid_option = true;
308  }
309  else if (option == "ratio-runs-from-initial-solutions") {
310  try {
311  ratio_runs_from_initial_solutions_ = std::stod(arg);
312  }
313  catch (...) {
314  throw Error(std::string("Invalid argument \"") + arg + "\" for option ratio-runs-from-initial-solutions. Usage: options = \"[--ratio-runs-from-initial-solutions <convertible to double greater 0 and smaller equal 1>]\"");
315  }
316  if (ratio_runs_from_initial_solutions_ <= 0 or ratio_runs_from_initial_solutions_ > 1) {
317  throw Error(std::string("Invalid argument \"") + arg + "\" for option ratio-runs-from-initial-solutions. Usage: options = \"[--ratio-runs-from-initial-solutions <convertible to double greater 0 and smaller equal 1>]\"");
318  }
319  is_valid_option = true;
320  }
321  else if (option == "threads") {
322  try {
323  num_threads_ = std::stoul(arg);
324  }
325  catch (...) {
326  throw Error(std::string("Invalid argument \"") + arg + "\" for option threads. Usage: options = \"[--threads <convertible to int greater 0>]\"");
327  }
328  if (num_threads_ <= 0) {
329  throw Error(std::string("Invalid argument \"") + arg + "\" for option threads. Usage: options = \"[--threads <convertible to int greater 0>]\"");
330  }
331  if (initialization_options_ != "") {
332  initialization_options_ += " ";
333  }
334  initialization_options_ += "--threads " + std::to_string(num_threads_);
335  if (lower_bound_method_options_ != "") {
336  lower_bound_method_options_ += " ";
337  }
338  lower_bound_method_options_ += "--threads " + std::to_string(num_threads_);
339  is_valid_option = true;
340  }
341  else if (option == "num-randpost-loops") {
342  try {
343  num_randpost_loops_ = std::stoul(arg);
344  }
345  catch (...) {
346  throw Error(std::string("Invalid argument \"") + arg + "\" for option num-randpost-loops. Usage: options = \"[--num-randpost-loops <convertible to int greater equal 0>]\"");
347  }
348  is_valid_option = true;
349  }
350  else if (option == "max-randpost-retrials") {
351  try {
352  max_randpost_retrials_ = std::stoul(arg);
353  }
354  catch (...) {
355  throw Error(std::string("Invalid argument \"") + arg + "\" for option max-randpost-retrials. Usage: options = \"[--max-randpost-retrials <convertible to int greater equal 0>]\"");
356  }
357  is_valid_option = true;
358  }
359  if (initialization_method_) {
360  initialization_method_->set_options(initialization_options_);
361  }
362  if (lower_bound_method_) {
363  lower_bound_method_->set_options(lower_bound_method_options_);
364  }
365  is_valid_option = is_valid_option or ls_parse_option_(option, arg);
366  return is_valid_option;
367 }
368 
369 template<class UserNodeLabel, class UserEdgeLabel>
370 std::string
373  if (ls_valid_options_string_() == "") {
374  return "[--randomness <arg>] [--log <arg>] [--initialization-method <arg>] [--initialization-options <arg>] [--random-substitution-ratio <arg>] [--initial-solutions <arg>] [--ratio-runs-from-initial-solutions <arg>] [--threads <arg>] [--num-randpost-loops <arg>] [--max-randpost-retrials <arg>] [--randpost-penalty <arg>] [--randpost-decay <arg>]";
375  }
376  return ls_valid_options_string_() + "[--randomness <arg>] [--log <arg>] [--initialization-method <arg>] [--initialization-options <arg>] [--random-substitution-ratio <arg>] [--initial-solutions <arg>] [--ratio-runs-from-initial-solutions <arg>] [--threads <arg>] [--num-randpost-loops <arg>] [--max-randpost-retrials <arg>] [--randpost-penalty <arg>] [--randpost-decay <arg>]";
377 }
378 
379 template<class UserNodeLabel, class UserEdgeLabel>
380 void
383  delete initialization_method_;
384  initialization_method_ = nullptr;
385  initialization_options_ = std::string("");
386  delete lower_bound_method_;
387  lower_bound_method_ = nullptr;
388  lower_bound_method_options_ = std::string("");
389  random_substitution_ratio_ = 1.0;
390  num_initial_solutions_ = 1;
391  ratio_runs_from_initial_solutions_ = 1.0;
392  num_threads_ = 1;
393  num_randpost_loops_ = 0;
394  max_randpost_retrials_ = 10;
395  randpost_penalty_ = 0;
396  randpost_decay_ = 1;
397  logfile_name_ = std::string("");
398  use_real_randomness_ = true;
400 }
401 
402 // Definitions of private helper member functions.
403 
404 
405 template<class UserNodeLabel, class UserEdgeLabel>
406 std::size_t
409  return static_cast<std::size_t>(std::ceil(ratio_runs_from_initial_solutions_ * static_cast<double>(num_initial_solutions_)));
410 }
411 
412 template<class UserNodeLabel, class UserEdgeLabel>
413 void
415 generate_initial_node_maps_(const GEDGraph & g, const GEDGraph & h, std::vector<NodeMap> & initial_node_maps, Result & result) {
416  if (initialization_method_) {
417  generate_lsape_based_initial_node_maps_(g, h, initial_node_maps, result);
418  }
419  generate_random_initial_node_maps_(g, h, initial_node_maps);
420 }
421 
422 template<class UserNodeLabel, class UserEdgeLabel>
423 void
425 generate_lsape_based_initial_node_maps_(const GEDGraph & g, const GEDGraph & h, std::vector<NodeMap> & initial_node_maps, Result & result) {
426  Result lsape_result;
427  initialization_method_->run_as_util(g, h, lsape_result);
428  initial_node_maps = lsape_result.node_maps();
429  result.set_lower_bound(lsape_result.lower_bound());
430 }
431 
432 template<class UserNodeLabel, class UserEdgeLabel>
433 double
435 update_counts_matrix_and_visited_node_maps_(const std::vector<NodeMap> & result_node_maps, const std::vector<bool> & is_converged_node_map, const double & upper_bound,
436  const double & lower_bound, std::vector<NodeMap> & visited_node_maps, std::size_t loop, std::vector<std::vector<double>> & counts_matrix) const {
437  if (randpost_decay_ < 1) {
438  for (auto & row : counts_matrix) {
439  for (auto & cell : row) {
440  cell *= randpost_decay_;
441  }
442  }
443  }
444  std::size_t num_nodes_g{counts_matrix.size()};
445  std::size_t num_nodes_h{counts_matrix[0].size() - 1};
447  std::size_t node_map_id{0};
448  for (const NodeMap & node_map : result_node_maps) {
449  if (not is_converged_node_map.at(node_map_id++)) {
450  continue;
451  }
452  for (GEDGraph::NodeID i{0}; i < num_nodes_g; i++) {
453  k = node_map.image(i);
454  if (k != GEDGraph::dummy_node()) {
455  counts_matrix[i][k] += (1 - randpost_penalty_) + randpost_penalty_ * (upper_bound - lower_bound) / (node_map.induced_cost() - lower_bound);
456  }
457  else {
458  counts_matrix[i][num_nodes_h]++;
459  }
460  }
461  visited_node_maps.emplace_back(node_map);
462  }
463  if (logfile_name_ == "") {
464  return 0.0;
465  }
466  double skewdness{0.0};
467  std::size_t num_non_zeros_row{0};
468  std::size_t num_solutions = (loop + 1) * num_runs_from_initial_solutions_();
469  if (num_solutions == 1){
470  return 1.0;
471  }
472  for (const auto & row : counts_matrix) {
473  num_non_zeros_row = 0;
474  for (const auto & cell : row) {
475  if (cell > 0) {
476  num_non_zeros_row++;
477  }
478  }
479  skewdness += static_cast<double>(num_solutions-num_non_zeros_row)/static_cast<double>(num_solutions-1);
480  }
481  return skewdness/static_cast<double>(counts_matrix.size());
482 }
483 
484 template<class UserNodeLabel, class UserEdgeLabel>
485 void
487 generate_random_initial_node_maps_(const GEDGraph & g, const GEDGraph & h, std::vector<NodeMap> & initial_node_maps) {
488  std::vector<GEDGraph::NodeID> permutation_g;
489  for (auto node = g.nodes().first; node != g.nodes().second; node++) {
490  permutation_g.push_back(*node);
491  }
492  std::vector<GEDGraph::NodeID> permutation_h;
493  for (auto node = h.nodes().first; node != h.nodes().second; node++) {
494  permutation_h.push_back(*node);
495  }
496  std::size_t num_substituted_nodes{std::min(g.num_nodes(), h.num_nodes())};
497  num_substituted_nodes = std::lround(num_substituted_nodes * random_substitution_ratio_);
498  std::mt19937 urng_g;
499  std::mt19937 urng_h;
500  if (use_real_randomness_) {
501  std::random_device rng_g;
502  urng_g.seed(rng_g());
503  std::random_device rng_h;
504  urng_h.seed(rng_h());
505  }
506  for (std::size_t counter{initial_node_maps.size()}; counter < num_initial_solutions_; counter++) {
507  std::shuffle(permutation_g.begin(), permutation_g.end(), urng_g);
508  std::shuffle(permutation_h.begin(), permutation_h.end(), urng_h);
509  initial_node_maps.emplace_back(g.num_nodes(), h.num_nodes());
510  for (auto node = g.nodes().first; node != g.nodes().second; node++) {
511  initial_node_maps.back().add_assignment(*node, GEDGraph::dummy_node());
512  }
513  for (auto node = h.nodes().first; node != h.nodes().second; node++) {
514  initial_node_maps.back().add_assignment(GEDGraph::dummy_node(), *node);
515  }
516  for (std::size_t pos{0}; pos < num_substituted_nodes; pos++) {
517  initial_node_maps.back().add_assignment(permutation_g[pos], permutation_h[pos]);
518  }
519  this->ged_data_.compute_induced_cost(g, h, initial_node_maps.back());
520  }
521 }
522 
523 template<class UserNodeLabel, class UserEdgeLabel>
524 void
526 generate_node_maps_from_counts_matrix_(const GEDGraph & g, const GEDGraph & h,const std::vector<std::vector<double>> & counts_matrix, std::vector<NodeMap> & visited_node_maps, std::vector<NodeMap> & initial_node_maps) const {
527  std::size_t num_nodes_g{counts_matrix.size()};
528  std::size_t num_nodes_h{counts_matrix[0].size() - 1};
529  double max_count{0};
530  for (const auto & row : counts_matrix) {
531  for (const auto & cell : row) {
532  max_count = std::max(max_count, cell);
533  }
534  }
535  std::mt19937 urng;
536  if (use_real_randomness_) {
537  std::random_device rng;
538  urng.seed(rng());
539  }
540  std::size_t node_map_id{0};
541  std::size_t num_unsuccessful_trials{0};
542  while (node_map_id < initial_node_maps.size()) {
543  std::vector<std::vector<double>> temp_counts_matrix(counts_matrix);
544 
545  // flatten the distribution if necessary
546  if (max_randpost_retrials_ > 0 and num_unsuccessful_trials > 0) {
547  for (auto & row : temp_counts_matrix) {
548  for (auto & cell : row) {
549  cell += max_count * static_cast<double>(num_unsuccessful_trials) / static_cast<double>(max_randpost_retrials_);
550  }
551  }
552  }
553  // generate a node map
554  std::vector<bool> is_assigned_target_node(num_nodes_h, false);
555  for (GEDGraph::NodeID i{0}; i < num_nodes_g; i++) {
556  std::discrete_distribution<std::size_t> distribution(temp_counts_matrix[i].begin(), temp_counts_matrix[i].end());
557  GEDGraph::NodeID k{distribution(urng)};
558  if (k == 0){
559  bool is_all_zero{true};
560  for (const auto & cell : temp_counts_matrix[i]){
561  if (cell != 0){
562  is_all_zero = true;
563  break;
564  }
565 
566  }
567  if (is_all_zero) {
568  k=num_nodes_h;
569  }
570  }
571 
572  if (k < num_nodes_h) {
573  initial_node_maps.at(node_map_id).add_assignment(i, k);
574  is_assigned_target_node[k] = true;
575  for (GEDGraph::NodeID j{i+1}; j < num_nodes_g; j++) {
576  temp_counts_matrix[j][k] = 0.0;
577  }
578  }
579  else {
580  initial_node_maps.at(node_map_id).add_assignment(i, GEDGraph::dummy_node());
581  }
582 
583  }
584  for (GEDGraph::NodeID k{0}; k < num_nodes_h; k++) {
585  if (not is_assigned_target_node[k]) {
586  initial_node_maps.at(node_map_id).add_assignment(GEDGraph::dummy_node(), k);
587  }
588  }
589 
590  // check if node map has not been visited yet
591  bool retry{false};
592  if (num_unsuccessful_trials < max_randpost_retrials_) {
593  for (auto visited_node_map = visited_node_maps.crbegin(); visited_node_map != visited_node_maps.crend(); visited_node_map++) {
594  if (*visited_node_map == initial_node_maps.at(node_map_id)) {
595  retry = true;
596  num_unsuccessful_trials++;
597  initial_node_maps.at(node_map_id).clear();
598  break;
599  }
600  }
601  }
602  if (not retry) {
603  visited_node_maps.emplace_back(initial_node_maps.at(node_map_id));
604  this->ged_data_.compute_induced_cost(g, h, initial_node_maps.at(node_map_id));
605  node_map_id++;
606  }
607  }
608 }
609 
610 // Default definitions of virtual member functions.
611 template<class UserNodeLabel, class UserEdgeLabel>
612 void
614 ls_run_from_initial_solution_(const GEDGraph & g, const GEDGraph & h, double lower_bound, const NodeMap & initial_node_map, NodeMap & output_node_map) {}
615 
616 template<class UserNodeLabel, class UserEdgeLabel>
617 void
620 
621 template<class UserNodeLabel, class UserEdgeLabel>
622 void
624 ls_runtime_init_(const GEDGraph & g, const GEDGraph & h) {}
625 
626 template<class UserNodeLabel, class UserEdgeLabel>
627 bool
629 ls_parse_option_(const std::string & option, const std::string & arg) {
630  return false;
631 }
632 
633 template<class UserNodeLabel, class UserEdgeLabel>
634 std::string
637  return "";
638 }
639 
640 template<class UserNodeLabel, class UserEdgeLabel>
641 void
644 
645 }
646 
647 
648 
649 #endif /* SRC_METHODS_LS_BASED_METHOD_IPP_ */
Computes lower and upper bounds for general edit costs.
Definition: branch.hpp:42
std::size_t num_nodes() const
Returns the number of nodes.
Definition: ged_graph.ipp:211
Abstract class for methods that use variants of local search for upper bounding the graph edit distan...
Contains the standardized input data along with basic functionality.
Definition: ged_data.hpp:55
virtual void ls_runtime_init_(const GEDGraph &g, const GEDGraph &h)
Initializes the method for a run between two graphs.
A class for node maps.
Definition: node_map.hpp:43
Computes an upper bound for general edit costs.
Definition: walks.hpp:47
const GEDData< UserNodeLabel, UserEdgeLabel > & ged_data_
The data on which the method is run.
Definition: ged_method.hpp:124
void set_lower_bound(double lower_bound)
Sets the lower bound for GED.
Definition: result.ipp:39
Uses characteristics of an LSAPE instance for defining feature vectors for node edit operations...
double lower_bound() const
Returns the lower bound for GED.
Definition: result.ipp:45
Computes lower and upper bounds for general edit costs.
Definition: node.hpp:42
std::vector< NodeMap > & node_maps()
Provides access to all node maps.
Definition: result.ipp:98
A wrapper structure for the result of calls to ged::GEDMethod::run_as_util() and ged::GEDMethod::ged_...
Definition: result.hpp:38
Abstract class for the (suboptimal) computation of the graph edit distance.
Definition: ged_method.hpp:40
void sort_node_maps_and_set_upper_bound(std::size_t num_node_maps=std::numeric_limits< std::size_t >::max())
Sorts the vector of node maps w.r.t non-decreasing induced cost and possibly discards expensive node ...
Definition: result.ipp:110
Computes lower and upper bounds for general edit costs.
LSBasedMethod(const GEDData< UserNodeLabel, UserEdgeLabel > &ged_data)
Constructor.
virtual std::string ged_valid_options_string_() const final
Returns string of all valid options.
Uses ring structures for defining feature vectors for node edit operations.
Definition: ring_ml.hpp:48
virtual bool ls_parse_option_(const std::string &option, const std::string &arg)
Parses one option that is not among the ones shared by all derived classes of ged::LSBasedMethod.
std::size_t add_node_map(std::size_t num_nodes_g, std::size_t num_nodes_h)
Adds an empty node map to the result.
Definition: result.ipp:60
Runtime error class.
Definition: error.hpp:37
virtual ~LSBasedMethod()=0
Pure virtual destructor.
static NodeID dummy_node()
Returns a dummy node.
Definition: ged_graph.ipp:56
Computes lower and upper bounds for general edit costs.
Definition: bipartite.hpp:42
virtual void ged_run_(const GEDGraph &g, const GEDGraph &h, Result &result) final
Runs the method with options specified by set_options().
Computes lower and upper bounds for general edit costs.
Definition: branch_fast.hpp:45
std::size_t num_threads_
The number of threads to be used.
virtual void ls_init_()
Initializes the method.
virtual std::string ls_valid_options_string_() const
Returns string of all valid options that are not among the ones shared by all derived classes of ged:...
std::pair< node_iterator, node_iterator > nodes() const
Provides access to all nodes in the graph.
Definition: ged_graph.ipp:205
virtual void ged_init_() final
Initializes the method.
The normalized input graphs used by GEDLIB. All labels are integers.
Definition: ged_graph.hpp:104
Global namespace for GEDLIB.
Computes upper bounds for general edit costs.
Definition: subgraph.hpp:49
Computes an upper bound for general edit costs.
Definition: ring.hpp:51
virtual void ged_set_default_options_() final
Sets all options to default values.
virtual void ls_run_from_initial_solution_(const GEDGraph &g, const GEDGraph &h, double lower_bound, const NodeMap &initial_node_map, NodeMap &output_node_map)
Runs the local search from an initial node map.
Computes lower and upper bounds for uniform edit costs.
std::size_t NodeID
Internally used vertex ID type.
Definition: ged_graph.hpp:108
GraphID id() const
Returns the ID of the graph.
Definition: ged_graph.ipp:184
virtual bool ged_parse_option_(const std::string &option, const std::string &arg) final
Parses one option.
virtual void ls_set_default_options_()
Sets all options that are not among the ones shared by all derived classes of ged::LSBasedMethod to d...