GEDLIB  1.0
branch_tight.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_BRANCH_TIGHT_IPP_
28 #define SRC_METHODS_BRANCH_TIGHT_IPP_
29 
30 namespace ged {
31 
32 // === Definitions of destructor and constructor. ===
33 template<class UserNodeLabel, class UserEdgeLabel>
34 BranchTight<UserNodeLabel, UserEdgeLabel>::
35 ~BranchTight() {}
36 
37 template<class UserNodeLabel, class UserEdgeLabel>
38 BranchTight<UserNodeLabel, UserEdgeLabel>::
39 BranchTight(const GEDData<UserNodeLabel, UserEdgeLabel> & ged_data) :
40 GEDMethod<UserNodeLabel, UserEdgeLabel>(ged_data),
41 max_itrs_{20},
42 range_{0.0},
43 epsilon_{0.01},
44 upper_bound_option_{BEST},
45 naive_regularization_{true},
46 num_threads_{1},
47 time_limit_in_sec_{0.0} {}
48 
49 // === Definitions of member functions inherited from GEDMethod. ===
50 template<class UserNodeLabel, class UserEdgeLabel>
51 void
54  max_itrs_ = 20;
55  range_ = 0.0;
56  epsilon_ = 0.01;
57  upper_bound_option_ = BEST;
58  naive_regularization_ = true;
59  num_threads_= 1;
60  time_limit_in_sec_ = 0.0;
61 }
62 
63 template<class UserNodeLabel, class UserEdgeLabel>
64 std::string
67  return "[--iterations <arg>] [--range <arg>] [--epsilon <arg>] [--upper-bound <arg>] [--regularize <arg>] [--threads <arg>] [--time-limit <arg>]";
68 }
69 
70 template<class UserNodeLabel, class UserEdgeLabel>
71 bool
73 ged_parse_option_(const std::string & option, const std::string & arg) {
74  if (option == "iterations") {
75  try {
76  max_itrs_ = std::stoul(arg);
77  }
78  catch (...) {
79  throw Error(std::string("Invalid argument \"") + arg + "\" for option iterations. Usage: options = \"[--iterations <convertible to int>] [...]");
80  }
81  if ((max_itrs_ <= 0) and (epsilon_ <= 0) and (time_limit_in_sec_ <= 0)) {
82  throw Error("Switching off all termination criteria that guarantee termination is illegal.");
83  }
84  return true;
85  }
86  else if (option == "time-limit") {
87  try {
88  time_limit_in_sec_ = std::stod(arg);
89  }
90  catch (...) {
91  throw Error(std::string("Invalid argument \"") + arg + "\" for option time-limit. Usage: options = \"[--time-limit <convertible to double>] [...]");
92  }
93  if ((max_itrs_ <= 0) and (epsilon_ <= 0) and (time_limit_in_sec_ <= 0)) {
94  throw Error("Switching off all termination criteria that guarantee termination is illegal.");
95  }
96  return true;
97  }
98  else if (option == "range") {
99  try {
100  range_ = std::stod(arg);
101  }
102  catch (...) {
103  throw Error(std::string("Invalid argument \"") + arg + "\" for option range. Usage: options = \"[--range <convertible to double>] [...]");
104  }
105  return true;
106  }
107  else if (option == "epsilon") {
108  try {
109  epsilon_ = std::stod(arg);
110  }
111  catch (...) {
112  throw Error(std::string("Invalid argument \"") + arg + "\" for option epsilon. Usage: options = \"[--epsilon <convertible to double>] [...]");
113  }
114  if ((max_itrs_ <= 0) and (epsilon_ <= 0) and (time_limit_in_sec_ <= 0)) {
115  throw Error("Switching off all termination criteria that guarantee termination is illegal.");
116  }
117  return true;
118  }
119  else if (option == "upper-bound") {
120  if (arg == "FIRST") {
121  upper_bound_option_ = FIRST;
122  }
123  else if (arg == "LAST") {
124  upper_bound_option_ = LAST;
125  }
126  else if (arg == "BEST") {
127  upper_bound_option_ = BEST;
128  }
129  else if (arg == "NO") {
130  upper_bound_option_ = NO;
131  }
132  else {
133  throw ged::Error(std::string("Invalid argument ") + arg + " for option upper-bound. Usage: options = \"[--upper-bound NO|FIRST|LAST|BEST]\"");
134  }
135  return true;
136  }
137  else if (option == "regularize") {
138  if (arg == "K-FACTOR") {
139  naive_regularization_ = false;
140  }
141  else if (arg == "NAIVE") {
142  naive_regularization_ = true;
143  }
144  else {
145  throw Error(std::string("Invalid argument \"") + arg + "\" for option range. Usage: options = \"[--regularize NAIVE|K-FACTOR] [...]");
146  }
147  return true;
148  }
149  else if (option == "threads") {
150  try {
151  num_threads_ = std::stoi(arg);
152  }
153  catch (...) {
154  throw Error(std::string("Invalid argument \"") + arg + "\" for option threads. Usage: options = \"[--threads <convertible to int greater 0>] [...]");
155  }
156  if (max_itrs_ <= 0) {
157  throw Error(std::string("Invalid argument \"") + arg + "\" for option threads. Usage: options = \"[--threads <convertible to int greater 0>] [...]");
158  }
159  return true;
160  }
161  return false;
162 }
163 
164 template<class UserNodeLabel, class UserEdgeLabel>
165 void
167 ged_run_(const GEDGraph & gg, const GEDGraph & hh, Result & result) {
168 
169  Timer timer(time_limit_in_sec_);
170 
171  // Regularize the graphs g and h.
172  GEDGraph g(gg);
173  GEDGraph h(hh);
174  std::size_t degree{};
175  if (naive_regularization_) {
176  degree = naive_regularize_(g, h);
177  }
178  else {
179  degree = regularize_(g, h);
180  }
181 
182 
183  // Initialize the subproblems, the node costs, and the weights for updating the subproblems.
184  SubproblemSolvers_ subproblem_solvers(static_cast<std::size_t>(g.num_nodes()), degree);
185  init_subproblems_(g, h, subproblem_solvers);
186  DMatrix node_costs(static_cast<std::size_t>(g.num_nodes()), static_cast<std::size_t>(g.num_nodes()));
187  init_node_costs_(g, h, node_costs);
188  Weights_ weights(static_cast<std::size_t>(g.num_nodes()));
189 
190  // The main loop.
191  DMatrix master_problem(g.num_nodes(), g.num_nodes());
192  LSAPSolver master_problem_solver(&master_problem);
193  double last_improvement{std::numeric_limits<double>::max()};
194  for (std::size_t current_itr{1}; not termination_criterion_met_(current_itr, last_improvement, result); current_itr++) {
195 
196  if ((current_itr > 1) and timer.expired()) {
197  break;
198  }
199 
200  // Update and solve the subproblems in parallel.
201  if (current_itr > 1) {
202  update_weights_(master_problem_solver, degree, subproblem_solvers, weights);
203  update_subproblem_costs_(weights, degree, subproblem_solvers);
204  }
205  subproblem_solvers.solve(num_threads_);
206 
207  // Update and solve the master problem.
208  update_master_problem_costs_(subproblem_solvers, node_costs, master_problem);
209  master_problem_solver.solve();
210 
211  // Update the lower bound bound, the improvement ratio, and the upper bound, if necessary.
212  if (current_itr > 1) {
213  if (result.lower_bound() < 0.00001) {
214  break;
215  }
216  last_improvement = (master_problem_solver.minimal_cost() - result.lower_bound()) / result.lower_bound();
217  }
218  result.set_lower_bound(master_problem_solver.minimal_cost());
219  if (upper_bound_option_ == BEST or (upper_bound_option_ == FIRST and current_itr == 1)) {
220  std::size_t index_node_map{result.add_node_map(g.num_nodes(), h.num_nodes())};
221  util::construct_node_map_from_solver(master_problem_solver, result.node_map(index_node_map));
222  if (result.is_non_redundant_node_map(index_node_map)) {
223  this->ged_data_.compute_induced_cost(g, h, result.node_map(index_node_map));
225  }
226  }
227  }
228 
229  // Store the last upper bound if the option upper-bound is set to LAST.
230  if (upper_bound_option_ == LAST) {
231  std::size_t index_node_map{result.add_node_map(g.num_nodes(), h.num_nodes())};
232  util::construct_node_map_from_solver(master_problem_solver, result.node_map(index_node_map));
233  this->ged_data_.compute_induced_cost(g, h, result.node_map(index_node_map));
235  }
236 }
237 
238 // === Definitions of private helper functions. ===
239 
240 template<class UserNodeLabel, class UserEdgeLabel>
241 bool
243 termination_criterion_met_(const std::size_t & current_itr, const double & last_improvement, Result & result) {
244  if ((result.upper_bound() >= 0) and (result.lower_bound() >= result.upper_bound())) {
245  return true;
246  }
247  if ((max_itrs_ > 0) and (max_itrs_ < current_itr)) {
248  return true;
249  }
250  if ((epsilon_ > 0) and (epsilon_ > last_improvement)) {
251  return true;
252  }
253  if ((range_ > 0) and (range_ < result.lower_bound())) {
254  return true;
255  }
256  return false;
257 }
258 
259 template<class UserNodeLabel, class UserEdgeLabel>
260 void
262 init_subproblems_(const GEDGraph & g, const GEDGraph & h, SubproblemSolvers_ & subproblems_solver) const {
263  for (std::size_t row_master{0}; row_master < subproblems_solver.get_size(); row_master++) {
264 
265  // Collect the edges that are incident to the node that corresponds to row row_master in the master problem.
266  auto incident_edges_i = g.incident_edges(row_master);
267 
268  for (std::size_t col_master{0}; col_master < subproblems_solver.get_size(); col_master++) {
269 
270  // Collect the edges that are incident to the node that corresponds to column col_master in the master problem.
271  auto incident_edges_k = h.incident_edges(col_master);
272 
273  // Initialize row and collumn indices.
274  std::size_t row_sub{0};
275  for (auto ij = incident_edges_i.first; ij != incident_edges_i.second; ij++) {
276  subproblems_solver.rows_subproblem_to_master(row_master, col_master)[row_sub++] = g.head(*ij);
277  }
278  std::size_t col_sub{0};
279  for (auto kl = incident_edges_k.first; kl != incident_edges_k.second; kl++) {
280  subproblems_solver.cols_subproblem_to_master(row_master, col_master)[col_sub++] = h.head(*kl);
281  }
282 
283  // Collect edge relabelling costs.
284  row_sub = 0;
285  for (auto ij = incident_edges_i.first; ij != incident_edges_i.second; ij++) {
286  col_sub = 0;
287  for (auto kl = incident_edges_k.first; kl != incident_edges_k.second; kl++) {
288  subproblems_solver.subproblem(row_master, col_master)(row_sub, col_sub++) = this->ged_data_.edge_cost(g.get_edge_label(*ij), h.get_edge_label(*kl)) * 0.5;
289  }
290  row_sub++;
291  }
292  }
293  }
294 }
295 
296 template<class UserNodeLabel, class UserEdgeLabel>
297 void
299 update_master_problem_costs_(const SubproblemSolvers_ & subproblems_solver, const DMatrix & node_costs, DMatrix & master_problem) const {
300  for (std::size_t row_master{0}; row_master < subproblems_solver.get_size(); row_master++) {
301  for (std::size_t col_master{0}; col_master < subproblems_solver.get_size(); col_master++) {
302  master_problem(row_master, col_master) = node_costs(row_master, col_master) + subproblems_solver.solver(row_master, col_master).minimal_cost();
303  }
304  }
305 }
306 
307 template<class UserNodeLabel, class UserEdgeLabel>
308 void
310 init_node_costs_(const GEDGraph & g, const GEDGraph & h, DMatrix & node_costs) const {
311  for (std::size_t row_master{0}; row_master < node_costs.num_rows(); row_master++) {
312  for (std::size_t col_master{0}; col_master < node_costs.num_cols(); col_master++) {
313  node_costs(row_master, col_master) = this->ged_data_.node_cost(g.get_node_label(row_master), h.get_node_label(col_master));
314  }
315  }
316 }
317 
318 template<class UserNodeLabel, class UserEdgeLabel>
319 void
321 update_weights_(const LSAPSolver & master_problem_solver, std::size_t degree, const SubproblemSolvers_ & subproblems_solver, Weights_ & weights) const {
322  double delta{1 / static_cast<double>(degree)};
323  double weight{0.0};
324  for (std::size_t row_master{0}; row_master < subproblems_solver.get_size(); row_master++) {
325  for (std::size_t col_master{0}; col_master < subproblems_solver.get_size(); col_master++) {
326  for (std::size_t row_sub{0}; row_sub < degree; row_sub++) {
327  std::size_t row_sub_in_master{subproblems_solver.row_in_master(row_master, col_master, row_sub)};
328  for (std::size_t col_sub{0}; col_sub < degree; col_sub++) {
329  std::size_t col_sub_in_master{subproblems_solver.col_in_master(row_master, col_master, col_sub)};
330  weight = subproblems_solver.solver(row_master, col_master).get_slack(row_sub, col_sub) + master_problem_solver.get_slack(row_master, col_master) * delta;
331  weights.set_weight(row_master, col_master, row_sub_in_master, col_sub_in_master, weight);
332  }
333  }
334  }
335  }
336 }
337 
338 template<class UserNodeLabel, class UserEdgeLabel>
339 void
341 update_subproblem_costs_(const Weights_ & weights, std::size_t degree, SubproblemSolvers_ & subproblems_solver) const {
342  double added_weight{0.0};
343  for (std::size_t row_master{0}; row_master < subproblems_solver.get_size(); row_master++) {
344  for (std::size_t col_master{0}; col_master < subproblems_solver.get_size(); col_master++) {
345  for (std::size_t row_sub{0}; row_sub < degree; row_sub++) {
346  std::size_t row_sub_in_master{subproblems_solver.row_in_master(row_master, col_master, row_sub)};
347  for (std::size_t col_sub{0}; col_sub < degree; col_sub++) {
348  std::size_t col_sub_in_master{subproblems_solver.col_in_master(row_master, col_master, col_sub)};
349  added_weight = weights.get_weight(row_sub_in_master, col_sub_in_master, row_master, col_master);
350  added_weight -= weights.get_weight(row_master, col_master, row_sub_in_master, col_sub_in_master);
351  subproblems_solver.subproblem(row_master, col_master)(row_sub, col_sub) += added_weight;
352  }
353  }
354  }
355  }
356 }
357 
358 template<class UserNodeLabel, class UserEdgeLabel>
359 std::size_t
361 regularize_(GEDGraph & g, GEDGraph & h) const {
362 
363  // Add dummy nodes to smaller graph or to both graphs if the costs are not quasimetric.
364  if (this->ged_data_.quasimetric_costs(g, h)) {
365  fill_up_smaller_graph_(g, h);
366  }
367  else {
368  fill_up_both_graphs_(g, h);
369  }
370 
371  // Build complement graphs.
372  GEDGraph complement_g{};
373  GEDGraph::NodeNodeMap complement_to_g;
374  construct_complement_graph_(g, complement_g, complement_to_g);
375  GEDGraph complement_h{};
376  GEDGraph::NodeNodeMap complement_to_h;
377  construct_complement_graph_(h, complement_h, complement_to_h);
378 
379  // Initialize k-factors.
380  GEDGraph::NodeSizeTMap complement_graph_g_to_k_factor, g_to_k_factor;
381  std::size_t size_k_factor_g{0};
382  for (auto complement_node = complement_g.nodes().first; complement_node != complement_g.nodes().second; complement_node++) {
383  complement_graph_g_to_k_factor[*complement_node] = size_k_factor_g;
384  g_to_k_factor[complement_to_g.at(*complement_node)] = size_k_factor_g++;
385  }
386  KFactor_ k_factor_g(size_k_factor_g);
387  GEDGraph::NodeSizeTMap complement_graph_h_to_k_factor, h_to_k_factor;
388  std::size_t size_k_factor_h{0};
389  for (auto complement_node = complement_h.nodes().first; complement_node != complement_h.nodes().second; complement_node++) {
390  complement_graph_h_to_k_factor[*complement_node] = size_k_factor_h;
391  h_to_k_factor[complement_to_h.at(*complement_node)] = size_k_factor_h++;
392  }
393  KFactor_ k_factor_h(size_k_factor_h);
394 
395  // Find maximum k s.t. both complement graphs have a k-factor.
396  std::size_t num_nodes(g.num_nodes());
397  std::size_t max_k(num_nodes - 1 - std::max(g.maxdeg(), h.maxdeg()));
398  std::size_t k{max_k};
399  for (; k >= 1; k--) {
400  if (not compute_k_factor_(complement_g, k, complement_graph_g_to_k_factor, k_factor_g)) {
401  k_factor_g.clear_edges();
402  continue;
403  }
404  if (not compute_k_factor_(complement_h, k, complement_graph_h_to_k_factor, k_factor_h)) {
405  k_factor_g.clear_edges();
406  k_factor_h.clear_edges();
407  continue;
408  }
409  break;
410  }
411 
412  // Regularize g and h by adding those edges that are in the complements but not in the k-factors.
413  regularize_from_k_factor_(k_factor_g, g_to_k_factor, g);
414  regularize_from_k_factor_(k_factor_h, h_to_k_factor, h);
415 
416  // Return the degree of all nodes in the regularized graphs g and h.
417  return (num_nodes - 1 - k);
418 }
419 
420 template<class UserNodeLabel, class UserEdgeLabel>
421 std::size_t
423 naive_regularize_(GEDGraph & g, GEDGraph & h) const {
424 
425  // Add dummy nodes to smaller graph or to both graphs if the costs are not quasimetric.
426  if (this->ged_data_.quasimetric_costs()) {
427  fill_up_smaller_graph_(g, h);
428  }
429  else {
430  fill_up_both_graphs_(g, h);
431  }
432 
433  // Add missing edges to g.
434  for (auto node_1 = g.nodes().first; node_1 != g.nodes().second; node_1++) {
435  for (auto node_2 = node_1 + 1; node_2 != g.nodes().second; node_2++) {
436  if (not g.is_edge(*node_1, *node_2)) {
437  GEDGraph::EdgeID new_edge{g.add_edge(*node_1, *node_2)};
438  g.set_label(new_edge, dummy_label());
439  }
440  }
441  }
443 
444  // add missing edges to h.
445  for (auto node_1 = h.nodes().first; node_1 != h.nodes().second; node_1++) {
446  for (auto node_2 = node_1 + 1; node_2 != h.nodes().second; node_2++) {
447  if (not h.is_edge(*node_1, *node_2)) {
448  GEDGraph::EdgeID new_edge{h.add_edge(*node_1, *node_2)};
449  h.set_label(new_edge, dummy_label());
450  }
451  }
452  }
454 
455  return (g.num_nodes() - 1);
456 }
457 
458 template<class UserNodeLabel, class UserEdgeLabel>
459 void
461 regularize_from_k_factor_(const KFactor_ & k_factor, const GEDGraph::NodeSizeTMap & graph_to_k_factor, GEDGraph & graph) const {
462  for (auto node_1 = graph.nodes().first; node_1 != graph.nodes().second; node_1++) {
463  for (auto node_2 = node_1 + 1; node_2 != graph.nodes().second; node_2++) {
464  if (graph.is_edge(*node_1, *node_2)) {
465  continue;
466  }
467  if (not k_factor.contains_edge(graph_to_k_factor.at(*node_1), graph_to_k_factor.at(*node_2))) {
468  GEDGraph::EdgeID new_edge{graph.add_edge(*node_1, *node_2)};
469  graph.set_label(new_edge, dummy_label());
470  }
471  }
472  }
473  graph.setup_adjacency_matrix();
474 }
475 
476 template<class UserNodeLabel, class UserEdgeLabel>
477 void
479 fill_up_smaller_graph_(GEDGraph & g, GEDGraph & h) const {
480  if (g.num_nodes() < h.num_nodes()) {
481  for (std::size_t counter{g.num_nodes()}; counter < h.num_nodes(); counter++) {
482  GEDGraph::NodeID new_node{g.add_node()};
483  g.set_label(new_node, dummy_label());
484  }
486  }
487  else if (g.num_nodes() > h.num_nodes()) {
488  for (std::size_t counter{h.num_nodes()}; counter < g.num_nodes(); counter++) {
489  GEDGraph::NodeID new_node{h.add_node()};
490  h.set_label(new_node, dummy_label());
491  }
493  }
494 }
495 
496 template<class UserNodeLabel, class UserEdgeLabel>
497 void
499 fill_up_both_graphs_(GEDGraph & g, GEDGraph & h) const {
500  std::size_t g_num_nodes{g.num_nodes()};
501  for (std::size_t counter{0}; counter < h.num_nodes(); counter++) {
502  GEDGraph::NodeID new_node{g.add_node()};
503  g.set_label(new_node, dummy_label());
504  }
506  for (std::size_t counter{0}; counter < g_num_nodes; counter++) {
507  GEDGraph::NodeID new_node{h.add_node()};
508  h.set_label(new_node, dummy_label());
509  }
511 }
512 
513 template<class UserNodeLabel, class UserEdgeLabel>
514 void
516 construct_complement_graph_(const GEDGraph & graph, GEDGraph & complement_graph, GEDGraph::NodeNodeMap & complement_to_graph) const {
517  GEDGraph::NodeNodeMap graph_to_complement;
518  for (auto node = graph.nodes().first; node != graph.nodes().second; node++) {
519  GEDGraph::NodeID complement_node{complement_graph.add_node()};
520  graph_to_complement[*node] = complement_node;
521  complement_to_graph[complement_node] = *node;
522  }
523  for (auto node_1 = graph.nodes().first; node_1 != graph.nodes().second; node_1++) {
524  for (auto node_2 = node_1 + 1; node_2 != graph.nodes().second; node_2++) {
525  if (not graph.is_edge(*node_1, *node_2)) {
526  complement_graph.add_edge(graph_to_complement.at(*node_1),graph_to_complement.at(*node_2));
527  }
528  }
529  }
530  complement_graph.setup_adjacency_matrix();
531 }
532 
533 template<class UserNodeLabel, class UserEdgeLabel>
534 bool
536 compute_k_factor_(const GEDGraph & complement_graph, std::size_t k, const GEDGraph::NodeSizeTMap & complement_graph_to_k_factor, KFactor_ & k_factor) const {
537 
538  // Initialize output variables for construct_transformed_complement_graph_().
539  GEDGraph transformed_complement_graph{};
540  GEDGraph::NodeNodeMap transformed_to_original_nodes;
541 
542  /*
543  * Call construct_transformed_complement_graph_() and return false if the
544  * construction failed because k is larger than the minimum degree in the
545  * complement graph.
546  */
547  if (not construct_transformed_complement_graph_(complement_graph, k, transformed_complement_graph, transformed_to_original_nodes)) {
548  return false;
549  }
550 
551  // Initialize matching.
552  GEDGraph::NodeNodeMap matching;
553 
554  /*
555  * Use the boost implementation of edmonds blossom algorithm to compute a
556  * maximum matching in transformed complement graph. Reconstruct k-factor
557  * for the complement graph and return true if the matching is perfect.
558  * Otherwise, return false.
559  */
560  boost::edmonds_maximum_cardinality_matching(transformed_complement_graph.get_adjacency_list(), MateMap_(matching));
561  for (auto assignment = matching.begin(); assignment != matching.end(); assignment++) {
562  if (assignment->second == GEDGraph::dummy_node()) {
563  return false;
564  }
565  auto original_node = transformed_to_original_nodes.find(assignment->first);
566  auto assigned_original_node = transformed_to_original_nodes.find(assignment->second);
567  if (original_node != transformed_to_original_nodes.end() and assigned_original_node != transformed_to_original_nodes.end()) {
568  k_factor.add_edge(complement_graph_to_k_factor.at(original_node->second), complement_graph_to_k_factor.at(assigned_original_node->second));
569  }
570  }
571  return true;
572 }
573 
574 template<class UserNodeLabel, class UserEdgeLabel>
575 bool
577 construct_transformed_complement_graph_(const GEDGraph & complement_graph, std::size_t k, GEDGraph & transformed_complement_graph, GEDGraph::NodeNodeMap & transformed_to_original_nodes) const {
578 
579  // colect the degrees of the transformed graph's nodes and return false is mindeg < k
580  GEDGraph::NodeSizeTMap degrees;
581  for (auto node = complement_graph.nodes().first; node != complement_graph.nodes().second; node++) {
582  degrees[*node] = complement_graph.degree(*node);
583  if (degrees[*node] < k) {
584  return false;
585  }
586  }
587 
588  // check if to use type 1 gadgets or type 2 gadgets
589  std::map<GEDGraph::NodeID,bool> type_1_gadget;
590  for (auto node_deg_pair = degrees.begin(); node_deg_pair != degrees.end(); node_deg_pair++) {
591  type_1_gadget[node_deg_pair->first] = (k < (node_deg_pair->second / 2));
592  }
593 
594  // the IDs of the core, inner, and outer nodes
595  std::map<GEDGraph::NodeID, std::vector<GEDGraph::NodeID>> core_nodes;
596  std::map<GEDGraph::NodeID, std::vector<GEDGraph::NodeID>> inner_nodes;
597  std::map<GEDGraph::NodeID, std::vector<GEDGraph::NodeID>> outer_nodes;
598  for (auto node = complement_graph.nodes().first; node != complement_graph.nodes().second; node++) {
599  core_nodes[*node] = std::vector<GEDGraph::NodeID>();
600  inner_nodes[*node] = std::vector<GEDGraph::NodeID>();
601  outer_nodes[*node] = std::vector<GEDGraph::NodeID>();
602  }
603 
604  // add nodes to the transformed graph
605  for (auto node = complement_graph.nodes().first; node != complement_graph.nodes().second; node++) {
606  if (type_1_gadget.at(*node)) {
607  for (std::size_t counter{0}; counter < k; counter++) {
608  core_nodes[*node].push_back(transformed_complement_graph.add_node());
609  }
610  for (std::size_t counter{0}; counter < degrees.at(*node); counter++) {
611  inner_nodes[*node].push_back(transformed_complement_graph.add_node());
612  }
613  }
614  else {
615  for (std::size_t counter{0}; counter < degrees.at(*node) - k; counter++) {
616  core_nodes[*node].push_back(transformed_complement_graph.add_node());
617  }
618  }
619  for (std::size_t counter{0}; counter < degrees.at(*node); counter++) {
620  GEDGraph::NodeID transformed_node{transformed_complement_graph.add_node()};
621  outer_nodes[*node].push_back(transformed_node);
622  transformed_to_original_nodes[transformed_node] = *node;
623  }
624  }
625 
626  // add core and inner edges to the transformed graph
627  for (auto node = complement_graph.nodes().first; node != complement_graph.nodes().second; node++) {
628  if (type_1_gadget.at(*node)) {
629  for (auto inner_node = inner_nodes.at(*node).begin(), outer_node = outer_nodes.at(*node).begin(); inner_node != inner_nodes.at(*node).end(); inner_node++, outer_node++) {
630  transformed_complement_graph.add_edge(*inner_node, *outer_node);
631  for (auto core_node = core_nodes.at(*node).begin(); core_node != core_nodes.at(*node).end(); core_node++) {
632  transformed_complement_graph.add_edge(*core_node, *inner_node);
633  }
634  }
635  }
636  else {
637  for (auto core_node = core_nodes.at(*node).begin(); core_node != core_nodes.at(*node).end(); core_node++) {
638  for (auto outer_node = outer_nodes.at(*node).begin(); outer_node != outer_nodes.at(*node).end(); outer_node++) {
639  transformed_complement_graph.add_edge(*core_node, *outer_node);
640  }
641  }
642  }
643  }
644 
645  // add outer edges to the transformed graph
646  std::map<GEDGraph::NodeID, std::vector<GEDGraph::NodeID>::iterator> outer_nodes_itr;
647  for (auto node = complement_graph.nodes().first; node != complement_graph.nodes().second; node++) {
648  outer_nodes_itr[*node] = outer_nodes.at(*node).begin();
649  }
650  for (auto node_1 = complement_graph.nodes().first; node_1 != complement_graph.nodes().second; node_1++) {
651  for (auto node_2 = node_1 +1; node_2 != complement_graph.nodes().second; node_2++) {
652  if (complement_graph.is_edge(*node_1, *node_2)) {
653  transformed_complement_graph.add_edge(*outer_nodes_itr.at(*node_1)++, *outer_nodes_itr.at(*node_2)++);
654  }
655  }
656  }
657  return true;
658 }
659 
660 // === Definition of private class KFactor_. ===
661 template<class UserNodeLabel, class UserEdgeLabel>
664 KFactor_(LabelID num_nodes) :
665 num_nodes_{num_nodes},
666 is_edge_(num_nodes_ * num_nodes_, false) {}
667 
668 template<class UserNodeLabel, class UserEdgeLabel>
669 bool
672 contains_edge(std::size_t id_i, std::size_t id_k) const {
673  return is_edge_.at(id_i + id_k * num_nodes_);
674 }
675 
676 template<class UserNodeLabel, class UserEdgeLabel>
677 std::size_t
680 num_nodes() const {
681  return num_nodes_;
682 }
683 
684 template<class UserNodeLabel, class UserEdgeLabel>
685 void
688 add_edge(std::size_t id_i, std::size_t id_k) {
689  is_edge_[id_i + id_k * num_nodes_] = true;
690  is_edge_[id_k + id_i * num_nodes_] = true;
691 }
692 
693 template<class UserNodeLabel, class UserEdgeLabel>
694 void
697 clear_edges() {
698  for (auto edge_info = is_edge_.begin(); edge_info != is_edge_.end(); edge_info++) {
699  *edge_info = false;
700  }
701 }
702 
703 // === Definition of private class Weights_. ===
704 template<class UserNodeLabel, class UserEdgeLabel>
707 Weights_(std::size_t size_master) :
708 size_master_{size_master},
709 weights_(size_master_ * size_master_ * size_master_ * size_master_, 0.0){}
710 
711 template<class UserNodeLabel, class UserEdgeLabel>
712 double
715 get_weight(std::size_t row_in_master, std::size_t col_in_master, std::size_t row_sub_in_master, std::size_t col_sub_in_master) const {
716  return weights_.at(row_in_master + size_master_ * col_in_master + size_master_ * size_master_ * row_sub_in_master + size_master_ * size_master_ * size_master_ * col_sub_in_master);
717 }
718 
719 template<class UserNodeLabel, class UserEdgeLabel>
720 void
723 set_weight(std::size_t row_in_master, std::size_t col_in_master, std::size_t row_sub_in_master, std::size_t col_sub_in_master, double weight) {
724  weights_[row_in_master + size_master_ * col_in_master + size_master_ * size_master_ * row_sub_in_master + size_master_ * size_master_ * size_master_ * col_sub_in_master] = weight;
725 }
726 
727 // === Definition of private class SubproblemSolvers_. ===
728 template<class UserNodeLabel, class UserEdgeLabel>
731 SubproblemSolvers_(std::size_t size_master, std::size_t degree) :
732 size_master_{size_master},
733 degree_{degree},
734 subproblems_(size_master_ * size_master_, DMatrix(degree_, degree_)),
735 subproblem_solvers_(),
736 rows_sub_to_master_(size_master_ * size_master_),
737 cols_sub_to_master_(size_master_ * size_master_) {
738  for (const auto & subproblem : subproblems_) {
739  subproblem_solvers_.emplace_back(&subproblem);
740  }
741 }
742 
743 template<class UserNodeLabel, class UserEdgeLabel>
744 LSAPSolver &
747 solver(std::size_t row_in_master, std::size_t col_in_master) {
748  return subproblem_solvers_.at(row_in_master + size_master_ * col_in_master);
749 }
750 
751 template<class UserNodeLabel, class UserEdgeLabel>
752 const LSAPSolver &
755 solver(std::size_t row_in_master, std::size_t col_in_master) const {
756  return subproblem_solvers_.at(row_in_master + size_master_ * col_in_master);
757 }
758 
759 template<class UserNodeLabel, class UserEdgeLabel>
760 DMatrix &
763 subproblem(std::size_t row_in_master, std::size_t col_in_master) {
764  return subproblems_.at(row_in_master + size_master_ * col_in_master);
765 }
766 
767 template<class UserNodeLabel, class UserEdgeLabel>
768 const DMatrix &
771 subproblem(std::size_t row_in_master, std::size_t col_in_master) const {
772  return subproblems_.at(row_in_master + size_master_ * col_in_master);
773 }
774 
775 template<class UserNodeLabel, class UserEdgeLabel>
776 typename BranchTight<UserNodeLabel, UserEdgeLabel>::SizeTSizeTMap_ &
779 rows_subproblem_to_master(std::size_t row_in_master, std::size_t col_in_master) {
780  return rows_sub_to_master_.at(row_in_master + size_master_ * col_in_master);
781 }
782 
783 template<class UserNodeLabel, class UserEdgeLabel>
784 std::size_t
787 row_in_master(std::size_t row_in_master, std::size_t col_in_master, std::size_t row_in_subproblem) const {
788  return rows_sub_to_master_.at(row_in_master + size_master_ * col_in_master).at(row_in_subproblem);
789 }
790 
791 template<class UserNodeLabel, class UserEdgeLabel>
792 typename BranchTight<UserNodeLabel, UserEdgeLabel>::SizeTSizeTMap_ &
795 cols_subproblem_to_master(std::size_t row_in_master, std::size_t col_in_master) {
796  return cols_sub_to_master_.at(row_in_master + size_master_ * col_in_master);
797 }
798 
799 template<class UserNodeLabel, class UserEdgeLabel>
800 std::size_t
803 col_in_master(std::size_t row_in_master, std::size_t col_in_master, std::size_t col_in_subproblem) const {
804  return cols_sub_to_master_.at(row_in_master + size_master_ * col_in_master).at(col_in_subproblem);
805 }
806 
807 template<class UserNodeLabel, class UserEdgeLabel>
808 std::size_t
811 get_size() const {
812  return size_master_;
813 }
814 
815 template<class UserNodeLabel, class UserEdgeLabel>
816 std::size_t
819 get_degree() const {
820  return degree_;
821 }
822 
823 template<class UserNodeLabel, class UserEdgeLabel>
824 void
827 solve(std::size_t num_threads) {
828 #ifdef _OPENMP
829  omp_set_num_threads(num_threads - 1);
830 #pragma omp parallel for if(num_threads > 1)
831 #endif
832  for (std::size_t i = 0; i < subproblem_solvers_.size(); i++) {
833  subproblem_solvers_.at(i).solve();
834  }
835 }
836 
837 }
838 
839 #endif /* SRC_METHODS_BRANCH_TIGHT_IPP_ */
std::map< NodeID, NodeID > NodeNodeMap
Map that assigns node IDs to node IDs.
Definition: ged_graph.hpp:114
std::size_t num_nodes() const
Returns the number of nodes.
Definition: ged_graph.ipp:211
EdgeID add_edge(NodeID tail, NodeID head)
Adds a new edge to the graph.
Definition: ged_graph.ipp:86
double upper_bound() const
Returns the upper bound for GED.
Definition: result.ipp:51
virtual void ged_set_default_options_() final
Sets all options to default values.
virtual void ged_run_(const GEDGraph &g, const GEDGraph &h, Result &result) final
Runs the method with options specified by set_options().
std::map< NodeID, std::size_t > NodeSizeTMap
Map that assigns node IDs to integers.
Definition: ged_graph.hpp:116
A timer class that can be used by methods that support time limits.
Definition: timer.hpp:38
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
std::size_t degree(NodeID node) const
Returns node degree.
Definition: ged_graph.ipp:162
double lower_bound() const
Returns the lower bound for GED.
Definition: result.ipp:45
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
std::size_t maxdeg() const
Returns the maximum degree of the graph.
Definition: ged_graph.ipp:168
Computes lower and upper bounds for general edit costs.
bool is_non_redundant_node_map(std::size_t index_node_map)
Checks if a node map is already contained in the vector of all node maps and removes it if this is th...
Definition: result.ipp:80
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.
Matrix< double > DMatrix
Matrix with double entries.
Definition: matrix.hpp:199
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
LabelID get_edge_label(EdgeID edge) const
Returns the label of a given edge.
Definition: ged_graph.ipp:135
void set_label(NodeID node, LabelID label)
Sets a node&#39;s label.
Definition: ged_graph.ipp:114
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
virtual bool ged_parse_option_(const std::string &option, const std::string &arg) final
Parses one option.
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.
Global namespace for GEDLIB.
NodeID add_node()
Adds a new vertex to the graph.
Definition: ged_graph.ipp:74
This class solves LSAP instances by calling the library lsape available at https://bougleux.users.greyc.fr/lsape/.
Definition: lsap_solver.hpp:39
NodeMap & node_map(std::size_t index_node_map)
Provides access to a node map.
Definition: result.ipp:74
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
std::size_t NodeID
Internally used vertex ID type.
Definition: ged_graph.hpp:108
virtual std::string ged_valid_options_string_() const
Returns string of all valid options.
void setup_adjacency_matrix()
Prepares the adjacency matrix to allow quick retrieval of edges.
Definition: ged_graph.ipp:95
double get_slack(std::size_t row, std::size_t col) const
Returns the slack of a cell.
std::size_t num_rows() const
Returns the number of rows.
Definition: matrix.ipp:85