GEDLIB  1.0
anchor_aware_ged.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_ANCHOR_AWARE_GED_IPP_
28 #define SRC_METHODS_ANCHOR_AWARE_GED_IPP_
29 
30 namespace ged {
31 
32 template<class UserNodeLabel, class UserEdgeLabel>
33 AnchorAwareGED<UserNodeLabel, UserEdgeLabel>::
34 ~AnchorAwareGED() {}
35 
36 template<class UserNodeLabel, class UserEdgeLabel>
37 AnchorAwareGED<UserNodeLabel, UserEdgeLabel>::
38 AnchorAwareGED(const GEDData<UserNodeLabel, UserEdgeLabel> & ged_data) :
39 GEDMethod<UserNodeLabel, UserEdgeLabel>(ged_data),
40 lsape_model_{LSAPESolver::ECBP},
41 search_method_{DFS},
42 lower_bound_method_{BRANCH_FAST},
43 num_threads_{1},
44 time_limit_in_sec_{0.0},
45 map_root_to_root_{false},
46 sorted_edges_(),
47 best_feasible_(0, 0),
48 open_(),
49 omega_{0.0} {}
50 
51 template<class UserNodeLabel, class UserEdgeLabel>
52 void
55  for (auto graph = this->ged_data_.begin(); graph != this->ged_data_.end(); graph++) {
56  init_graph_(*graph);
57  }
58 }
59 
60 template<class UserNodeLabel, class UserEdgeLabel>
61 void
63 ged_run_(const GEDGraph & g, const GEDGraph & h, Result & result) {
64  best_feasible_ = NodeMap(g.num_nodes(), h.num_nodes());
65  open_ = std::priority_queue<TreeNode_>();
66  omega_ = this->ged_data_.max_edit_cost(g, h) + 10.0;
67  Timer timer(time_limit_in_sec_);
68 
69  if (not this->initialized_ and lower_bound_method_ == BRANCH_FAST) {
70  init_graph_(g);
71  init_graph_(h);
72  }
73 
74  TreeNode_ current_node(g, h, this);
75  if (current_node.is_leaf_node()) {
76  current_node.extend_leaf_node(g, h);
77  result.add_node_map(current_node.node_map());
78  result.set_lower_bound(current_node.induced_cost());
80  return;
81  }
82  generate_best_child_(g, h, current_node);
83 
84  if (not map_root_to_root_) {
86  ipfp.set_options("--quadratic-model QAPE --threads " + std::to_string(num_threads_));
87  Result ipfp_result;
88  ipfp.run_as_util(g, h, ipfp_result);
89  best_feasible_ = ipfp_result.node_map(0);
90  }
91 
92  while (not open_.empty() and not timer.expired()) {
93  current_node = open_.top();
94  open_.pop();
95  if (current_node.lower_bound() >= best_feasible_.induced_cost()) {
96  continue;
97  }
98  if (current_node.is_leaf_node()) {
99  current_node.extend_leaf_node(g, h);
100  if (current_node.induced_cost() < best_feasible_.induced_cost()) {
101  best_feasible_ = current_node.node_map();
102  }
103  }
104  else {
105  if (current_node.has_unexplored_sibling()) {
106  generate_best_sibling_(g, h, current_node);
107  }
108  generate_best_child_(g, h, current_node);
109  }
110  }
111 
112  result.add_node_map(best_feasible_);
114  if (open_.empty()) {
115  result.set_lower_bound(best_feasible_.induced_cost());
116  }
117 }
118 
119 template<class UserNodeLabel, class UserEdgeLabel>
120 void
123  lsape_model_ = LSAPESolver::ECBP;
124  search_method_ = DFS;
125  lower_bound_method_ = BRANCH_FAST;
126  num_threads_ = 1;
127  time_limit_in_sec_ = 0.0;
128  map_root_to_root_ = false;
129 }
130 
131 template<class UserNodeLabel, class UserEdgeLabel>
132 std::string
135  return "[--lsape-model <arg>] [--search-method <arg>] [--lower-bound-method <arg>] [--threads <arg>] [--time-limit] [--map-root-to-root <arg>]";
136 }
137 
138 template<class UserNodeLabel, class UserEdgeLabel>
139 bool
141 ged_parse_option_(const std::string & option, const std::string & arg) {
142  if (option == "lsape-model") {
143  if (arg == "EBP") {
144  lsape_model_ = LSAPESolver::EBP;
145  }
146  else if (arg == "FLWC") {
147  lsape_model_ = LSAPESolver::FLWC;
148  }
149  else if (arg == "FLCC") {
150  lsape_model_ = LSAPESolver::FLCC;
151  }
152  else if (arg == "FBP") {
153  lsape_model_ = LSAPESolver::FBP;
154  }
155  else if (arg == "SFBP") {
156  lsape_model_ = LSAPESolver::SFBP;
157  }
158  else if (arg == "FBP0") {
159  lsape_model_ = LSAPESolver::FBP0;
160  }
161  else if (arg == "ECBP") {
162  lsape_model_ = LSAPESolver::ECBP;
163  }
164  else {
165  throw Error(std::string("Invalid argument \"") + arg + "\" for option lsape-model. Usage: options = \"[--lsape-model ECBP|EBP|FLWC|FLCC|FBP|SFBP|FBP0] [...]\"");
166  }
167  return true;
168  }
169  else if (option == "search-method") {
170  if (arg == "DFS") {
171  search_method_ = DFS;
172  }
173  else if (arg == "ASTAR") {
174  search_method_ = ASTAR;
175  }
176  else {
177  throw Error(std::string("Invalid argument \"") + arg + "\" for option search-method. Usage: options = \"[--search-method DFS|ASTAR] [...]\"");
178  }
179  return true;
180  }
181  else if (option == "lower-bound-method") {
182  if (arg == "BRANCH") {
183  lower_bound_method_ = BRANCH;
184  }
185  else if (arg == "BRANCH_FAST") {
186  lower_bound_method_ = BRANCH_FAST;
187  }
188  else {
189  throw Error(std::string("Invalid argument \"") + arg + "\" for option lower-bound-method. Usage: options = \"[--lower-bound-method BRANCH|BRANCH_FAST] [...]\"");
190  }
191  return true;
192  }
193  else if (option == "time-limit") {
194  try {
195  time_limit_in_sec_ = std::stod(arg);
196  }
197  catch (...) {
198  throw Error(std::string("Invalid argument \"") + arg + "\" for option time-limit. Usage: options = \"[--time-limit <convertible to double>] [...]");
199  }
200  return true;
201  }
202  else if (option == "threads") {
203  try {
204  num_threads_ = std::stoi(arg);
205  }
206  catch (...) {
207  throw Error(std::string("Invalid argument ") + arg + " for option threads. Usage: options = \"[--threads <convertible to int greater 0>] [...]");
208  }
209  if (num_threads_ <= 0) {
210  throw Error(std::string("Invalid argument ") + arg + " for option threads. Usage: options = \"[--threads <convertible to int greater 0>] [...]");
211  }
212  return true;
213  }
214  else if (option == "map-root-to-root") {
215  if (arg == "TRUE") {
216  map_root_to_root_ = true;
217  }
218  else if (arg == "FALSE") {
219  map_root_to_root_ = false;
220  }
221  else {
222  throw Error(std::string("Invalid argument ") + arg + " for option map-root-to-root. Usage: options = \"[--map-root-to-root TRUE|FALSE] [...]");
223  }
224  return true;
225  }
226  return false;
227 }
228 
229 // ==== Definition of private struct Edge_. ====
230 template<class UserNodeLabel, class UserEdgeLabel>
232 Edge_ ::
233 Edge_(LabelID label, GEDGraph::EdgeID edge_id) :
234 label{label},
235 edge_id{edge_id}{}
236 
237 template<class UserNodeLabel, class UserEdgeLabel>
238 bool
240 Edge_ ::
241 operator<(const Edge_ & rhs) const {
242  return label < rhs.label;
243 }
244 
245 // ==== Definition of private class SortedEdges_. ====
246 template<class UserNodeLabel, class UserEdgeLabel>
249 SortedEdges_() :
250 sorted_edges_() {}
251 
252 template<class UserNodeLabel, class UserEdgeLabel>
255 SortedEdges_(const GEDGraph & g):
256 sorted_edges_(){
257  for (auto node = g.nodes().first; node != g.nodes().second; node++) {
258  sorted_edges_[*node] = std::vector<Edge_>();
259  for (auto edge = g.incident_edges(*node).first; edge != g.incident_edges(*node).second; edge++) {
260  sorted_edges_[*node].push_back(Edge_(g.get_edge_label(*edge), *edge));
261  }
262  std::sort(sorted_edges_[*node].begin(), sorted_edges_[*node].end());
263  }
264 }
265 
266 template<class UserNodeLabel, class UserEdgeLabel>
267 void
270 operator=(const SortedEdges_ & rhs) {
271  sorted_edges_ = rhs.sorted_edges_;
272 }
273 
274 template<class UserNodeLabel, class UserEdgeLabel>
275 const std::vector<typename AnchorAwareGED<UserNodeLabel, UserEdgeLabel>::Edge_> &
279  return sorted_edges_.at(node);
280 }
281 
282 // ==== Definition of private class TreeNode_. ====
283 template<class UserNodeLabel, class UserEdgeLabel>
286 TreeNode_(const GEDGraph & g, const GEDGraph & h, const AnchorAwareGED * exact) :
287 exact_{exact},
288 node_map_(g.num_nodes(), h.num_nodes()),
289 is_matched_node_in_g_(g.num_nodes(), false),
290 is_matched_node_in_h_(h.num_nodes(), false),
291 is_candidate_in_h_(h.num_nodes(), true),
292 dummy_node_is_candidate_in_h_{true},
293 original_id_of_unmatched_nodes_in_h_(),
294 induced_cost_{0.0},
295 lower_bound_to_leaf_{0.0},
296 num_matched_nodes_in_g_{0},
297 num_matched_nodes_in_h_{0} {
298  if (exact_->map_root_to_root_) {
299  num_matched_nodes_in_g_++;
300  num_matched_nodes_in_h_++;
301  is_matched_node_in_g_[0] = true;
302  is_matched_node_in_h_[0] = true;
303  is_candidate_in_h_[0] = false;
304  node_map_.add_assignment(0, 0);
305  induced_cost_ = exact_->ged_data_.node_cost(g.get_node_label(0), h.get_node_label(0));
306  }
307 }
308 
309 template<class UserNodeLabel, class UserEdgeLabel>
312 TreeNode_(const TreeNode_ & tree_node) :
313 exact_{tree_node.exact_},
314 node_map_(tree_node.node_map_),
315 is_matched_node_in_g_(tree_node.is_matched_node_in_g_),
316 is_matched_node_in_h_(tree_node.is_matched_node_in_h_),
317 is_candidate_in_h_(tree_node.is_candidate_in_h_),
318 dummy_node_is_candidate_in_h_(tree_node.dummy_node_is_candidate_in_h_),
319 original_id_of_unmatched_nodes_in_h_(tree_node.original_id_of_unmatched_nodes_in_h_),
320 induced_cost_{tree_node.induced_cost_},
321 lower_bound_to_leaf_{tree_node.lower_bound_to_leaf_},
322 num_matched_nodes_in_g_{tree_node.num_matched_nodes_in_g_},
323 num_matched_nodes_in_h_{tree_node.num_matched_nodes_in_h_} {}
324 
325 template<class UserNodeLabel, class UserEdgeLabel>
326 void
329 operator=(const TreeNode_ & rhs) {
330  exact_ = rhs.exact_;
331  node_map_ = rhs.node_map_;
332  is_matched_node_in_g_ = rhs.is_matched_node_in_g_;
333  is_matched_node_in_h_ = rhs.is_matched_node_in_h_;
334  is_candidate_in_h_ = rhs.is_candidate_in_h_;
335  dummy_node_is_candidate_in_h_ = rhs.dummy_node_is_candidate_in_h_;
336  original_id_of_unmatched_nodes_in_h_ = rhs.original_id_of_unmatched_nodes_in_h_;
337  induced_cost_ = rhs.induced_cost_;
338  lower_bound_to_leaf_ = rhs.lower_bound_to_leaf_;
339  num_matched_nodes_in_g_ = rhs.num_matched_nodes_in_g_;
340  num_matched_nodes_in_h_ = rhs.num_matched_nodes_in_h_;
341 }
342 
343 template<class UserNodeLabel, class UserEdgeLabel>
344 double
347 lower_bound() const {
348  return induced_cost_ + lower_bound_to_leaf_;
349 }
350 
351 template<class UserNodeLabel, class UserEdgeLabel>
352 bool
355 operator<(const TreeNode_ & rhs) const {
356  if (exact_->search_method_ == ASTAR){
357  return lower_bound() > rhs.lower_bound();
358  }
359  return num_matched_nodes_in_g_ < rhs.num_matched_nodes_in_g_;
360 }
361 
362 template<class UserNodeLabel, class UserEdgeLabel>
366 next_unmatched_node_in_g() const {
367  if (num_matched_nodes_in_g_ < is_matched_node_in_g_.size()) {
368  return num_matched_nodes_in_g_;
369  }
370  return GEDGraph::dummy_node();
371 }
372 
373 template<class UserNodeLabel, class UserEdgeLabel>
377 last_matched_node_in_g() const {
378  if (num_matched_nodes_in_g_ > 0) {
379  return num_matched_nodes_in_g_ - 1;
380  }
381  return GEDGraph::undefined_node();
382 }
383 
384 template<class UserNodeLabel, class UserEdgeLabel>
385 void
389  for (GEDGraph::NodeID node_in_h{0}; node_in_h < is_matched_node_in_h_.size(); node_in_h++) {
390  is_candidate_in_h_[node_in_h] = not is_matched_node_in_h_.at(node_in_h);
391  }
392  dummy_node_is_candidate_in_h_ = true;
393 }
394 
395 template<class UserNodeLabel, class UserEdgeLabel>
396 void
399 append_extension(const GEDGraph & g, const GEDGraph & h, const NodeMap & extension) {
400  std::vector<NodeMap::Assignment> assignments;
401  extension.as_relation(assignments);
402  for (const auto & assignment : assignments) {
403  if (assignment.first != GEDGraph::dummy_node() and assignment.second != GEDGraph::dummy_node()) {
404  node_map_.add_assignment(assignment.first + num_matched_nodes_in_g_, original_id_of_unmatched_nodes_in_h_.at(assignment.second));
405  }
406  else if (assignment.first != GEDGraph::dummy_node()) {
407  node_map_.add_assignment(assignment.first + num_matched_nodes_in_g_, GEDGraph::dummy_node());
408  }
409  else {
410  node_map_.add_assignment(GEDGraph::dummy_node(), original_id_of_unmatched_nodes_in_h_.at(assignment.second));
411  }
412  }
413  exact_->ged_data_.compute_induced_cost(g, h, node_map_);
414  induced_cost_ = node_map_.induced_cost();
415 }
416 
417 template<class UserNodeLabel, class UserEdgeLabel>
418 void
421 append_next_assignment(const NodeMap & extension) {
422  GEDGraph::NodeID next_node_in_g = num_matched_nodes_in_g_++;
423  is_matched_node_in_g_[next_node_in_g] = true;
424  GEDGraph::NodeID next_node_in_h_in_extension{extension.image(0)};
425  GEDGraph::NodeID next_node_in_h{next_node_in_h_in_extension == GEDGraph::dummy_node() ? GEDGraph::dummy_node() : original_id_of_unmatched_nodes_in_h_.at(next_node_in_h_in_extension)};
426  if (next_node_in_h != GEDGraph::dummy_node()) {
427  num_matched_nodes_in_h_++;
428  is_matched_node_in_h_[next_node_in_h] = true;
429  is_candidate_in_h_[next_node_in_h] = false;
430  }
431  else {
432  dummy_node_is_candidate_in_h_ = false;
433  }
434  node_map_.add_assignment(next_node_in_g, next_node_in_h);
435 }
436 
437 template<class UserNodeLabel, class UserEdgeLabel>
438 void
441 populate_lsape_instance(const GEDGraph & g, const GEDGraph & h, DMatrix & lsape_instance) {
442 #ifdef _OPENMP
443  omp_set_num_threads(exact_->num_threads_ - 1);
444 #pragma omp parallel for if(exact_->num_threads_ > 1)
445 #endif
446  for (std::size_t row = 0; row < lsape_instance.num_rows(); row++) {
447  for (std::size_t col = 0; col < lsape_instance.num_cols(); col++) {
448  if ((row < lsape_instance.num_rows() - 1) and (col < lsape_instance.num_cols() - 1)) {
449  if ((row == 0) and (not is_candidate_in_h_.at(original_id_of_unmatched_nodes_in_h_.at(col)))) {
450  lsape_instance(row, col) = exact_->omega_;
451  }
452  else {
453  if (exact_->lower_bound_method_ == BRANCH) {
454  lsape_instance(row, col) = compute_branch_substitution_cost_(g, h, row + num_matched_nodes_in_g_, original_id_of_unmatched_nodes_in_h_.at(col));
455  }
456  else {
457  lsape_instance(row, col) = compute_branch_fast_substitution_cost_(g, h, row + num_matched_nodes_in_g_, original_id_of_unmatched_nodes_in_h_.at(col));
458  }
459  }
460  }
461  else if (row < lsape_instance.num_rows() - 1) {
462  if ((row == 0) and (not dummy_node_is_candidate_in_h_)) {
463  lsape_instance(row, col) = exact_->omega_;
464  }
465  else {
466  lsape_instance(row, col) = compute_deletion_cost_(g, row + num_matched_nodes_in_g_);
467  }
468  }
469  else if (col < lsape_instance.num_cols() - 1) {
470  lsape_instance(row, col) = compute_insertion_cost_(h, original_id_of_unmatched_nodes_in_h_.at(col));
471  }
472  }
473  }
474 }
475 
476 template<class UserNodeLabel, class UserEdgeLabel>
477 double
481  // Collect node deletion cost.
482  double cost{exact_->ged_data_.node_cost(g.get_node_label(i), ged::dummy_label())};
483 
484  // Collect edge deletion costs.
485  auto incident_edges_i = g.incident_edges(i);
486  for (auto ij = incident_edges_i.first; ij != incident_edges_i.second; ij++) {
487  if (not is_matched_node_in_g_.at(g.head(*ij))) {
488  cost += exact_->ged_data_.edge_cost(g.get_edge_label(*ij), ged::dummy_label()) * 0.5;
489  }
490  else {
491  cost += exact_->ged_data_.edge_cost(g.get_edge_label(*ij), ged::dummy_label());
492  }
493  }
494 
495  // Return overall deletion cost.
496  return cost;
497 }
498 
499 template<class UserNodeLabel, class UserEdgeLabel>
500 double
504  // Collect node insertion cost.
505  double cost{exact_->ged_data_.node_cost(ged::dummy_label(), h.get_node_label(k))};
506 
507  // Collect edge insertion costs.
508  auto incident_edges_k = h.incident_edges(k);
509  for (auto kl = incident_edges_k.first; kl != incident_edges_k.second; kl++) {
510  if (not is_matched_node_in_h_.at(h.head(*kl))) {
511  cost += exact_->ged_data_.edge_cost(ged::dummy_label(), h.get_edge_label(*kl)) * 0.5;
512  }
513  else {
514  cost += exact_->ged_data_.edge_cost(ged::dummy_label(), h.get_edge_label(*kl));
515  }
516  }
517 
518  // Return overall insertion cost.
519  return cost;
520 }
521 
522 template<class UserNodeLabel, class UserEdgeLabel>
523 double
527  // Collect node substitution costs.
528  double cost{exact_->ged_data_.node_cost(g.get_node_label(i), h.get_node_label(k))};
529 
530  // Collect outer edge costs.
531  std::vector<NodeMap::Assignment> assignments;
532  node_map_.as_relation(assignments);
533  for (const auto & assignment : assignments) {
534  GEDGraph::NodeID j{assignment.first};
535  GEDGraph::NodeID l{assignment.second};
536  if (g.is_edge(i, j) and h.is_edge(k, l)) {
537  cost += exact_->ged_data_.edge_cost(g.get_edge_label(g.get_edge(i, j)), h.get_edge_label(h.get_edge(k, l)));
538  }
539  else if (g.is_edge(i, j)) {
540  cost += exact_->ged_data_.edge_cost(g.get_edge_label(g.get_edge(i, j)), dummy_label());
541  }
542  else if (h.is_edge(k, l)) {
543  cost += exact_->ged_data_.edge_cost(dummy_label(), h.get_edge_label(h.get_edge(k, l)));
544  }
545  }
546 
547  // Collect unmatched edge labels.
548  std::vector<LabelID> edge_labels_to_unmatched_neighbours_i;
549  for (auto ij = exact_->sorted_edges_.at(g.id()).get_incident_edges(i).begin(); ij != exact_->sorted_edges_.at(g.id()).get_incident_edges(i).end(); ij++) {
550  if (not is_matched_node_in_g_.at(g.head(ij->edge_id))) {
551  edge_labels_to_unmatched_neighbours_i.push_back(ij->label);
552  }
553  }
554  std::vector<LabelID> edge_labels_to_unmatched_neighbours_k;
555  for (auto kl = exact_->sorted_edges_.at(h.id()).get_incident_edges(k).begin(); kl != exact_->sorted_edges_.at(h.id()).get_incident_edges(k).end(); kl++) {
556  if (not is_matched_node_in_h_.at(h.head(kl->edge_id))) {
557  edge_labels_to_unmatched_neighbours_k.push_back(kl->label);
558  }
559  }
560 
561  // Compute and add minimal edge insertion costs.
562  if (edge_labels_to_unmatched_neighbours_i.size() < edge_labels_to_unmatched_neighbours_k.size()) {
563  double min_ins_cost{std::numeric_limits<double>::infinity()};
564  for (auto label_h = edge_labels_to_unmatched_neighbours_k.begin(); label_h != edge_labels_to_unmatched_neighbours_k.end(); label_h++) {
565  min_ins_cost = std::min(min_ins_cost, exact_->ged_data_.edge_cost(dummy_label(), *label_h));
566  }
567  cost += static_cast<double>(edge_labels_to_unmatched_neighbours_k.size() - edge_labels_to_unmatched_neighbours_i.size()) * min_ins_cost * 0.5;
568  }
569 
570  // Compute and add minimal edge deletion costs.
571  if (edge_labels_to_unmatched_neighbours_i.size() > edge_labels_to_unmatched_neighbours_k.size()) {
572  double min_del_cost{std::numeric_limits<double>::infinity()};
573  for (auto label_g = edge_labels_to_unmatched_neighbours_i.begin(); label_g != edge_labels_to_unmatched_neighbours_i.end(); label_g++) {
574  min_del_cost = std::min(min_del_cost, exact_->ged_data_.edge_cost(*label_g, dummy_label()));
575  }
576  cost += static_cast<double>(edge_labels_to_unmatched_neighbours_i.size() - edge_labels_to_unmatched_neighbours_k.size()) * min_del_cost * 0.5;
577  }
578 
579  // Compute minimal edge relabelling costs.
580  double min_rel_cost{std::numeric_limits<double>::infinity()};
581  for (auto label_g = edge_labels_to_unmatched_neighbours_i.begin(); label_g != edge_labels_to_unmatched_neighbours_i.end(); label_g++) {
582  for (auto label_h = edge_labels_to_unmatched_neighbours_k.begin(); label_h != edge_labels_to_unmatched_neighbours_k.end(); label_h++) {
583  if (*label_g != *label_h) {
584  min_rel_cost = std::min(min_rel_cost, exact_->ged_data_.edge_cost(*label_g, *label_h));
585  }
586  }
587  }
588 
589  // Compute multiset intersection size.
590  std::size_t intersection_size{0};
591  auto label_g = edge_labels_to_unmatched_neighbours_i.begin();
592  auto label_h = edge_labels_to_unmatched_neighbours_k.begin();
593  while ((label_g != edge_labels_to_unmatched_neighbours_i.end()) and (label_h != edge_labels_to_unmatched_neighbours_k.end())) {
594  if (*label_g == *label_h) {
595  intersection_size++;
596  label_g++;
597  label_h++;
598  }
599  else if (*label_g < *label_h) {
600  label_g++;
601  }
602  else {
603  label_h++;
604  }
605  }
606 
607  // Collect edge relabelling costs.
608  std::size_t gamma(std::min(edge_labels_to_unmatched_neighbours_i.size(), edge_labels_to_unmatched_neighbours_k.size()) - intersection_size);
609  if (gamma > 0) {
610  cost += static_cast<double>(gamma) * min_rel_cost * 0.5;
611  }
612 
613  return cost;
614 }
615 
616 template<class UserNodeLabel, class UserEdgeLabel>
617 double
621  // Collect node substitution costs.
622  double cost{exact_->ged_data_.node_cost(g.get_node_label(i), h.get_node_label(k))};
623 
624  // Collect outer edge costs.
625  std::vector<NodeMap::Assignment> assignments;
626  node_map_.as_relation(assignments);
627  for (const auto & assignment : assignments) {
628  GEDGraph::NodeID j{assignment.first};
629  GEDGraph::NodeID l{assignment.second};
630  if (g.is_edge(i, j) and h.is_edge(k, l)) {
631  cost += exact_->ged_data_.edge_cost(g.get_edge_label(g.get_edge(i, j)), h.get_edge_label(h.get_edge(k, l)));
632  }
633  else if (g.is_edge(i, j)) {
634  cost += exact_->ged_data_.edge_cost(g.get_edge_label(g.get_edge(i, j)), dummy_label());
635  }
636  else if (h.is_edge(k, l)) {
637  cost += exact_->ged_data_.edge_cost(dummy_label(), h.get_edge_label(h.get_edge(k, l)));
638  }
639  }
640 
641  // Initialize subproblem.
642  std::vector<LabelID> edge_labels_to_unmatched_neighbours_i;
643  for (auto ij = g.incident_edges(i).first; ij != g.incident_edges(i).second; ij++) {
644  if (not is_matched_node_in_g_.at(g.head(*ij))) {
645  edge_labels_to_unmatched_neighbours_i.push_back(g.get_edge_label(*ij));
646  }
647  }
648  std::vector<LabelID> edge_labels_to_unmatched_neighbours_k;
649  for (auto kl = h.incident_edges(k).first; kl != h.incident_edges(k).second; kl++) {
650  if (not is_matched_node_in_h_.at(h.head(*kl))) {
651  edge_labels_to_unmatched_neighbours_k.push_back(h.get_edge_label(*kl));
652  }
653  }
654  DMatrix subproblem(edge_labels_to_unmatched_neighbours_i.size() + 1, edge_labels_to_unmatched_neighbours_k.size() + 1);
655 
656  // Collect edge deletion costs.
657  std::size_t row{0};
658  for (auto label_ij = edge_labels_to_unmatched_neighbours_i.begin(); label_ij != edge_labels_to_unmatched_neighbours_i.end(); label_ij++, row++) {
659  subproblem(row, edge_labels_to_unmatched_neighbours_k.size()) = exact_->ged_data_.edge_cost(*label_ij, ged::dummy_label()) * 0.5;
660  }
661 
662  // Collect edge insertion costs.
663  std::size_t col{0};
664  for (auto label_kl = edge_labels_to_unmatched_neighbours_k.begin(); label_kl != edge_labels_to_unmatched_neighbours_k.end(); label_kl++, col++) {
665  subproblem(edge_labels_to_unmatched_neighbours_i.size(), col) = exact_->ged_data_.edge_cost(ged::dummy_label(), *label_kl) * 0.5;
666  }
667 
668  // Collect edge relabelling costs.
669  row = 0;
670  for (auto label_ij = edge_labels_to_unmatched_neighbours_i.begin(); label_ij != edge_labels_to_unmatched_neighbours_i.end(); label_ij++, row++) {
671  col = 0;
672  for (auto label_kl = edge_labels_to_unmatched_neighbours_k.begin(); label_kl != edge_labels_to_unmatched_neighbours_k.end(); label_kl++, col++) {
673  subproblem(row, col) = exact_->ged_data_.edge_cost(*label_ij, *label_kl) * 0.5;
674  }
675  }
676 
677  // Solve subproblem.
678  LSAPESolver subproblem_solver(&subproblem);
679  subproblem_solver.set_model(exact_->lsape_model_);
680  subproblem_solver.solve();
681 
682  // Update and return overall substitution cost.
683  cost += subproblem_solver.minimal_cost();
684  return cost;
685 }
686 
687 template<class UserNodeLabel, class UserEdgeLabel>
688 void
691 set_lower_bound_to_leaf(double lower_bound_to_leaf) {
692  lower_bound_to_leaf_ = lower_bound_to_leaf;
693 }
694 
695 template<class UserNodeLabel, class UserEdgeLabel>
696 void
699 update_induced_cost(const GEDGraph & g, const GEDGraph & h) {
700  GEDGraph::NodeID i{last_matched_node_in_g()};
701  GEDGraph::NodeID k{node_map_.image(i)};
702  if (k != GEDGraph::dummy_node()) {
703  induced_cost_ += exact_->ged_data_.node_cost(g.get_node_label(i), h.get_node_label(k));
704  }
705  else {
706  induced_cost_ += exact_->ged_data_.node_cost(g.get_node_label(i), dummy_label());
707  }
708  std::vector<NodeMap::Assignment> assignments;
709  node_map_.as_relation(assignments);
710  for (const auto & assignment : assignments) {
711  GEDGraph::NodeID j{assignment.first};
712  GEDGraph::NodeID l{assignment.second};
713  if (g.is_edge(i, j) and h.is_edge(k, l)) {
714  induced_cost_ += exact_->ged_data_.edge_cost(g.get_edge_label(g.get_edge(i, j)), h.get_edge_label(h.get_edge(k, l)));
715  }
716  else if (g.is_edge(i, j)) {
717  induced_cost_ += exact_->ged_data_.edge_cost(g.get_edge_label(g.get_edge(i, j)), dummy_label());
718  }
719  else if (h.is_edge(k, l)) {
720  induced_cost_ += exact_->ged_data_.edge_cost(dummy_label(), h.get_edge_label(h.get_edge(k, l)));
721  }
722  }
723 }
724 
725 template<class UserNodeLabel, class UserEdgeLabel>
726 double
729 induced_cost() const {
730  return induced_cost_;
731 }
732 
733 template<class UserNodeLabel, class UserEdgeLabel>
734 const NodeMap &
737 node_map() const {
738  return node_map_;
739 }
740 
741 template<class UserNodeLabel, class UserEdgeLabel>
742 bool
745 is_leaf_node() const {
746  return ((num_matched_nodes_in_g_ == is_matched_node_in_g_.size()) or (num_matched_nodes_in_h_ == is_matched_node_in_h_.size()));
747 }
748 
749 template<class UserNodeLabel, class UserEdgeLabel>
750 void
753 extend_leaf_node(const GEDGraph & g, const GEDGraph & h) {
754  for (GEDGraph::NodeID i{0}; i < is_matched_node_in_g_.size(); i++) {
755  if (not is_matched_node_in_g_.at(i)) {
756  node_map_.add_assignment(i, GEDGraph::dummy_node());
757  is_matched_node_in_g_[i] = true;
758  num_matched_nodes_in_g_++;
759  }
760  }
761  for (GEDGraph::NodeID k{0}; k < is_matched_node_in_h_.size(); k++) {
762  if (not is_matched_node_in_h_.at(k)) {
763  node_map_.add_assignment(GEDGraph::dummy_node(), k);
764  is_matched_node_in_h_[k] = true;
765  num_matched_nodes_in_h_++;
766  }
767  }
768  exact_->ged_data_.compute_induced_cost(g, h, node_map_);
769  induced_cost_ = node_map_.induced_cost();
770 }
771 
772 template<class UserNodeLabel, class UserEdgeLabel>
773 void
777  GEDGraph::NodeID next_node_g{--num_matched_nodes_in_g_};
778  is_matched_node_in_g_[next_node_g] = false;
779  GEDGraph::NodeID next_node_h{node_map_.image(next_node_g)};
780  node_map_.erase_image(next_node_g);
781  if (next_node_h != GEDGraph::dummy_node()) {
782  is_matched_node_in_h_[next_node_h] = false;
783  num_matched_nodes_in_h_--;
784  }
785 }
786 
787 template<class UserNodeLabel, class UserEdgeLabel>
788 bool
792  if (num_matched_nodes_in_g_ == 0) {
793  return false;
794  }
795  if (dummy_node_is_candidate_in_h_) {
796  return true;
797  }
798  for (auto is_candidate : is_candidate_in_h_) {
799  if (is_candidate) {
800  return true;
801  }
802  }
803  return false;
804 }
805 
806 template<class UserNodeLabel, class UserEdgeLabel>
807 std::size_t
810 num_unmatched_nodes_in_g() const {
811  return is_matched_node_in_g_.size() - num_matched_nodes_in_g_;
812 }
813 
814 template<class UserNodeLabel, class UserEdgeLabel>
815 std::size_t
818 num_unmatched_nodes_in_h() const {
819  return is_matched_node_in_h_.size() - num_matched_nodes_in_h_;
820 }
821 
822 template<class UserNodeLabel, class UserEdgeLabel>
823 void
827  original_id_of_unmatched_nodes_in_h_.clear();
828  GEDGraph::NodeID k{0};
829  for (auto is_matched_node : is_matched_node_in_h_) {
830  if (not is_matched_node) {
831  original_id_of_unmatched_nodes_in_h_.push_back(k);
832  }
833  }
834 }
835 
836 // ==== Definitions of private helper member functions. ====
837 template<class UserNodeLabel, class UserEdgeLabel>
838 void
840 init_graph_(const GEDGraph & graph) {
841  sorted_edges_[graph.id()] = SortedEdges_(graph);
842 }
843 
844 template<class UserNodeLabel, class UserEdgeLabel>
845 void
847 generate_next_tree_node_(const GEDGraph & g, const GEDGraph & h, TreeNode_ & next_tree_node, bool update_induced_cost, bool update_upper_bound) {
848 
849  next_tree_node.update_original_id_of_unmatched_nodes_in_h();
850  // construct LSAPE instance
851  DMatrix lsape_instance(next_tree_node.num_unmatched_nodes_in_g() + 1, next_tree_node.num_unmatched_nodes_in_h() + 1, 0.0);
852  next_tree_node.populate_lsape_instance(g, h, lsape_instance);
853 
854  // solve LSAPE instance and update lower bound to leaf
855  LSAPESolver lsape_solver(&lsape_instance);
856  lsape_solver.set_model(lsape_model_);
857  lsape_solver.solve();
858  next_tree_node.set_lower_bound_to_leaf(lsape_solver.minimal_cost());
859 
860  // update node map
861  NodeMap extension(next_tree_node.num_unmatched_nodes_in_g(), next_tree_node.num_unmatched_nodes_in_h());
862  util::construct_node_map_from_solver(lsape_solver, extension);
863  next_tree_node.append_next_assignment(extension);
864 
865  // update induced cost
866  if (update_induced_cost) {
867  next_tree_node.update_induced_cost(g, h);
868  }
869  open_.push(next_tree_node);
870 
871  if (update_upper_bound) {
872  extension.erase_image(0);
873  next_tree_node.append_extension(g, h, extension);
874  if (next_tree_node.induced_cost() < best_feasible_.induced_cost()) {
875  best_feasible_ = next_tree_node.node_map();
876  }
877  }
878 
879 }
880 
881 template<class UserNodeLabel, class UserEdgeLabel>
882 void
884 generate_best_child_(const GEDGraph & g, const GEDGraph & h, const TreeNode_ & current_node) {
885  TreeNode_ child_node(current_node);
886  child_node.prepare_for_child_generation();
887  generate_next_tree_node_(g, h, child_node, true, false);
888 }
889 
890 template<class UserNodeLabel, class UserEdgeLabel>
891 void
893 generate_best_sibling_(const GEDGraph & g, const GEDGraph & h, const TreeNode_ & current_node) {
894  TreeNode_ sibling_node(current_node);
895  sibling_node.prepare_for_sibling_generation();
896  generate_next_tree_node_(g, h, sibling_node, false, false);
897 }
898 
899 }
900 
901 #endif /* SRC_METHODS_ANCHOR_AWARE_GED_IPP_ */
void set_model(const Model &model)
Makes the solver use a specific model for optimal solving.
void erase_image(GEDGraph::NodeID node)
Erases image of a node.
Definition: node_map.ipp:122
Reduction to compact LSAP instance for quasimetric costs.
Reduction to compact LSAP instance for quasimetric costs.
std::size_t num_nodes() const
Returns the number of nodes.
Definition: ged_graph.ipp:211
virtual void ged_run_(const GEDGraph &g, const GEDGraph &h, Result &result) final
Runs the method with options specified by set_options().
This class solves LSAPE instances by calling the library lsape available at https://bougleux.users.greyc.fr/lsape/.
Reduction to compact LSAP instance without cost constraints.
void as_relation(std::vector< Assignment > &relation) const
Constructs the representation as relation.
Definition: node_map.ipp:164
A class for node maps.
Definition: node_map.hpp:43
bool empty() const
Checks if the node map is empty.
Definition: node_map.ipp:65
A timer class that can be used by methods that support time limits.
Definition: timer.hpp:38
EdgeID get_edge(NodeID tail, NodeID head) const
Retrieves an edge from its incident nodes.
Definition: ged_graph.ipp:241
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
NodeID head(EdgeID edge) const
Returns the head of an edge.
Definition: ged_graph.ipp:199
bool is_edge(NodeID tail, NodeID head) const
Checks if an edge exists.
Definition: ged_graph.ipp:262
bool expired() const
Checks if the time limit has expired.
Definition: timer.ipp:39
void run_as_util(const GEDGraph &g, const GEDGraph &h, Result &result)
Runs the method with options specified by set_options().
Definition: ged_method.ipp:129
void solve(int num_solutions=1)
Solves the LSAPE problem instance.
A wrapper structure for the result of calls to ged::GEDMethod::run_as_util() and ged::GEDMethod::ged_...
Definition: result.hpp:38
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
Reduction to extended LSAP instance without cost constraints.
virtual void ged_init_() final
Initializes the method.
double induced_cost() const
Returns the induced cost.
Definition: node_map.ipp:210
std::size_t num_cols() const
Returns the number of columns.
Definition: matrix.ipp:110
std::size_t LabelID
Internally used type of node and edge labels.
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
LabelID get_node_label(NodeID node) const
Returns the label of a given node.
Definition: ged_graph.ipp:126
Runtime error class.
Definition: error.hpp:37
static NodeID dummy_node()
Returns a dummy node.
Definition: ged_graph.ipp:56
static NodeID undefined_node()
Returns an undefined node.
Definition: ged_graph.ipp:62
Adaption of Hungarian Algorithm to LSAPE.
LabelID get_edge_label(EdgeID edge) const
Returns the label of a given edge.
Definition: ged_graph.ipp:135
void set_options(const std::string &options)
Sets the options of the method.
Definition: ged_method.ipp:99
std::pair< incident_edge_iterator, incident_edge_iterator > incident_edges(NodeID node) const
Provides access to all incident edges of a node.
Definition: ged_graph.ipp:150
double minimal_cost() const
Returns the cost of the computed solutions.
bool initialized_
A flag that equals true if init() has been called and false otherwise.
Definition: ged_method.hpp:119
virtual std::string ged_valid_options_string_() const final
Returns string of all valid options.
std::pair< node_iterator, node_iterator > nodes() const
Provides access to all nodes in the graph.
Definition: ged_graph.ipp:205
The normalized input graphs used by GEDLIB. All labels are integers.
Definition: ged_graph.hpp:104
detail::GedGraphAL::edge_descriptor EdgeID
Internally used edge ID type.
Definition: ged_graph.hpp:110
constexpr LabelID dummy_label()
Returns a dummy label.
Reduction to compact LSAP instance for quasimetric costs.
Global namespace for GEDLIB.
Computes the exact graph edit distance for general edit costs.
GEDGraph::NodeID image(GEDGraph::NodeID node) const
Returns image of a node.
Definition: node_map.ipp:110
NodeMap & node_map(std::size_t index_node_map)
Provides access to a node map.
Definition: result.ipp:74
virtual void ged_set_default_options_() final
Sets all options to default values.
void construct_node_map_from_solver(const Solver &solver, NodeMap &node_map, std::size_t solution_id=0)
Constructs a node map from a solution to LSAPE or LSAPE stored in a ged::LSAPESolver or a ged::LSAPSo...
Definition: misc.ipp:86
Computes an upper bound for general edit costs.
Definition: ipfp.hpp:64
std::size_t NodeID
Internally used vertex ID type.
Definition: ged_graph.hpp:108
Reduction to compact LSAP instance for quasimetric costs.
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.
std::size_t num_rows() const
Returns the number of rows.
Definition: matrix.ipp:85