27 #ifndef SRC_METHODS_ANCHOR_AWARE_GED_IPP_ 28 #define SRC_METHODS_ANCHOR_AWARE_GED_IPP_ 32 template<
class UserNodeLabel,
class UserEdgeLabel>
33 AnchorAwareGED<UserNodeLabel, UserEdgeLabel>::
36 template<
class UserNodeLabel,
class UserEdgeLabel>
37 AnchorAwareGED<UserNodeLabel, UserEdgeLabel>::
38 AnchorAwareGED(
const GEDData<UserNodeLabel, UserEdgeLabel> & ged_data) :
39 GEDMethod<UserNodeLabel, UserEdgeLabel>(ged_data),
42 lower_bound_method_{BRANCH_FAST},
44 time_limit_in_sec_{0.0},
45 map_root_to_root_{
false},
51 template<
class UserNodeLabel,
class UserEdgeLabel>
60 template<
class UserNodeLabel,
class UserEdgeLabel>
65 open_ = std::priority_queue<TreeNode_>();
66 omega_ = this->
ged_data_.max_edit_cost(g, h) + 10.0;
67 Timer timer(time_limit_in_sec_);
69 if (not this->
initialized_ and lower_bound_method_ == BRANCH_FAST) {
74 TreeNode_ current_node(g, h,
this);
75 if (current_node.is_leaf_node()) {
76 current_node.extend_leaf_node(g, h);
82 generate_best_child_(g, h, current_node);
84 if (not map_root_to_root_) {
86 ipfp.
set_options(
"--quadratic-model QAPE --threads " + std::to_string(num_threads_));
89 best_feasible_ = ipfp_result.
node_map(0);
93 current_node = open_.top();
95 if (current_node.lower_bound() >= best_feasible_.
induced_cost()) {
98 if (current_node.is_leaf_node()) {
99 current_node.extend_leaf_node(g, h);
100 if (current_node.induced_cost() < best_feasible_.
induced_cost()) {
101 best_feasible_ = current_node.node_map();
105 if (current_node.has_unexplored_sibling()) {
106 generate_best_sibling_(g, h, current_node);
108 generate_best_child_(g, h, current_node);
119 template<
class UserNodeLabel,
class UserEdgeLabel>
124 search_method_ = DFS;
125 lower_bound_method_ = BRANCH_FAST;
127 time_limit_in_sec_ = 0.0;
128 map_root_to_root_ =
false;
131 template<
class UserNodeLabel,
class UserEdgeLabel>
135 return "[--lsape-model <arg>] [--search-method <arg>] [--lower-bound-method <arg>] [--threads <arg>] [--time-limit] [--map-root-to-root <arg>]";
138 template<
class UserNodeLabel,
class UserEdgeLabel>
142 if (option ==
"lsape-model") {
146 else if (arg ==
"FLWC") {
149 else if (arg ==
"FLCC") {
152 else if (arg ==
"FBP") {
155 else if (arg ==
"SFBP") {
158 else if (arg ==
"FBP0") {
161 else if (arg ==
"ECBP") {
165 throw Error(std::string(
"Invalid argument \"") + arg +
"\" for option lsape-model. Usage: options = \"[--lsape-model ECBP|EBP|FLWC|FLCC|FBP|SFBP|FBP0] [...]\"");
169 else if (option ==
"search-method") {
171 search_method_ = DFS;
173 else if (arg ==
"ASTAR") {
174 search_method_ = ASTAR;
177 throw Error(std::string(
"Invalid argument \"") + arg +
"\" for option search-method. Usage: options = \"[--search-method DFS|ASTAR] [...]\"");
181 else if (option ==
"lower-bound-method") {
182 if (arg ==
"BRANCH") {
183 lower_bound_method_ = BRANCH;
185 else if (arg ==
"BRANCH_FAST") {
186 lower_bound_method_ = BRANCH_FAST;
189 throw Error(std::string(
"Invalid argument \"") + arg +
"\" for option lower-bound-method. Usage: options = \"[--lower-bound-method BRANCH|BRANCH_FAST] [...]\"");
193 else if (option ==
"time-limit") {
195 time_limit_in_sec_ = std::stod(arg);
198 throw Error(std::string(
"Invalid argument \"") + arg +
"\" for option time-limit. Usage: options = \"[--time-limit <convertible to double>] [...]");
202 else if (option ==
"threads") {
204 num_threads_ = std::stoi(arg);
207 throw Error(std::string(
"Invalid argument ") + arg +
" for option threads. Usage: options = \"[--threads <convertible to int greater 0>] [...]");
209 if (num_threads_ <= 0) {
210 throw Error(std::string(
"Invalid argument ") + arg +
" for option threads. Usage: options = \"[--threads <convertible to int greater 0>] [...]");
214 else if (option ==
"map-root-to-root") {
216 map_root_to_root_ =
true;
218 else if (arg ==
"FALSE") {
219 map_root_to_root_ =
false;
222 throw Error(std::string(
"Invalid argument ") + arg +
" for option map-root-to-root. Usage: options = \"[--map-root-to-root TRUE|FALSE] [...]");
230 template<
class UserNodeLabel,
class UserEdgeLabel>
237 template<
class UserNodeLabel,
class UserEdgeLabel>
242 return label < rhs.label;
246 template<
class UserNodeLabel,
class UserEdgeLabel>
252 template<
class UserNodeLabel,
class UserEdgeLabel>
257 for (
auto node = g.
nodes().first; node != g.
nodes().second; node++) {
258 sorted_edges_[*node] = std::vector<Edge_>();
260 sorted_edges_[*node].push_back(Edge_(g.
get_edge_label(*edge), *edge));
262 std::sort(sorted_edges_[*node].begin(), sorted_edges_[*node].end());
266 template<
class UserNodeLabel,
class UserEdgeLabel>
271 sorted_edges_ = rhs.sorted_edges_;
274 template<
class UserNodeLabel,
class UserEdgeLabel>
275 const std::vector<typename AnchorAwareGED<UserNodeLabel, UserEdgeLabel>::Edge_> &
279 return sorted_edges_.at(node);
283 template<
class UserNodeLabel,
class UserEdgeLabel>
289 is_matched_node_in_g_(g.
num_nodes(),
false),
290 is_matched_node_in_h_(h.
num_nodes(),
false),
292 dummy_node_is_candidate_in_h_{
true},
293 original_id_of_unmatched_nodes_in_h_(),
295 lower_bound_to_leaf_{0.0},
296 num_matched_nodes_in_g_{0},
297 num_matched_nodes_in_h_{0} {
298 if (exact_->map_root_to_root_) {
299 num_matched_nodes_in_g_++;
300 num_matched_nodes_in_h_++;
301 is_matched_node_in_g_[0] =
true;
302 is_matched_node_in_h_[0] =
true;
303 is_candidate_in_h_[0] =
false;
304 node_map_.add_assignment(0, 0);
309 template<
class UserNodeLabel,
class UserEdgeLabel>
313 exact_{tree_node.exact_},
314 node_map_(tree_node.node_map_),
315 is_matched_node_in_g_(tree_node.is_matched_node_in_g_),
316 is_matched_node_in_h_(tree_node.is_matched_node_in_h_),
317 is_candidate_in_h_(tree_node.is_candidate_in_h_),
318 dummy_node_is_candidate_in_h_(tree_node.dummy_node_is_candidate_in_h_),
319 original_id_of_unmatched_nodes_in_h_(tree_node.original_id_of_unmatched_nodes_in_h_),
320 induced_cost_{tree_node.induced_cost_},
321 lower_bound_to_leaf_{tree_node.lower_bound_to_leaf_},
322 num_matched_nodes_in_g_{tree_node.num_matched_nodes_in_g_},
323 num_matched_nodes_in_h_{tree_node.num_matched_nodes_in_h_} {}
325 template<
class UserNodeLabel,
class UserEdgeLabel>
331 node_map_ = rhs.node_map_;
332 is_matched_node_in_g_ = rhs.is_matched_node_in_g_;
333 is_matched_node_in_h_ = rhs.is_matched_node_in_h_;
334 is_candidate_in_h_ = rhs.is_candidate_in_h_;
335 dummy_node_is_candidate_in_h_ = rhs.dummy_node_is_candidate_in_h_;
336 original_id_of_unmatched_nodes_in_h_ = rhs.original_id_of_unmatched_nodes_in_h_;
337 induced_cost_ = rhs.induced_cost_;
338 lower_bound_to_leaf_ = rhs.lower_bound_to_leaf_;
339 num_matched_nodes_in_g_ = rhs.num_matched_nodes_in_g_;
340 num_matched_nodes_in_h_ = rhs.num_matched_nodes_in_h_;
343 template<
class UserNodeLabel,
class UserEdgeLabel>
348 return induced_cost_ + lower_bound_to_leaf_;
351 template<
class UserNodeLabel,
class UserEdgeLabel>
356 if (exact_->search_method_ == ASTAR){
357 return lower_bound() > rhs.lower_bound();
359 return num_matched_nodes_in_g_ < rhs.num_matched_nodes_in_g_;
362 template<
class UserNodeLabel,
class UserEdgeLabel>
367 if (num_matched_nodes_in_g_ < is_matched_node_in_g_.size()) {
368 return num_matched_nodes_in_g_;
373 template<
class UserNodeLabel,
class UserEdgeLabel>
378 if (num_matched_nodes_in_g_ > 0) {
379 return num_matched_nodes_in_g_ - 1;
384 template<
class UserNodeLabel,
class UserEdgeLabel>
389 for (
GEDGraph::NodeID node_in_h{0}; node_in_h < is_matched_node_in_h_.size(); node_in_h++) {
390 is_candidate_in_h_[node_in_h] = not is_matched_node_in_h_.at(node_in_h);
392 dummy_node_is_candidate_in_h_ =
true;
395 template<
class UserNodeLabel,
class UserEdgeLabel>
400 std::vector<NodeMap::Assignment> assignments;
402 for (
const auto & assignment : assignments) {
404 node_map_.add_assignment(assignment.first + num_matched_nodes_in_g_, original_id_of_unmatched_nodes_in_h_.at(assignment.second));
410 node_map_.add_assignment(
GEDGraph::dummy_node(), original_id_of_unmatched_nodes_in_h_.at(assignment.second));
413 exact_->ged_data_.compute_induced_cost(g, h, node_map_);
414 induced_cost_ = node_map_.induced_cost();
417 template<
class UserNodeLabel,
class UserEdgeLabel>
423 is_matched_node_in_g_[next_node_in_g] =
true;
427 num_matched_nodes_in_h_++;
428 is_matched_node_in_h_[next_node_in_h] =
true;
429 is_candidate_in_h_[next_node_in_h] =
false;
432 dummy_node_is_candidate_in_h_ =
false;
434 node_map_.add_assignment(next_node_in_g, next_node_in_h);
437 template<
class UserNodeLabel,
class UserEdgeLabel>
443 omp_set_num_threads(exact_->num_threads_ - 1);
444 #pragma omp parallel for if(exact_->num_threads_ > 1) 446 for (std::size_t row = 0; row < lsape_instance.
num_rows(); row++) {
447 for (std::size_t col = 0; col < lsape_instance.
num_cols(); col++) {
448 if ((row < lsape_instance.
num_rows() - 1) and (col < lsape_instance.
num_cols() - 1)) {
449 if ((row == 0) and (not is_candidate_in_h_.at(original_id_of_unmatched_nodes_in_h_.at(col)))) {
450 lsape_instance(row, col) = exact_->omega_;
453 if (exact_->lower_bound_method_ == BRANCH) {
454 lsape_instance(row, col) = compute_branch_substitution_cost_(g, h, row + num_matched_nodes_in_g_, original_id_of_unmatched_nodes_in_h_.at(col));
457 lsape_instance(row, col) = compute_branch_fast_substitution_cost_(g, h, row + num_matched_nodes_in_g_, original_id_of_unmatched_nodes_in_h_.at(col));
461 else if (row < lsape_instance.
num_rows() - 1) {
462 if ((row == 0) and (not dummy_node_is_candidate_in_h_)) {
463 lsape_instance(row, col) = exact_->omega_;
466 lsape_instance(row, col) = compute_deletion_cost_(g, row + num_matched_nodes_in_g_);
469 else if (col < lsape_instance.
num_cols() - 1) {
470 lsape_instance(row, col) = compute_insertion_cost_(h, original_id_of_unmatched_nodes_in_h_.at(col));
476 template<
class UserNodeLabel,
class UserEdgeLabel>
486 for (
auto ij = incident_edges_i.first; ij != incident_edges_i.second; ij++) {
487 if (not is_matched_node_in_g_.at(g.
head(*ij))) {
499 template<
class UserNodeLabel,
class UserEdgeLabel>
509 for (
auto kl = incident_edges_k.first; kl != incident_edges_k.second; kl++) {
510 if (not is_matched_node_in_h_.at(h.
head(*kl))) {
522 template<
class UserNodeLabel,
class UserEdgeLabel>
531 std::vector<NodeMap::Assignment> assignments;
532 node_map_.as_relation(assignments);
533 for (
const auto & assignment : assignments) {
548 std::vector<LabelID> edge_labels_to_unmatched_neighbours_i;
549 for (
auto ij = exact_->sorted_edges_.at(g.
id()).get_incident_edges(i).begin(); ij != exact_->sorted_edges_.at(g.
id()).get_incident_edges(i).end(); ij++) {
550 if (not is_matched_node_in_g_.at(g.
head(ij->edge_id))) {
551 edge_labels_to_unmatched_neighbours_i.push_back(ij->label);
554 std::vector<LabelID> edge_labels_to_unmatched_neighbours_k;
555 for (
auto kl = exact_->sorted_edges_.at(h.
id()).get_incident_edges(k).begin(); kl != exact_->sorted_edges_.at(h.
id()).get_incident_edges(k).end(); kl++) {
556 if (not is_matched_node_in_h_.at(h.
head(kl->edge_id))) {
557 edge_labels_to_unmatched_neighbours_k.push_back(kl->label);
562 if (edge_labels_to_unmatched_neighbours_i.size() < edge_labels_to_unmatched_neighbours_k.size()) {
563 double min_ins_cost{std::numeric_limits<double>::infinity()};
564 for (
auto label_h = edge_labels_to_unmatched_neighbours_k.begin(); label_h != edge_labels_to_unmatched_neighbours_k.end(); label_h++) {
565 min_ins_cost = std::min(min_ins_cost, exact_->ged_data_.edge_cost(
dummy_label(), *label_h));
567 cost +=
static_cast<double>(edge_labels_to_unmatched_neighbours_k.size() - edge_labels_to_unmatched_neighbours_i.size()) * min_ins_cost * 0.5;
571 if (edge_labels_to_unmatched_neighbours_i.size() > edge_labels_to_unmatched_neighbours_k.size()) {
572 double min_del_cost{std::numeric_limits<double>::infinity()};
573 for (
auto label_g = edge_labels_to_unmatched_neighbours_i.begin(); label_g != edge_labels_to_unmatched_neighbours_i.end(); label_g++) {
574 min_del_cost = std::min(min_del_cost, exact_->ged_data_.edge_cost(*label_g,
dummy_label()));
576 cost +=
static_cast<double>(edge_labels_to_unmatched_neighbours_i.size() - edge_labels_to_unmatched_neighbours_k.size()) * min_del_cost * 0.5;
580 double min_rel_cost{std::numeric_limits<double>::infinity()};
581 for (
auto label_g = edge_labels_to_unmatched_neighbours_i.begin(); label_g != edge_labels_to_unmatched_neighbours_i.end(); label_g++) {
582 for (
auto label_h = edge_labels_to_unmatched_neighbours_k.begin(); label_h != edge_labels_to_unmatched_neighbours_k.end(); label_h++) {
583 if (*label_g != *label_h) {
584 min_rel_cost = std::min(min_rel_cost, exact_->ged_data_.edge_cost(*label_g, *label_h));
590 std::size_t intersection_size{0};
591 auto label_g = edge_labels_to_unmatched_neighbours_i.begin();
592 auto label_h = edge_labels_to_unmatched_neighbours_k.begin();
593 while ((label_g != edge_labels_to_unmatched_neighbours_i.end()) and (label_h != edge_labels_to_unmatched_neighbours_k.end())) {
594 if (*label_g == *label_h) {
599 else if (*label_g < *label_h) {
608 std::size_t gamma(std::min(edge_labels_to_unmatched_neighbours_i.size(), edge_labels_to_unmatched_neighbours_k.size()) - intersection_size);
610 cost +=
static_cast<double>(gamma) * min_rel_cost * 0.5;
616 template<
class UserNodeLabel,
class UserEdgeLabel>
625 std::vector<NodeMap::Assignment> assignments;
626 node_map_.as_relation(assignments);
627 for (
const auto & assignment : assignments) {
642 std::vector<LabelID> edge_labels_to_unmatched_neighbours_i;
644 if (not is_matched_node_in_g_.at(g.
head(*ij))) {
645 edge_labels_to_unmatched_neighbours_i.push_back(g.
get_edge_label(*ij));
648 std::vector<LabelID> edge_labels_to_unmatched_neighbours_k;
650 if (not is_matched_node_in_h_.at(h.
head(*kl))) {
651 edge_labels_to_unmatched_neighbours_k.push_back(h.
get_edge_label(*kl));
654 DMatrix subproblem(edge_labels_to_unmatched_neighbours_i.size() + 1, edge_labels_to_unmatched_neighbours_k.size() + 1);
658 for (
auto label_ij = edge_labels_to_unmatched_neighbours_i.begin(); label_ij != edge_labels_to_unmatched_neighbours_i.end(); label_ij++, row++) {
659 subproblem(row, edge_labels_to_unmatched_neighbours_k.size()) = exact_->ged_data_.edge_cost(*label_ij,
ged::dummy_label()) * 0.5;
664 for (
auto label_kl = edge_labels_to_unmatched_neighbours_k.begin(); label_kl != edge_labels_to_unmatched_neighbours_k.end(); label_kl++, col++) {
665 subproblem(edge_labels_to_unmatched_neighbours_i.size(), col) = exact_->ged_data_.edge_cost(
ged::dummy_label(), *label_kl) * 0.5;
670 for (
auto label_ij = edge_labels_to_unmatched_neighbours_i.begin(); label_ij != edge_labels_to_unmatched_neighbours_i.end(); label_ij++, row++) {
672 for (
auto label_kl = edge_labels_to_unmatched_neighbours_k.begin(); label_kl != edge_labels_to_unmatched_neighbours_k.end(); label_kl++, col++) {
673 subproblem(row, col) = exact_->ged_data_.edge_cost(*label_ij, *label_kl) * 0.5;
679 subproblem_solver.
set_model(exact_->lsape_model_);
680 subproblem_solver.
solve();
687 template<
class UserNodeLabel,
class UserEdgeLabel>
692 lower_bound_to_leaf_ = lower_bound_to_leaf;
695 template<
class UserNodeLabel,
class UserEdgeLabel>
708 std::vector<NodeMap::Assignment> assignments;
709 node_map_.as_relation(assignments);
710 for (
const auto & assignment : assignments) {
725 template<
class UserNodeLabel,
class UserEdgeLabel>
730 return induced_cost_;
733 template<
class UserNodeLabel,
class UserEdgeLabel>
741 template<
class UserNodeLabel,
class UserEdgeLabel>
746 return ((num_matched_nodes_in_g_ == is_matched_node_in_g_.size()) or (num_matched_nodes_in_h_ == is_matched_node_in_h_.size()));
749 template<
class UserNodeLabel,
class UserEdgeLabel>
755 if (not is_matched_node_in_g_.at(i)) {
757 is_matched_node_in_g_[i] =
true;
758 num_matched_nodes_in_g_++;
762 if (not is_matched_node_in_h_.at(k)) {
764 is_matched_node_in_h_[k] =
true;
765 num_matched_nodes_in_h_++;
768 exact_->
ged_data_.compute_induced_cost(g, h, node_map_);
769 induced_cost_ = node_map_.induced_cost();
772 template<
class UserNodeLabel,
class UserEdgeLabel>
778 is_matched_node_in_g_[next_node_g] =
false;
780 node_map_.erase_image(next_node_g);
782 is_matched_node_in_h_[next_node_h] =
false;
783 num_matched_nodes_in_h_--;
787 template<
class UserNodeLabel,
class UserEdgeLabel>
792 if (num_matched_nodes_in_g_ == 0) {
795 if (dummy_node_is_candidate_in_h_) {
798 for (
auto is_candidate : is_candidate_in_h_) {
806 template<
class UserNodeLabel,
class UserEdgeLabel>
811 return is_matched_node_in_g_.size() - num_matched_nodes_in_g_;
814 template<
class UserNodeLabel,
class UserEdgeLabel>
819 return is_matched_node_in_h_.size() - num_matched_nodes_in_h_;
822 template<
class UserNodeLabel,
class UserEdgeLabel>
827 original_id_of_unmatched_nodes_in_h_.clear();
829 for (
auto is_matched_node : is_matched_node_in_h_) {
830 if (not is_matched_node) {
831 original_id_of_unmatched_nodes_in_h_.push_back(k);
837 template<
class UserNodeLabel,
class UserEdgeLabel>
841 sorted_edges_[graph.
id()] = SortedEdges_(graph);
844 template<
class UserNodeLabel,
class UserEdgeLabel>
849 next_tree_node.update_original_id_of_unmatched_nodes_in_h();
851 DMatrix lsape_instance(next_tree_node.num_unmatched_nodes_in_g() + 1, next_tree_node.num_unmatched_nodes_in_h() + 1, 0.0);
852 next_tree_node.populate_lsape_instance(g, h, lsape_instance);
857 lsape_solver.
solve();
858 next_tree_node.set_lower_bound_to_leaf(lsape_solver.
minimal_cost());
861 NodeMap extension(next_tree_node.num_unmatched_nodes_in_g(), next_tree_node.num_unmatched_nodes_in_h());
863 next_tree_node.append_next_assignment(extension);
866 if (update_induced_cost) {
867 next_tree_node.update_induced_cost(g, h);
869 open_.push(next_tree_node);
871 if (update_upper_bound) {
873 next_tree_node.append_extension(g, h, extension);
874 if (next_tree_node.induced_cost() < best_feasible_.
induced_cost()) {
875 best_feasible_ = next_tree_node.node_map();
881 template<
class UserNodeLabel,
class UserEdgeLabel>
885 TreeNode_ child_node(current_node);
886 child_node.prepare_for_child_generation();
887 generate_next_tree_node_(g, h, child_node,
true,
false);
890 template<
class UserNodeLabel,
class UserEdgeLabel>
894 TreeNode_ sibling_node(current_node);
895 sibling_node.prepare_for_sibling_generation();
896 generate_next_tree_node_(g, h, sibling_node,
false,
false);
void set_model(const Model &model)
Makes the solver use a specific model for optimal solving.
void erase_image(GEDGraph::NodeID node)
Erases image of a node.
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.
virtual void ged_run_(const GEDGraph &g, const GEDGraph &h, Result &result) final
Runs the method with options specified by set_options().
This class solves LSAPE instances by calling the library lsape available at https://bougleux.users.greyc.fr/lsape/.
Reduction to compact LSAP instance without cost constraints.
void as_relation(std::vector< Assignment > &relation) const
Constructs the representation as relation.
bool empty() const
Checks if the node map is empty.
A timer class that can be used by methods that support time limits.
EdgeID get_edge(NodeID tail, NodeID head) const
Retrieves an edge from its incident nodes.
const GEDData< UserNodeLabel, UserEdgeLabel > & ged_data_
The data on which the method is run.
void set_lower_bound(double lower_bound)
Sets the lower bound for GED.
NodeID head(EdgeID edge) const
Returns the head of an edge.
bool is_edge(NodeID tail, NodeID head) const
Checks if an edge exists.
bool expired() const
Checks if the time limit has expired.
void run_as_util(const GEDGraph &g, const GEDGraph &h, Result &result)
Runs the method with options specified by set_options().
void solve(int num_solutions=1)
Solves the LSAPE problem instance.
A wrapper structure for the result of calls to ged::GEDMethod::run_as_util() and ged::GEDMethod::ged_...
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 ...
Reduction to extended LSAP instance without cost constraints.
virtual void ged_init_() final
Initializes the method.
double induced_cost() const
Returns the induced cost.
std::size_t num_cols() const
Returns the number of columns.
std::size_t LabelID
Internally used type of node and edge labels.
std::size_t add_node_map(std::size_t num_nodes_g, std::size_t num_nodes_h)
Adds an empty node map to the result.
LabelID get_node_label(NodeID node) const
Returns the label of a given node.
static NodeID dummy_node()
Returns a dummy node.
static NodeID undefined_node()
Returns an undefined node.
Adaption of Hungarian Algorithm to LSAPE.
LabelID get_edge_label(EdgeID edge) const
Returns the label of a given edge.
void set_options(const std::string &options)
Sets the options of the method.
std::pair< incident_edge_iterator, incident_edge_iterator > incident_edges(NodeID node) const
Provides access to all incident edges of a node.
double minimal_cost() const
Returns the cost of the computed solutions.
bool initialized_
A flag that equals true if init() has been called and false otherwise.
virtual std::string ged_valid_options_string_() const final
Returns string of all valid options.
std::pair< node_iterator, node_iterator > nodes() const
Provides access to all nodes in the graph.
The normalized input graphs used by GEDLIB. All labels are integers.
detail::GedGraphAL::edge_descriptor EdgeID
Internally used edge ID type.
constexpr LabelID dummy_label()
Returns a dummy label.
Reduction to compact LSAP instance for quasimetric costs.
Global namespace for GEDLIB.
Computes the exact graph edit distance for general edit costs.
GEDGraph::NodeID image(GEDGraph::NodeID node) const
Returns image of a node.
NodeMap & node_map(std::size_t index_node_map)
Provides access to a node map.
virtual void ged_set_default_options_() final
Sets all options to default values.
void construct_node_map_from_solver(const Solver &solver, NodeMap &node_map, std::size_t solution_id=0)
Constructs a node map from a solution to LSAPE or LSAPE stored in a ged::LSAPESolver or a ged::LSAPSo...
Computes an upper bound for general edit costs.
std::size_t NodeID
Internally used vertex ID type.
Reduction to compact LSAP instance for quasimetric costs.
GraphID id() const
Returns the ID of the graph.
virtual bool ged_parse_option_(const std::string &option, const std::string &arg) final
Parses one option.
std::size_t num_rows() const
Returns the number of rows.