GEDLIB  1.0
ipfp.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_IPFP_IPP_
28 #define SRC_METHODS_IPFP_IPP_
29 
30 namespace ged {
31 
32 // === Definitions of destructor and constructor. ===
33 template<class UserNodeLabel, class UserEdgeLabel>
34 IPFP<UserNodeLabel, UserEdgeLabel>::
35 ~IPFP() {}
36 
37 template<class UserNodeLabel, class UserEdgeLabel>
38 IPFP<UserNodeLabel, UserEdgeLabel>::
39 IPFP(const GEDData<UserNodeLabel, UserEdgeLabel> & ged_data) :
40 LSBasedMethod<UserNodeLabel, UserEdgeLabel>(ged_data),
41 quadratic_model_{QAPE},
42 lsape_model_{LSAPESolver::ECBP},
43 epsilon_{0.001},
44 max_itrs_{100},
45 time_limit_in_sec_{0.0},
46 omega_{0.0},
47 qap_instance_() {}
48 
49 // === Definitions of member functions inherited from LSBasedMethod. ===
50 template<class UserNodeLabel, class UserEdgeLabel>
51 void
53 ls_run_from_initial_solution_(const GEDGraph & g, const GEDGraph & h, double lower_bound, const NodeMap & initial_node_map, NodeMap & output_node_map) {
54 
55  // Start timer.
56  Timer timer(time_limit_in_sec_);
57 
58  // Initialize output node map and upper bound.
59  output_node_map = initial_node_map;
60  double upper_bound{output_node_map.induced_cost()};
61 
62  // Initialize fractional solution x (is integral at start).
63  DMatrix x;
64  node_map_to_matrix_(initial_node_map, x);
65  double linear_cost_x{compute_induced_linear_cost_(qap_instance_, x)};
66  double overall_cost_x{initial_node_map.induced_cost()};
67  bool x_is_integral{true};
68 
69  // Initialize gradient direction b.
70  DMatrix b(qap_instance_.num_rows(), qap_instance_.num_cols());
71  double linear_cost_b{0.0};
72  double overall_cost_b{0.0};
73 
74  // Initialize alpha, beta, and the step width.
75  double alpha{0.0};
76  double beta{0.0};
77  double step_width{0.0};
78 
79  // Initialize linear problem and solvers.
80  DMatrix linear_problem(qap_instance_.num_rows(), qap_instance_.num_cols());
81  LSAPESolver lsape_solver;
82  lsape_solver.set_model(lsape_model_);
83  LSAPSolver lsap_solver;
84  if (quadratic_model_ == QAPE) {
85  lsape_solver.set_problem(&linear_problem);
86  }
87  else {
88  lsap_solver.set_problem(&linear_problem);
89  }
90  double min_linear_problem{0.0};
91 
92  // Initialize linear cost matrix.
93  DMatrix linear_cost_matrix(qap_instance_.num_rows(), qap_instance_.num_cols());
94  init_linear_cost_matrix_(qap_instance_, linear_cost_matrix);
95 
96  // Main loop.
97  for (std::size_t current_itr{1}; not termination_criterion_met_(timer, alpha, min_linear_problem, current_itr, lower_bound, upper_bound); current_itr++) {
98 
99  // Compute the next gradient direction b and update the upper bound and the output node map.
100  init_next_linear_problem_(qap_instance_, x, linear_cost_matrix, linear_problem);
101  if (quadratic_model_ == QAPE) {
102  solve_linear_problem_(qap_instance_, lsape_solver, min_linear_problem, linear_cost_b, overall_cost_b, b);
103  if (overall_cost_b < upper_bound) {
104  upper_bound = overall_cost_b;
105  util::construct_node_map_from_solver(lsape_solver, output_node_map);
106  this->ged_data_.compute_induced_cost(g, h, output_node_map);
107  if (std::fabs(upper_bound - output_node_map.induced_cost()) > 0.000001) {
108  throw Error("upper_bound: " + std::to_string(upper_bound) + ", linear_cost_b: " + std::to_string(linear_cost_b) + ", quadratic_cost_b: " + std::to_string(2*(upper_bound - linear_cost_b)) + ". Induced cost: " + std::to_string(output_node_map.induced_cost()) + ".");
109  }
110  output_node_map.set_induced_cost(upper_bound);
111  }
112  }
113  else {
114  solve_linear_problem_(qap_instance_, lsap_solver, min_linear_problem, linear_cost_b, overall_cost_b, b);
115  if (overall_cost_b < upper_bound) {
116  upper_bound = overall_cost_b;
117  util::construct_node_map_from_solver(lsap_solver, output_node_map);
118  output_node_map.set_induced_cost(upper_bound);
119  }
120  }
121 
122  // Determine the step width.
123  alpha = min_linear_problem - 2 * overall_cost_x + linear_cost_x;
124  beta = overall_cost_b + overall_cost_x - min_linear_problem - linear_cost_x;
125  if (beta > 0.00001) {
126  step_width = -alpha / (2 * beta);
127  }
128 
129  // Update the possibly fractional solution x.
130  if (beta <= 0.00001 or step_width >= 1) {
131  x_is_integral = true;
132  x = b;
133  overall_cost_x = overall_cost_b;
134  linear_cost_x = linear_cost_b;
135  }
136  else {
137  x_is_integral = false;
138  x += (b - x) * step_width;
139  overall_cost_x -= (alpha * alpha) / (4 * beta);
140  linear_cost_x = compute_induced_linear_cost_(qap_instance_, x);
141  }
142  }
143 
144 
145  // If converged solution x is fractional, project it to integral solution.
146  if (not x_is_integral) {
147  DMatrix projection_problem(qap_instance_.num_rows(), qap_instance_.num_cols(), 1.0);
148  projection_problem -= x;
149  NodeMap projected_node_map(output_node_map);
150  if (quadratic_model_ == QAPE) {
151  lsape_solver.set_problem(&projection_problem);
152  lsape_solver.solve();
153  util::construct_node_map_from_solver(lsape_solver, projected_node_map);
154  }
155  else {
156  lsap_solver.set_problem(&projection_problem);
157  lsap_solver.solve();
158  util::construct_node_map_from_solver(lsap_solver, projected_node_map);
159  }
160  this->ged_data_.compute_induced_cost(g, h, projected_node_map);
161  if (projected_node_map.induced_cost() < output_node_map.induced_cost()) {
162  output_node_map = projected_node_map;
163  }
164  }
165 }
166 
167 template<class UserNodeLabel, class UserEdgeLabel>
168 void
170 ls_runtime_init_(const GEDGraph & g, const GEDGraph & h) {
171  omega_ = this->ged_data_.max_edit_cost(g, h) + 10.0;
172  qap_instance_.init(this, g, h);
173 }
174 
175 template<class UserNodeLabel, class UserEdgeLabel>
176 void
179  quadratic_model_ = QAPE;
180  lsape_model_ = LSAPESolver::ECBP;
181  epsilon_ = 0.001;
182  max_itrs_ = 100;
183  time_limit_in_sec_ = 0.0;
184 }
185 
186 template<class UserNodeLabel, class UserEdgeLabel>
187 std::string
190  return "[--lsape-model <arg>] [--quadratic-model <arg>] [--iterations <arg>] [--time-limit <arg>] [--epsilon <arg>]";
191 }
192 
193 template<class UserNodeLabel, class UserEdgeLabel>
194 bool
196 ls_parse_option_(const std::string & option, const std::string & arg) {
197  if (option == "lsape-model") {
198  if (arg == "EBP") {
199  lsape_model_ = LSAPESolver::EBP;
200  }
201  else if (arg == "FLWC") {
202  lsape_model_ = LSAPESolver::FLWC;
203  }
204  else if (arg == "FLCC") {
205  lsape_model_ = LSAPESolver::FLCC;
206  }
207  else if (arg == "FBP") {
208  lsape_model_ = LSAPESolver::FBP;
209  }
210  else if (arg == "SFBP") {
211  lsape_model_ = LSAPESolver::SFBP;
212  }
213  else if (arg == "FBP0") {
214  lsape_model_ = LSAPESolver::FBP0;
215  }
216  else if (arg == "ECBP") {
217  lsape_model_ = LSAPESolver::ECBP;
218  }
219  else {
220  throw ged::Error(std::string("Invalid argument ") + arg + " for option lsape-model. Usage: options = \"[--lsape-model ECBP|EBP|FLWC|FLCC|FBP|SFBP|FBP0] [...]\"");
221  }
222  return true;
223  }
224  else if (option == "time-limit") {
225  try {
226  time_limit_in_sec_ = std::stod(arg);
227  }
228  catch (...) {
229  throw Error(std::string("Invalid argument \"") + arg + "\" for option time-limit. Usage: options = \"[--time-limit <convertible to double>] [...]");
230  }
231  return true;
232  }
233  else if (option == "quadratic-model") {
234  if (arg == "QAPE") {
235  quadratic_model_ = QAPE;
236  }
237  else if (arg == "B-QAP") {
238  quadratic_model_ = B_QAP;
239  }
240  else if (arg == "C-QAP") {
241  quadratic_model_ = C_QAP;
242  }
243  else {
244  throw ged::Error(std::string("Invalid argument ") + arg + " for option quadratic-model. Usage: options = \"[--quadratic-model QAPE|B-QAP|C-QAP] [...]\"");
245  }
246  return true;
247  }
248  else if (option == "iterations") {
249  try {
250  max_itrs_ = std::stoi(arg);
251  }
252  catch (...) {
253  throw Error(std::string("Invalid argument \"") + arg + "\" for option iterations. Usage: options = \"[--iterations <convertible to int>] [...]");
254  }
255  return true;
256  }
257  else if (option == "epsilon") {
258  try {
259  epsilon_ = std::stod(arg);
260  }
261  catch (...) {
262  throw Error(std::string("Invalid argument \"") + arg + "\" for option epsilon. Usage: options = \"[--epsilon <convertible to double greater 0>] [...]");
263  }
264  if (epsilon_ <= 0) {
265  throw Error(std::string("Invalid argument \"") + arg + "\" for option epsilon. Usage: options = \"[--epsilon <convertible to double greater 0>] [...]");
266  }
267  return true;
268  }
269  return false;
270 }
271 
272 // == Definitions of private helper member functions. ===
273 
274 template<class UserNodeLabel, class UserEdgeLabel>
275 void
277 node_map_to_matrix_(const NodeMap & node_map, DMatrix & matrix) const {
278  matrix.resize(qap_instance_.num_rows(), qap_instance_.num_cols());
279  matrix.set_to_val(0.0);
280  std::vector<NodeMap::Assignment> relation;
281  node_map.as_relation(relation);
282  for (auto assignment : relation) {
283  std::size_t row{undefined()};
284  std::size_t col{undefined()};
285  if (assignment.first != GEDGraph::dummy_node()) {
286  row = assignment.first;
287  }
288  if (assignment.second != GEDGraph::dummy_node()) {
289  col = assignment.second;
290  }
291  if ((row != undefined()) and (col != undefined())) {
292  matrix(row, col) = 1.0;
293  }
294  else if (row != undefined()) {
295  if (quadratic_model_ == QAPE) {
296  matrix(row, matrix.num_cols() - 1) = 1.0;
297  }
298  else if (quadratic_model_ == B_QAP){
299  matrix(row, row + qap_instance_.num_nodes_h()) = 1.0;
300  }
301  }
302  else if (col != undefined()) {
303  if (quadratic_model_ == QAPE) {
304  matrix(matrix.num_rows() - 1, col) = 1.0;
305  }
306  else if (quadratic_model_ == B_QAP){
307  matrix(col + qap_instance_.num_nodes_g(), col) = 1.0;
308  }
309  }
310  }
311 }
312 
313 template<class UserNodeLabel, class UserEdgeLabel>
314 void
316 init_linear_cost_matrix_(const QAPInstance_ & qap_instance, DMatrix & linear_cost_matrix) const {
317  for (std::size_t row_1{0}; row_1 < linear_cost_matrix.num_rows(); row_1++) {
318  for (std::size_t col_1{0}; col_1 < linear_cost_matrix.num_cols(); col_1++) {
319  linear_cost_matrix(row_1, col_1) = qap_instance(row_1, col_1);
320  }
321  }
322 }
323 
324 
325 template<class UserNodeLabel, class UserEdgeLabel>
326 void
328 init_next_linear_problem_(const QAPInstance_ & qap_instance, const DMatrix & x, const DMatrix & linear_cost_matrix, DMatrix & linear_problem) const {
329  std::vector<NodeMap::Assignment> support_x;
330  for (std::size_t col{0}; col < linear_problem.num_cols(); col++) {
331  for (std::size_t row{0}; row < linear_problem.num_rows(); row++) {
332  if (x(row, col) != 0) {
333  support_x.emplace_back(row, col);
334  }
335  }
336  }
337  for (std::size_t col{0}; col < linear_problem.num_cols(); col++) {
338  for (std::size_t row{0}; row < linear_problem.num_rows(); row++) {
339  linear_problem(row, col) = 0.0;
340  for (const auto & assignment : support_x) {
341  linear_problem(row, col) += qap_instance(row, col, assignment.first, assignment.second) * x(assignment.first, assignment.second);
342  }
343  }
344  }
345  linear_problem += linear_cost_matrix;
346 }
347 
348 template<class UserNodeLabel, class UserEdgeLabel>
349 bool
351 termination_criterion_met_(const Timer & timer, const double & alpha, const double & min_linear_problem, const std::size_t & current_itr, double lower_bound, double upper_bound) const {
352  if (current_itr == 1) {
353  return false;
354  }
355  if (lower_bound >= upper_bound) {
356  return true;
357  }
358  if (timer.expired()) {
359  return true;
360  }
361  if (max_itrs_ > 0 and current_itr > max_itrs_) {
362  return true;
363  }
364  if (min_linear_problem < 0.00001) {
365  return std::fabs(alpha) < epsilon_;
366  }
367  return std::fabs(alpha / min_linear_problem) < epsilon_;
368 }
369 
370 template<class UserNodeLabel, class UserEdgeLabel>
371 void
373 solve_linear_problem_(const QAPInstance_ & qap_instance, LSAPSolver & solver,
374  double & min_linear_problem, double & linear_cost_b, double & overall_cost_b, DMatrix & b) const {
375  solver.solve();
376  min_linear_problem = solver.minimal_cost();
377  solver_to_matrix_(solver, b);
378  linear_cost_b = compute_induced_linear_cost_(qap_instance, solver);
379  overall_cost_b = linear_cost_b + 0.5 * compute_induced_quadratic_cost_(qap_instance, solver);
380 }
381 
382 template<class UserNodeLabel, class UserEdgeLabel>
383 void
385 solve_linear_problem_(const QAPInstance_ & qap_instance, LSAPESolver & solver,
386  double & min_linear_problem, double & linear_cost_b, double & overall_cost_b, DMatrix & b) const {
387  solver.solve();
388  min_linear_problem = solver.minimal_cost();
389  solver_to_matrix_(solver, b);
390  linear_cost_b = compute_induced_linear_cost_(qap_instance, solver);
391  overall_cost_b = linear_cost_b + 0.5 * compute_induced_quadratic_cost_(qap_instance, solver);
392 }
393 
394 template<class UserNodeLabel, class UserEdgeLabel>
395 double
397 compute_induced_linear_cost_(const QAPInstance_ & qap_instance, const DMatrix & x) const {
398  double linear_cost{0.0};
399  for (std::size_t row{0}; row < qap_instance.num_rows(); row++) {
400  for (std::size_t col{0}; col < qap_instance.num_cols(); col++) {
401  linear_cost += qap_instance(row, col) * x(row, col);
402  }
403  }
404  return linear_cost;
405 }
406 
407 template<class UserNodeLabel, class UserEdgeLabel>
408 void
410 solver_to_matrix_(const LSAPSolver & solver, DMatrix & b) const {
411  b.matrix().setZero();
412  for (std::size_t row{0}; row < solver.num_rows(); row++) {
413  if (solver.get_assigned_col(row) < solver.num_cols()) {
414  b(row, solver.get_assigned_col(row)) = 1.0;
415  }
416  }
417 }
418 
419 template<class UserNodeLabel, class UserEdgeLabel>
420 void
422 solver_to_matrix_(const LSAPESolver & solver, DMatrix & b) const {
423  b.matrix().setZero();
424  for (std::size_t row{0}; row < solver.num_rows(); row++) {
425  b(row, solver.get_assigned_col(row)) = 1.0;
426  }
427  for (std::size_t col{0}; col < solver.num_cols(); col++) {
428  if (solver.get_assigned_row(col) == solver.num_rows()) {
429  b(solver.get_assigned_row(col), col) = 1.0;
430  }
431  }
432 }
433 
434 template<class UserNodeLabel, class UserEdgeLabel>
435 double
437 compute_induced_linear_cost_(const QAPInstance_ & qap_instance, const LSAPSolver & solver) const {
438  double linear_cost{0.0};
439  for (std::size_t row{0}; row < solver.num_rows(); row++) {
440  if (solver.get_assigned_col(row) < solver.num_cols()) {
441  linear_cost += qap_instance(row, solver.get_assigned_col(row));
442  }
443  }
444  return linear_cost;
445 }
446 
447 template<class UserNodeLabel, class UserEdgeLabel>
448 double
450 compute_induced_linear_cost_(const QAPInstance_ & qap_instance, const LSAPESolver & solver) const {
451  double linear_cost{0.0};
452  for (std::size_t row{0}; row < solver.num_rows(); row++) {
453  linear_cost += qap_instance(row, solver.get_assigned_col(row));
454  }
455  for (std::size_t col{0}; col < solver.num_cols(); col++) {
456  if (solver.get_assigned_row(col) == solver.num_rows()) {
457  linear_cost += qap_instance(solver.get_assigned_row(col), col);
458  }
459  }
460  return linear_cost;
461 }
462 
463 template<class UserNodeLabel, class UserEdgeLabel>
464 double
466 compute_induced_quadratic_cost_(const QAPInstance_ & qap_instance, const LSAPSolver & solver) const {
467  double quadratic_cost{0.0};
468  std::vector<NodeMap::Assignment> assignments;
469  for (std::size_t row{0}; row < solver.num_rows(); row++) {
470  if (solver.get_assigned_col(row) < solver.num_cols()) {
471  assignments.emplace_back(row, solver.get_assigned_col(row));
472  }
473  }
474  for (const auto & assignment_1 : assignments) {
475  for (const auto & assignment_2 : assignments) {
476  quadratic_cost += qap_instance(assignment_1.first, assignment_1.second, assignment_2.first, assignment_2.second);
477  }
478  }
479  return quadratic_cost;
480 }
481 
482 template<class UserNodeLabel, class UserEdgeLabel>
483 double
485 compute_induced_quadratic_cost_(const QAPInstance_ & qap_instance, const LSAPESolver & solver) const {
486  double quadratic_cost{0.0};
487  std::vector<NodeMap::Assignment> assignments;
488  for (std::size_t row{0}; row < solver.num_rows(); row++) {
489  assignments.emplace_back(row, solver.get_assigned_col(row));
490  }
491  for (std::size_t col{0}; col < solver.num_cols(); col++) {
492  if (solver.get_assigned_row(col) == solver.num_rows()) {
493  assignments.emplace_back(solver.get_assigned_row(col), col);
494  }
495  }
496  for (const auto & assignment_1 : assignments) {
497  for (const auto & assignment_2 : assignments) {
498  quadratic_cost += qap_instance(assignment_1.first, assignment_1.second, assignment_2.first, assignment_2.second);
499  }
500  }
501  return quadratic_cost;
502 }
503 
504 // === Definition of private class QAPInstance_. ===
505 template<class UserNodeLabel, class UserEdgeLabel>
508 QAPInstance_() :
509 ipfp_{nullptr},
510 g_{nullptr},
511 h_{nullptr},
512 num_nodes_g_{0},
513 num_nodes_h_{0},
514 translation_factor_{0.0} {}
515 
516 template<class UserNodeLabel, class UserEdgeLabel>
517 void
520 init(const IPFP<UserNodeLabel, UserEdgeLabel> * ipfp, const GEDGraph & g, const GEDGraph & h) {
521  ipfp_ = ipfp;
522  g_ = &g;
523  h_ = &h;
524  num_nodes_g_ = g_->num_nodes();
525  num_nodes_h_ = h_->num_nodes();
526  if (ipfp_->quadratic_model_ == C_QAP) {
527  if (num_nodes_g_ > num_nodes_h_) {
528  translation_factor_ = 3 * std::max(ipfp_->ged_data_.max_node_del_cost(*g_), ipfp_->ged_data_.max_edge_del_cost(*g_));
529  }
530  else if (num_nodes_g_ < num_nodes_h_) {
531  translation_factor_ = 3 * std::max(ipfp_->ged_data_.max_node_ins_cost(*h_), ipfp_->ged_data_.max_edge_ins_cost(*h_));
532  }
533  }
534 }
535 
536 template<class UserNodeLabel, class UserEdgeLabel>
537 std::size_t
540 num_rows() const {
541  switch(ipfp_->quadratic_model_) {
542  case QAPE:
543  return static_cast<std::size_t>(num_nodes_g_) + 1;
544  case B_QAP:
545  return static_cast<std::size_t>(num_nodes_g_) + static_cast<std::size_t>(num_nodes_h_);
546  default:
547  return static_cast<std::size_t>(num_nodes_g_);
548  }
549 }
550 
551 template<class UserNodeLabel, class UserEdgeLabel>
552 std::size_t
555 num_cols() const {
556  switch(ipfp_->quadratic_model_) {
557  case QAPE:
558  return static_cast<std::size_t>(num_nodes_h_) + 1;
559  case B_QAP:
560  return static_cast<std::size_t>(num_nodes_h_) + static_cast<std::size_t>(num_nodes_g_);
561  default:
562  return static_cast<std::size_t>(num_nodes_h_);
563  }
564 }
565 
566 template<class UserNodeLabel, class UserEdgeLabel>
567 std::size_t
570 num_nodes_g() const {
571  return num_nodes_g_;
572 }
573 
574 template<class UserNodeLabel, class UserEdgeLabel>
575 std::size_t
578 num_nodes_h() const {
579  return num_nodes_h_;
580 }
581 
582 template<class UserNodeLabel, class UserEdgeLabel>
583 double
586 operator() (std::size_t row, std::size_t col) const {
587  if (row >= num_rows() or col >= num_cols()) {
588  throw Error("Out of range error for FrankWolfe<UserNodeLabel, UserEdgeLabel>::QAPInstance_.");
589  }
590  double return_val{0.0};
591  switch(ipfp_->quadratic_model_) {
592  case QAP:
593  return_val += ipfp_->ged_data_.node_cost(g_->get_node_label(row), h_->get_node_label(col));
594  break;
595  case C_QAP:
596  return_val += ipfp_->ged_data_.node_cost(g_->get_node_label(row), h_->get_node_label(col));
597  if (num_nodes_g_ > num_nodes_h_) {
598  return_val -= ipfp_->ged_data_.node_cost(g_->get_node_label(row), dummy_label());
599  }
600  else if (num_nodes_g_ < num_nodes_h_) {
601  return_val -= ipfp_->ged_data_.node_cost(dummy_label(), h_->get_node_label(col));
602  }
603  if (num_nodes_g_ != num_nodes_h_) {
604  return_val += translation_factor_;
605  }
606  break;
607  case QAPE:
608  if (row < num_nodes_g_ and col < num_nodes_h_) {
609  return_val += ipfp_->ged_data_.node_cost(g_->get_node_label(row), h_->get_node_label(col));
610  }
611  else if (row < num_nodes_g_) {
612  return_val += ipfp_->ged_data_.node_cost(g_->get_node_label(row), dummy_label());
613  }
614  else if (col < num_nodes_h_) {
615  return_val += ipfp_->ged_data_.node_cost(dummy_label(), h_->get_node_label(col));
616  }
617  break;
618  case B_QAP:
619  if (row < num_nodes_g_ and col >= num_nodes_h_ and col != row + num_nodes_h_) {
620  return_val += ipfp_->omega_;
621  }
622  else if (row >= num_nodes_g_ and col < num_nodes_h_ and row != col + num_nodes_g_) {
623  return_val += ipfp_->omega_;
624  }
625  else if (row < num_nodes_g_ and col < num_nodes_h_) {
626  return_val += ipfp_->ged_data_.node_cost(g_->get_node_label(row), h_->get_node_label(col));
627  }
628  else if (row < num_nodes_g_) {
629  return_val += ipfp_->ged_data_.node_cost(g_->get_node_label(row), dummy_label());
630  }
631  else if (col < num_nodes_h_) {
632  return_val += ipfp_->ged_data_.node_cost(dummy_label(), h_->get_node_label(col));
633  }
634  break;
635  }
636  return return_val;
637 }
638 
639 template<class UserNodeLabel, class UserEdgeLabel>
640 double
643 quadratic_cost_b_qap_(std::size_t row_1, std::size_t col_1, std::size_t row_2, std::size_t col_2) const {
644  if (row_1 == row_2 or col_1 == col_2) {
645  return 0.0;
646  }
647  double return_val{0.0};
648  GEDGraph::EdgeID edge_g{g_->get_edge(row_1, row_2)};
649  GEDGraph::EdgeID edge_h{h_->get_edge(col_1, col_2)};
650  if (row_1 < num_nodes_g_ and col_1 >= num_nodes_h_ and col_1 != row_1 + num_nodes_h_) {
651  return_val += ipfp_->omega_;
652  }
653  else if (row_2 < num_nodes_g_ and col_2 >= num_nodes_h_ and col_2 != row_2 + num_nodes_h_) {
654  return_val += ipfp_->omega_;
655  }
656  else if (col_1 < num_nodes_h_ and row_1 >= num_nodes_g_ and row_1 != col_1 + num_nodes_g_) {
657  return_val += ipfp_->omega_;
658  }
659  else if (col_2 < num_nodes_h_ and row_2 >= num_nodes_g_ and row_2 != col_2 + num_nodes_g_) {
660  return_val += ipfp_->omega_;
661  }
662  else if (row_1 < num_nodes_g_ and col_1 < num_nodes_h_ and row_2 < num_nodes_g_ and col_2 < num_nodes_h_) {
663  if (edge_g != GEDGraph::dummy_edge() and edge_h != GEDGraph::dummy_edge()) {
664  return_val += ipfp_->ged_data_.edge_cost(g_->get_edge_label(edge_g), h_->get_edge_label(edge_h));
665  }
666  else if (edge_g != GEDGraph::dummy_edge()) {
667  return_val += ipfp_->ged_data_.edge_cost(g_->get_edge_label(edge_g), dummy_label());
668  }
669  else if (edge_h != GEDGraph::dummy_edge()) {
670  return_val += ipfp_->ged_data_.edge_cost(dummy_label(), h_->get_edge_label(edge_h));
671  }
672  }
673  else if (row_1 < num_nodes_g_ and row_2 < num_nodes_g_) {
674  if (edge_g != GEDGraph::dummy_edge()) {
675  return_val += ipfp_->ged_data_.edge_cost(g_->get_edge_label(edge_g), dummy_label());
676  }
677  }
678  else if (col_1 < num_nodes_h_ and col_2 < num_nodes_h_) {
679  if (edge_h != GEDGraph::dummy_edge()) {
680  return_val += ipfp_->ged_data_.edge_cost(dummy_label(), h_->get_edge_label(edge_h));
681  }
682  }
683  return return_val;
684 }
685 
686 template<class UserNodeLabel, class UserEdgeLabel>
687 double
690 quadratic_cost_c_qap_(std::size_t row_1, std::size_t col_1, std::size_t row_2, std::size_t col_2) const {
691  if (row_1 == row_2 or col_1 == col_2) {
692  return 0.0;
693  }
694  double return_val{0.0};
695  GEDGraph::EdgeID edge_g{g_->get_edge(row_1, row_2)};
696  GEDGraph::EdgeID edge_h{h_->get_edge(col_1, col_2)};
697  if (edge_g != GEDGraph::dummy_edge() and edge_h != GEDGraph::dummy_edge()) {
698  return_val += ipfp_->ged_data_.edge_cost(g_->get_edge_label(edge_g), h_->get_edge_label(edge_h));
699  }
700  else if (edge_g != GEDGraph::dummy_edge()) {
701  return_val += ipfp_->ged_data_.edge_cost(g_->get_edge_label(edge_g), dummy_label());
702  }
703  else if (edge_h != GEDGraph::dummy_edge()) {
704  return_val += ipfp_->ged_data_.edge_cost(dummy_label(), h_->get_edge_label(edge_h));
705  }
706  if (edge_g != GEDGraph::dummy_edge() and num_nodes_g_ > num_nodes_h_) {
707  return_val -= 3 * ipfp_->ged_data_.edge_cost(g_->get_edge_label(edge_g), dummy_label());
708  }
709  else if (edge_h != GEDGraph::dummy_edge() and num_nodes_g_ < num_nodes_h_) {
710  return_val -= 3 * ipfp_->ged_data_.edge_cost(dummy_label(), h_->get_edge_label(edge_h));
711  }
712  if (num_nodes_g_ != num_nodes_h_) {
713  return_val += translation_factor_;
714  }
715  return return_val;
716 }
717 
718 template<class UserNodeLabel, class UserEdgeLabel>
719 double
722 quadratic_cost_qap_(std::size_t row_1, std::size_t col_1, std::size_t row_2, std::size_t col_2) const {
723  if (row_1 == row_2 or col_1 == col_2) {
724  return 0.0;
725  }
726  double return_val{0.0};
727  GEDGraph::EdgeID edge_g{g_->get_edge(row_1, row_2)};
728  GEDGraph::EdgeID edge_h{h_->get_edge(col_1, col_2)};
729  if (edge_g != GEDGraph::dummy_edge() and edge_h != GEDGraph::dummy_edge()) {
730  return_val += ipfp_->ged_data_.edge_cost(g_->get_edge_label(edge_g), h_->get_edge_label(edge_h));
731  }
732  else if (edge_g != GEDGraph::dummy_edge()) {
733  return_val += ipfp_->ged_data_.edge_cost(g_->get_edge_label(edge_g), dummy_label());
734  }
735  else if (edge_h != GEDGraph::dummy_edge()) {
736  return_val += ipfp_->ged_data_.edge_cost(dummy_label(), h_->get_edge_label(edge_h));
737  }
738  return return_val;
739 }
740 
741 template<class UserNodeLabel, class UserEdgeLabel>
742 double
745 quadratic_cost_qape_(std::size_t row_1, std::size_t col_1, std::size_t row_2, std::size_t col_2) const {
746  if (row_1 == row_2 and col_1 == col_2) {
747  return 0.0;
748  }
749  if (row_1 == row_2 and row_1 < num_nodes_g_) {
750  return 0.0;
751  }
752  if (col_1 == col_2 and col_1 < num_nodes_h_) {
753  return 0.0;
754  }
755  double return_val{0.0};
756  GEDGraph::EdgeID edge_g{g_->get_edge(row_1, row_2)};
757  GEDGraph::EdgeID edge_h{h_->get_edge(col_1, col_2)};
758  if (row_1 < num_nodes_g_ and col_1 < num_nodes_h_ and row_2 < num_nodes_g_ and col_2 < num_nodes_h_) {
759  if (edge_g != GEDGraph::dummy_edge() and edge_h != GEDGraph::dummy_edge()) {
760  return_val += ipfp_->ged_data_.edge_cost(g_->get_edge_label(edge_g), h_->get_edge_label(edge_h));
761  }
762  else if (edge_g != GEDGraph::dummy_edge()) {
763  return_val += ipfp_->ged_data_.edge_cost(g_->get_edge_label(edge_g), dummy_label());
764  }
765  else if (edge_h != GEDGraph::dummy_edge()) {
766  return_val += ipfp_->ged_data_.edge_cost(dummy_label(), h_->get_edge_label(edge_h));
767  }
768  }
769  else if (row_1 < num_nodes_g_ and row_2 < num_nodes_g_) {
770  if (edge_g != GEDGraph::dummy_edge()) {
771  return_val += ipfp_->ged_data_.edge_cost(g_->get_edge_label(edge_g), dummy_label());
772  }
773  }
774  else if (col_1 < num_nodes_h_ and col_2 < num_nodes_h_) {
775  if (edge_h != GEDGraph::dummy_edge()) {
776  return_val += ipfp_->ged_data_.edge_cost(dummy_label(), h_->get_edge_label(edge_h));
777  }
778  }
779  return return_val;
780 }
781 
782 template<class UserNodeLabel, class UserEdgeLabel>
783 double
786 operator() (std::size_t row_1, std::size_t col_1, std::size_t row_2, std::size_t col_2) const {
787  if (row_1 >= num_rows() or col_1 >= num_cols() or row_2 >= num_rows() or col_2 >= num_cols()) {
788  throw Error("Out of range error for FrankWolfe<UserNodeLabel, UserEdgeLabel>::QAPInstance_.");
789  }
790  double return_val{0.0};
791  switch(ipfp_->quadratic_model_) {
792  case QAP:
793  return_val += quadratic_cost_qap_(row_1, col_1, row_2, col_2);
794  break;
795  case C_QAP:
796  return_val += quadratic_cost_c_qap_(row_1, col_1, row_2, col_2);
797  break;
798  case QAPE:
799  return_val += quadratic_cost_qape_(row_1, col_1, row_2, col_2);
800  break;
801  case B_QAP:
802  return_val += quadratic_cost_b_qap_(row_1, col_1, row_2, col_2);
803  break;
804  }
805  return return_val;
806 }
807 
808 }
809 
810 #endif /* SRC_METHODS_IPFP_IPP_ */
void set_model(const Model &model)
Makes the solver use a specific model for optimal solving.
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
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
std::size_t num_cols() const
Returns the number of real columns of the LSAPE problem instance, i.e, the number of columns of the i...
void resize(std::size_t num_rows, std::size_t num_cols)
Resizes the matrix.
Definition: matrix.ipp:92
std::size_t num_rows() const
Returns the number of rows of the LSAP problem instance.
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
bool expired() const
Checks if the time limit has expired.
Definition: timer.ipp:39
virtual bool ls_parse_option_(const std::string &options, const std::string &arg) final
Parses one option that is not among the ones shared by all derived classes of ged::LSBasedMethod.
Definition: ipfp.ipp:196
virtual void ls_set_default_options_() final
Sets all options that are not among the ones shared by all derived classes of ged::LSBasedMethod to d...
Definition: ipfp.ipp:178
void ls_run_from_initial_solution_(const GEDGraph &g, const GEDGraph &h, double lower_bound, const NodeMap &initial_node_map, NodeMap &output_node_map) final
Runs the local search from an initial node map.
Definition: ipfp.ipp:53
static EdgeID dummy_edge()
Returns a dummy edge.
Definition: ged_graph.ipp:68
void solve(int num_solutions=1)
Solves the LSAPE problem instance.
Reduction to extended LSAP instance without cost constraints.
double minimal_cost() const
Returns the cost of the computed solutions.
virtual std::string ls_valid_options_string_() const final
Returns string of all valid options that are not among the ones shared by all derived classes of ged:...
Definition: ipfp.ipp:189
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
void solve(int num_solutions=1)
Solves the LSAP problem instance.
Definition: lsap_solver.ipp:88
Runtime error class.
Definition: error.hpp:37
static NodeID dummy_node()
Returns a dummy node.
Definition: ged_graph.ipp:56
Adaption of Hungarian Algorithm to LSAPE.
Eigen::Matrix< ScalarT, Eigen::Dynamic, Eigen::Dynamic > & matrix()
Returns reference to the internal Eigen matrix.
Definition: matrix.ipp:228
double minimal_cost() const
Returns the cost of the computed solutions.
std::size_t get_assigned_row(std::size_t col, std::size_t solution_id=0) const
Returns the assigned row.
The normalized input graphs used by GEDLIB. All labels are integers.
Definition: ged_graph.hpp:104
std::size_t num_rows() const
Returns the number of real rows of the LSAPE problem instance, i.e, the number of rows of the interna...
detail::GedGraphAL::edge_descriptor EdgeID
Internally used edge ID type.
Definition: ged_graph.hpp:110
void set_induced_cost(double induced_cost)
Sets the induced cost of the node map.
Definition: node_map.ipp:204
constexpr LabelID dummy_label()
Returns a dummy label.
constexpr std::size_t undefined()
Returns undefined size.
Reduction to compact LSAP instance for quasimetric costs.
std::size_t get_assigned_col(std::size_t row, std::size_t solution_id=0) const
Returns the assigned column.
Global namespace for GEDLIB.
This class solves LSAP instances by calling the library lsape available at https://bougleux.users.greyc.fr/lsape/.
Definition: lsap_solver.hpp:39
std::size_t get_assigned_col(std::size_t row, std::size_t solution_id=0) const
Returns the assigned column.
void set_problem(const DMatrix *cost_matrix)
Sets the LSAP problem instance.
Definition: lsap_solver.ipp:56
void set_to_val(const ScalarT &val)
Sets all cells to val.
Definition: matrix.ipp:99
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
virtual void ls_runtime_init_(const GEDGraph &g, const GEDGraph &h) final
Initializes the method for a run between two graphs.
Definition: ipfp.ipp:170
std::size_t num_cols() const
Returns the number of columns of the LSAP problem instance.
Computes an upper bound for general edit costs.
Definition: ipfp.hpp:64
Reduction to compact LSAP instance for quasimetric costs.
std::size_t num_rows() const
Returns the number of rows.
Definition: matrix.ipp:85