GEDLIB  1.0
hybrid.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_HYBRID_IPP_
28 #define SRC_METHODS_HYBRID_IPP_
29 
30 namespace ged {
31 
32 // === Definitions of destructor and constructor. ===
33 template<class UserNodeLabel, class UserEdgeLabel>
34 Hybrid<UserNodeLabel, UserEdgeLabel>::
35 ~Hybrid() {}
36 
37 template<class UserNodeLabel, class UserEdgeLabel>
38 Hybrid<UserNodeLabel, UserEdgeLabel>::
39 Hybrid(const GEDData<UserNodeLabel, UserEdgeLabel> & ged_data) :
40 GEDMethod<UserNodeLabel, UserEdgeLabel>(ged_data),
41 lsape_model_{LSAPESolver::FLWC},
42 num_threads_{1},
43 time_limit_in_sec_{0.0} {}
44 
45 // === Definitions of member functions inherited from GEDMethod. ===
46 template<class UserNodeLabel, class UserEdgeLabel>
47 void
49 ged_run_(const GEDGraph & gg, const GEDGraph & hh, Result & result) {
50  Timer timer(time_limit_in_sec_);
51  GEDGraph g(gg);
52  GEDGraph h(hh);
53 
54  // Run Partition.
56  Result partition_result;
57  partition.run_as_util(g, h, partition_result);
58 
59  // Initialize and run BranchUniform without wildcards.
60  std::string options(std::string("--lsape-model ") + to_string_(lsape_model_) + " --threads " + std::to_string(num_threads_));
62  branch_uniform.set_options(options);
63  Result bu_result;
64  branch_uniform.run_as_util(gg, hh, bu_result);
65 
66  // Run BranchUniform DFS over mismatching substructures to find minimal wildcard Branch matching cost.
67  SubstructItr_ current_substruct{partition.get_unmatched_substructs_().cbegin()};
68  SubstructItr_ end_substructs{partition.get_unmatched_substructs_().cend()};
69 
70  if (current_substruct == end_substructs) {
71  result.set_lower_bound(bu_result.lower_bound());
72  }
73  else {
74  options += " --wildcards YES";
75  double lower_bound{std::numeric_limits<double>::infinity()};
76  if (branch_uniform_dfs_(timer, options, g, h, current_substruct, end_substructs, lower_bound)) {
77  result.set_lower_bound(std::max(partition_result.lower_bound() + lower_bound, bu_result.lower_bound()));
78  }
79  else {
80  result.set_lower_bound(std::max(partition_result.lower_bound(), bu_result.lower_bound()));
81  }
82  }
83 }
84 
85 template<class UserNodeLabel, class UserEdgeLabel>
86 void
89  lsape_model_ = LSAPESolver::FLWC;
90  num_threads_ = 1;
91  time_limit_in_sec_ = 0.;
92 }
93 
94 template<class UserNodeLabel, class UserEdgeLabel>
95 std::string
98  return "[--lsape-model <arg>] [--time-limit <arg>] [--threads <arg>]";
99 }
100 
101 template<class UserNodeLabel, class UserEdgeLabel>
102 bool
104 ged_parse_option_(const std::string & option, const std::string & arg) {
105  if (option == "lsape-model") {
106  if (arg == "EBP") {
107  lsape_model_ = LSAPESolver::EBP;
108  }
109  else if (arg == "FLWC") {
110  lsape_model_ = LSAPESolver::FLWC;
111  }
112  else if (arg == "FLCC") {
113  lsape_model_ = LSAPESolver::FLCC;
114  }
115  else if (arg == "FBP") {
116  lsape_model_ = LSAPESolver::FBP;
117  }
118  else if (arg == "SFBP") {
119  lsape_model_ = LSAPESolver::SFBP;
120  }
121  else if (arg == "FBP0") {
122  lsape_model_ = LSAPESolver::FBP0;
123  }
124  else if (arg == "ECBP") {
125  lsape_model_ = LSAPESolver::ECBP;
126  }
127  else {
128  throw Error(std::string("Invalid argument \"") + arg + "\" for option lsape-model. Usage: options = \"[--lsape-model ECBP|EBP|FLWC|FLCC|FBP|SFBP|FBP0] [...]\"");
129  }
130  return true;
131  }
132  else if (option == "time-limit") {
133  try {
134  time_limit_in_sec_ = std::stod(arg);
135  }
136  catch (...) {
137  throw Error(std::string("Invalid argument \"") + arg + "\" for option time-limit. Usage: options = \"[--time-limit <convertible to double>] [...]");
138  }
139  return true;
140  }
141  else if (option == "threads") {
142  try {
143  num_threads_ = std::stoi(arg);
144  }
145  catch (...) {
146  throw Error(std::string("Invalid argument \"") + arg + "\" for option threads. Usage: options = \"[--threads <convertible to int greater 0>] [...]");
147  }
148  if (num_threads_ <= 0) {
149  throw Error(std::string("Invalid argument \"") + arg + "\" for option threads. Usage: options = \"[--threads <convertible to int greater 0>] [...]");
150  }
151  return true;
152  }
153  return false;
154 }
155 
156 // === Definitions of private helper member functions. ===
157 template<class UserNodeLabel, class UserEdgeLabel>
158 bool
160 branch_uniform_dfs_(const Timer & timer, const std::string & options, GEDGraph & g, GEDGraph & h, SubstructItr_ current_substruct, SubstructItr_ end_substructs, double & lower_bound) {
161  if (timer.expired()) {
162  return false;
163  }
164 
165  // Base case: run BranchUniform on on g and h with wildcard labels and update lower bound.
166  if (current_substruct == end_substructs) {
167  Result bu_result;
169  branch_uniform.set_options(options);
170  branch_uniform.run_as_util(g, h, bu_result);
171  lower_bound = std::min(lower_bound, bu_result.lower_bound());
172  return true;
173  }
174 
175  // Recursively add wildcard labels to h.
176  bool success{true};
177  switch (current_substruct->type) {
179  h.set_label(current_substruct->node_1, dummy_label());
180  success = success and branch_uniform_dfs_(timer, options, g, h, current_substruct + 1, end_substructs, lower_bound);
181  if (not success) {
182  return false;
183  }
184  break;
185 
187  h.set_label(current_substruct->edge_1, dummy_label());
188  success = success and branch_uniform_dfs_(timer, options, g, h, current_substruct + 1, end_substructs, lower_bound);
189  break;
190 
192  h.set_label(current_substruct->node_1, dummy_label());
193  success = success and branch_uniform_dfs_(timer, options, g, h, current_substruct + 1, end_substructs, lower_bound);
194  if (not success) {
195  return false;
196  }
197  h.set_label(current_substruct->node_1, current_substruct->node_label_1);
198 
199  h.set_label(current_substruct->edge_1, dummy_label());
200  success = success and branch_uniform_dfs_(timer, options, g, h, current_substruct + 1, end_substructs, lower_bound);
201  if (not success) {
202  return false;
203  }
204  break;
205 
207  h.set_label(current_substruct->node_1, dummy_label());
208  success = success and branch_uniform_dfs_(timer, options, g, h, current_substruct + 1, end_substructs, lower_bound);
209  if (not success) {
210  return false;
211  }
212  h.set_label(current_substruct->node_1, current_substruct->node_label_1);
213 
214  h.set_label(current_substruct->edge_1, dummy_label());
215  success = success and branch_uniform_dfs_(timer, options, g, h, current_substruct + 1, end_substructs, lower_bound);
216  if (not success) {
217  return false;
218  }
219  h.set_label(current_substruct->edge_1, current_substruct->edge_label_1);
220 
221  h.set_label(current_substruct->node_2, dummy_label());
222  success = success and branch_uniform_dfs_(timer, options, g, h, current_substruct + 1, end_substructs, lower_bound);
223  if (not success) {
224  return false;
225  }
226  break;
227 
229  h.set_label(current_substruct->node_1, dummy_label());
230  success = success and branch_uniform_dfs_(timer, options, g, h, current_substruct + 1, end_substructs, lower_bound);
231  if (not success) {
232  return false;
233  }
234  h.set_label(current_substruct->node_1, current_substruct->node_label_1);
235 
236  h.set_label(current_substruct->edge_1, dummy_label());
237  success = success and branch_uniform_dfs_(timer, options, g, h, current_substruct + 1, end_substructs, lower_bound);
238  if (not success) {
239  return false;
240  }
241  h.set_label(current_substruct->edge_1, current_substruct->edge_label_1);
242 
243  h.set_label(current_substruct->edge_2, dummy_label());
244  success = success and branch_uniform_dfs_(timer, options, g, h, current_substruct + 1, end_substructs, lower_bound);
245  if (not success) {
246  return false;
247  }
248  break;
249  }
250  return success;
251 }
252 
253 template<class UserNodeLabel, class UserEdgeLabel>
254 std::string
256 to_string_(LSAPESolver::Model lsape_model) {
257  std::string method_string("");
258  switch (lsape_model) {
259  case LSAPESolver::ECBP:
260  method_string = "ECBP";
261  break;
262  case LSAPESolver::EBP:
263  method_string = "EBP";
264  break;
265  case LSAPESolver::FLWC:
266  method_string = "FLWC";
267  break;
268  case LSAPESolver::FLCC:
269  method_string = "FLCC";
270  break;
271  case LSAPESolver::FBP:
272  method_string = "FBP";
273  break;
274  case LSAPESolver::FBP0:
275  method_string = "FBP0";
276  break;
277  case LSAPESolver::SFBP:
278  method_string = "SFBP";
279  break;
280  }
281  return method_string;
282 }
283 
284 }
285 
286 #endif /* SRC_METHODS_HYBRID_IPP_ */
Computes a lower bound for uniform edit costs.
Definition: partition.hpp:42
Reduction to compact LSAP instance for quasimetric costs.
Reduction to compact LSAP instance for quasimetric costs.
virtual bool ged_parse_option_(const std::string &option, const std::string &arg) final
Parses one option.
Definition: hybrid.ipp:104
Reduction to compact LSAP instance without cost constraints.
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
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
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
Reduction to extended LSAP instance without cost constraints.
virtual void ged_set_default_options_() final
Sets all options to default values.
Definition: hybrid.ipp:88
virtual void ged_run_(const GEDGraph &g, const GEDGraph &h, Result &result) final
Runs the method with options specified by set_options().
Definition: hybrid.ipp:49
Runtime error class.
Definition: error.hpp:37
Adaption of Hungarian Algorithm to LSAPE.
void set_options(const std::string &options)
Sets the options of the method.
Definition: ged_method.ipp:99
void set_label(NodeID node, LabelID label)
Sets a node&#39;s label.
Definition: ged_graph.ipp:114
virtual std::string ged_valid_options_string_() const final
Returns string of all valid options.
Definition: hybrid.ipp:97
The normalized input graphs used by GEDLIB. All labels are integers.
Definition: ged_graph.hpp:104
constexpr LabelID dummy_label()
Returns a dummy label.
Computes a lower bound for uniform edit costs.
Definition: hybrid.hpp:47
Reduction to compact LSAP instance for quasimetric costs.
Global namespace for GEDLIB.
Computes lower and upper bounds for uniform edit costs.
Reduction to compact LSAP instance for quasimetric costs.
Model
Selects a model for solving LSAPE with the Hungarian algorithm.