GEDLIB  1.0
refine.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_REFINE_IPP_
28 #define SRC_METHODS_REFINE_IPP_
29 
30 namespace ged {
31 
32 // === Definitions of destructor and constructor. ===
33 template<class UserNodeLabel, class UserEdgeLabel>
34 Refine<UserNodeLabel, UserEdgeLabel>::
35 ~Refine() {}
36 
37 template<class UserNodeLabel, class UserEdgeLabel>
38 Refine<UserNodeLabel, UserEdgeLabel>::
39 Refine(const GEDData<UserNodeLabel, UserEdgeLabel> & ged_data) :
40 LSBasedMethod<UserNodeLabel, UserEdgeLabel>(ged_data),
41 max_swap_size_{2},
42 naive_{false},
43 add_dummy_assignment_{true} {}
44 
45 // === Definitions of member functions inherited from LSBasedMethod. ===
46 template<class UserNodeLabel, class UserEdgeLabel>
47 void
49 ls_run_from_initial_solution_(const GEDGraph & g, const GEDGraph & h, double lower_bound, const NodeMap & initial_node_map, NodeMap & output_node_map) {
50  output_node_map = initial_node_map;
51  double best_swap_cost{0.0};
52  Swap_ best_swap;
53  std::size_t swap_size{2};
54  while ((output_node_map.induced_cost() > lower_bound) and ((best_swap_cost < 0) or (swap_size <= max_swap_size_))) {
55  std::vector<NodeMap::Assignment> assignments;
56  output_node_map.as_relation(assignments);
57  if (add_dummy_assignment_) {
58  assignments.emplace_back(GEDGraph::dummy_node(), GEDGraph::dummy_node());
59  }
60  // terminate if there are not enough assignments to carry out swaps of the current swap size
61  if (swap_size > assignments.size()) {
62  break;
63  }
64  // initialization of swapped assignments
65  std::vector<std::size_t> swapped_original_indices;
66  for (std::size_t i=0; i<swap_size; i++) {
67  swapped_original_indices.emplace_back(i);
68  }
69  // test all possible subsets
70  do {
71  std::vector<NodeMap::Assignment> swapped_original_assignments;
72  for(std::size_t index : swapped_original_indices){
73  swapped_original_assignments.emplace_back(assignments[index]);
74  }
75  // continue if all nodes on the left or all nodes on the right are dummy nodes
76  bool real_node_on_left{false};
77  bool real_node_on_right{false};
78  for (const auto & assignment : swapped_original_assignments) {
79  real_node_on_left = (real_node_on_left or (assignment.first != GEDGraph::dummy_node()));
80  real_node_on_right = (real_node_on_right or (assignment.second != GEDGraph::dummy_node()));
81  if (real_node_on_left and real_node_on_right) {
82  break;
83  }
84  }
85  if (not (real_node_on_left and real_node_on_right)) {
86  continue;
87  }
88  // initialization of the swapping cycle inside the current subset
89  std::vector<std::size_t> cycle(swap_size-1);
90  for (std::size_t i=0; i < swap_size-1;i++) {
91  cycle[i] = i+1;
92  }
93  // test all possible cycle within the swapping set
94  do {
95  std::vector<NodeMap::Assignment> swapped_new_assignments;
96  NodeMap::Assignment original_assignment(swapped_original_assignments.at(0));
97  for (std::size_t i=0; i < swap_size - 1; i++) {
98  swapped_new_assignments.emplace_back(swapped_original_assignments.at(cycle.at(i)).first, original_assignment.second);
99  original_assignment = swapped_original_assignments.at(cycle.at(i));
100  }
101  swapped_new_assignments.emplace_back(swapped_original_assignments.at(0).first, original_assignment.second);
102  Swap_ current_swap;
103  current_swap.original_assignments = swapped_original_assignments;
104  current_swap.new_assignments = swapped_new_assignments;
105  double current_swap_cost = current_swap.cost(g, h, this->ged_data_, output_node_map, naive_);
106  if (current_swap_cost - best_swap_cost < -0.000000001) {
107  best_swap_cost = current_swap_cost;
108  best_swap = current_swap;
109  }
110  } while (std::next_permutation(cycle.data(), cycle.data() + swap_size-1));
111  } while (this->next_subset_(assignments.size(), swapped_original_indices));
112  if (best_swap_cost < -0.000000001) {
113  best_swap.do_swap(output_node_map, best_swap_cost);
114  best_swap_cost = 0.0;
115  swap_size = 2;
116  }
117  else {
118  best_swap_cost = 0.0;
119  swap_size++;
120  }
121  }
122 }
123 
124 
125 template<class UserNodeLabel, class UserEdgeLabel>
126 bool
128 next_subset_(const std::size_t set_size, std::vector<std::size_t> & subset){
129  // Returns FALSE if all subsets have been computed,
130  // modifies the in_out subset and returns true otherwise.
131 
132  std::size_t subset_size = subset.size();
133  std::size_t index_first_changed_element = subset_size - 1;
134  while (subset.at(index_first_changed_element) == set_size-subset_size+index_first_changed_element) {
135  if (index_first_changed_element==0) {
136  return false;
137  }
138  index_first_changed_element--;
139  }
140  std::size_t index_first_changed_element_in_set = subset[index_first_changed_element];
141 
142  for (std::size_t index_changed_element{index_first_changed_element}; index_changed_element < subset_size; index_changed_element++){
143  subset[index_changed_element] = index_first_changed_element_in_set + index_changed_element - index_first_changed_element+1;
144  }
145  return true;
146 
147 }
148 
149 template<class UserNodeLabel, class UserEdgeLabel>
150 bool
152 ls_parse_option_(const std::string & option, const std::string & arg) {
153  if (option == "max-swap-size") {
154  try {
155  max_swap_size_ = std::stoul(arg);
156  }
157  catch (...) {
158  throw Error(std::string("Invalid argument \"") + arg + "\" for option max-swap-size. Usage: options = \"[--max-swap-size <convertible to int greater equal 2>]\"");
159  }
160  if (max_swap_size_ < 2) {
161  throw Error(std::string("Invalid argument \"") + arg + "\" for option max-swap-size. Usage: options = \"[--max-swap-size <convertible to int greater equal 2>]\"");
162  }
163  return true;
164  }
165  else if (option == "naive") {
166  if (arg == "TRUE") {
167  naive_ = true;
168  }
169  else if (arg != "FALSE") {
170  throw Error(std::string("Invalid argument \"") + arg + "\" for option naive. Usage: options = \"[--naive <TRUE|FALSE>]\"");
171  }
172  return true;
173  }
174  else if (option == "add-dummy-assignment") {
175  if (arg == "FALSE") {
176  add_dummy_assignment_ = false;
177  }
178  else if (arg != "TRUE") {
179  throw Error(std::string("Invalid argument \"") + arg + "\" for option add-dummy-assignment. Usage: options = \"[--add-dummy-assignment <TRUE|FALSE>]\"");
180  }
181  return true;
182  }
183  return false;
184 }
185 
186 template<class UserNodeLabel, class UserEdgeLabel>
187 void
190  max_swap_size_ = 2;
191  naive_ = false;
192  add_dummy_assignment_ = true;
193 }
194 
195 template<class UserNodeLabel, class UserEdgeLabel>
196 std::string
199  return "[--max-swap-size <arg>] [--naive <arg>] [--add-dummy-assignment <arg>]";
200 }
201 
202 
203 template<class UserNodeLabel, class UserEdgeLabel>
204 double
206 Swap_::
207 cost(const GEDGraph & g, const GEDGraph & h, const GEDData<UserNodeLabel, UserEdgeLabel> & ged_data, NodeMap & node_map, bool naive) const {
208  double delta{0.0};
209  if (naive) {
210  double old_induced_cost{node_map.induced_cost()};
211  this->do_swap(node_map);
212  ged_data.compute_induced_cost(g, h, node_map);
213  delta = node_map.induced_cost() - old_induced_cost;
214  this->undo_swap(node_map);
215  node_map.set_induced_cost(old_induced_cost);
216  }
217  else if (this->original_assignments.size() == 2){
218  delta = ged_data.swap_cost(g, h, original_assignments[0], original_assignments[1], node_map);
219  }
220  else {
221  //collecting swapped vertices in both graphs
222 
223  std::vector<GEDGraph::NodeID> g_swapped_vertices;
224  std::vector<GEDGraph::NodeID> h_swapped_vertices;
225 
226  for(const auto & assignment : this->original_assignments){
227  g_swapped_vertices.push_back(assignment.first);
228  h_swapped_vertices.push_back(assignment.second);
229  }
230 
231  //collecting edges incident to swapped vertices in both graphs
232 
233  std::vector<GEDGraph::EdgeID> g_incident_edges;
234  std::vector<GEDGraph::EdgeID> h_incident_edges;
235 
236  for(std::size_t g_vertex_index{0}; g_vertex_index< g_swapped_vertices.size();g_vertex_index++){
237  GEDGraph::NodeID g_vertex = g_swapped_vertices[g_vertex_index];
238  if (g_vertex != GEDGraph::dummy_node()) {
239  for (auto edge = g.incident_edges(g_vertex).first; edge != g.incident_edges(g_vertex).second; edge++) {
240  // check if the edge has not been added yet
241  bool added_edge = false ;
242  for(std::size_t i=0; i<g_vertex_index; ++i){
243  if (g.head(*edge) == g_swapped_vertices[i]) {
244  added_edge = true;
245  break;
246  }
247  }
248  if (!added_edge){
249  g_incident_edges.push_back(*edge);
250  }
251  }
252  }
253  }
254  for(std::size_t h_vertex_index{0}; h_vertex_index< h_swapped_vertices.size();h_vertex_index++){
255  GEDGraph::NodeID h_vertex = h_swapped_vertices[h_vertex_index];
256  if (h_vertex != GEDGraph::dummy_node()) {
257  for (auto edge = h.incident_edges(h_vertex).first; edge != h.incident_edges(h_vertex).second; edge++) {
258  // check if the edge has not been added yet
259  bool added_edge = false ;
260  for(std::size_t i=0; i<h_vertex_index; ++i){
261  if (h.head(*edge) == h_swapped_vertices[i]) {
262  added_edge = true;
263  break;
264  }
265  }
266  if (!added_edge){
267  h_incident_edges.push_back(*edge);
268  }
269  }
270  }
271  }
272  // Compute node cost delta.
273  for(auto&& assignment: this->original_assignments){
274  delta -= ged_data.node_cost(g.get_node_label(assignment.first), h.get_node_label(assignment.second));
275  }
276  for(auto&& assignment: this->new_assignments){
277  delta += ged_data.node_cost(g.get_node_label(assignment.first), h.get_node_label(assignment.second));
278  }
279  // Compute negative part of edge cost delta.
280  for (const auto & edge : g_incident_edges) {
281  delta -= ged_data.edge_cost(g.get_edge_label(edge), h.get_edge_label(node_map.image(g.tail(edge)), node_map.image(g.head(edge))));
282  }
283  for (const auto & edge : h_incident_edges) {
284  if (not g.is_edge(node_map.pre_image(h.tail(edge)), node_map.pre_image(h.head(edge)))) {
285  delta -= ged_data.edge_cost(dummy_label(), h.get_edge_label(edge));
286  }
287  }
288  // Carry out the swap, (note : the node_map induced cost is unaffected)
289  this->do_swap(node_map);
290  // Compute positive part of edge cost delta.
291  for (const auto & edge : g_incident_edges) {
292  delta += ged_data.edge_cost(g.get_edge_label(edge), h.get_edge_label(node_map.image(g.tail(edge)), node_map.image(g.head(edge))));
293  }
294  for (const auto & edge : h_incident_edges) {
295  if (not g.is_edge(node_map.pre_image(h.tail(edge)), node_map.pre_image(h.head(edge)))) {
296  delta += ged_data.edge_cost(dummy_label(), h.get_edge_label(edge));
297  }
298  }
299  // Undo the swap.
300  this->undo_swap(node_map);
301  // Return the overall swap cost.
302  }
303  return delta;
304 }
305 
306 
307 template<class UserNodeLabel, class UserEdgeLabel>
308 void
310 Swap_::
311 do_swap(NodeMap & node_map, double delta_cost) const {
312  for(auto new_assignment : this->new_assignments){
313  node_map.add_assignment(new_assignment.first,new_assignment.second);
314  }
315  node_map.set_induced_cost(node_map.induced_cost()+delta_cost);
316 }
317 
318 template<class UserNodeLabel, class UserEdgeLabel>
319 void
321 Swap_::
322 undo_swap(NodeMap & node_map) const {
323  for(auto new_assignment : this->original_assignments)
324  node_map.add_assignment(new_assignment.first,new_assignment.second);
325 }
326 
327 template<class UserNodeLabel, class UserEdgeLabel>
328 std::string
330 Swap_::
331 print() const {
332  std::stringstream ss;
333  ss << "original assignments: { ";
334  for (const auto & assignment : original_assignments) {
335  ss << assignment << " ";
336  }
337  ss << "}";
338  ss << ", new assignments: { ";
339  for (const auto & assignment : new_assignments) {
340  ss << assignment << " ";
341  }
342  ss << "}";
343  return ss.str();
344 }
345 
346 
347 
348 }
349 
350 
351 #endif /* SRC_METHODS_REFINE_IPP_ */
Contains the standardized input data along with basic functionality.
Definition: ged_data.hpp:55
void compute_induced_cost(const GEDGraph &g, const GEDGraph &h, NodeMap &node_map) const
Computes the edit cost between two graphs induced by a node map.
Definition: ged_data.ipp:540
double edge_cost(LabelID label1, LabelID label2) const
Returns edge relabeling, insertion, or deletion cost.
Definition: ged_data.ipp:428
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
double node_cost(LabelID label1, LabelID label2) const
Returns node relabeling, insertion, or deletion cost.
Definition: ged_data.ipp:454
const GEDData< UserNodeLabel, UserEdgeLabel > & ged_data_
The data on which the method is run.
Definition: ged_method.hpp:124
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
GEDGraph::NodeID pre_image(GEDGraph::NodeID node) const
Returns pre-image of a node.
Definition: node_map.ipp:137
virtual void ls_set_default_options_()
Sets all options that are not among the ones shared by all derived classes of ged::LSBasedMethod to d...
Definition: refine.ipp:189
virtual bool ls_parse_option_(const std::string &option, const std::string &arg)
Parses one option that is not among the ones shared by all derived classes of ged::LSBasedMethod.
Definition: refine.ipp:152
double induced_cost() const
Returns the induced cost.
Definition: node_map.ipp:210
NodeID tail(EdgeID edge) const
Returns the tail of an edge.
Definition: ged_graph.ipp:178
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
double swap_cost(const GEDGraph &g, const GEDGraph &h, const NodeMap::Assignment &assignment_1, const NodeMap::Assignment &assignment_2, NodeMap &node_map) const
Computes the cost of swapping two assignments in a node map while leaving the node map unchanged...
Definition: ged_data.ipp:570
virtual std::string ls_valid_options_string_() const
Returns string of all valid options that are not among the ones shared by all derived classes of ged:...
Definition: refine.ipp:198
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
The normalized input graphs used by GEDLIB. All labels are integers.
Definition: ged_graph.hpp:104
void add_assignment(GEDGraph::NodeID i, GEDGraph::NodeID k)
Add node substitution, insertion, or deletion to the node map.
Definition: node_map.ipp:183
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.
Computes an upper bound for general edit costs.
Definition: refine.hpp:47
Global namespace for GEDLIB.
GEDGraph::NodeID image(GEDGraph::NodeID node) const
Returns image of a node.
Definition: node_map.ipp:110
virtual void ls_run_from_initial_solution_(const GEDGraph &g, const GEDGraph &h, double lower_bound, const NodeMap &initial_node_map, NodeMap &output_node_map) final
Runs the local search from an initial node map.
Definition: refine.ipp:49
std::size_t NodeID
Internally used vertex ID type.
Definition: ged_graph.hpp:108