27 #ifndef SRC_METHODS_BRANCH_TIGHT_IPP_ 28 #define SRC_METHODS_BRANCH_TIGHT_IPP_ 33 template<
class UserNodeLabel,
class UserEdgeLabel>
34 BranchTight<UserNodeLabel, UserEdgeLabel>::
37 template<
class UserNodeLabel,
class UserEdgeLabel>
38 BranchTight<UserNodeLabel, UserEdgeLabel>::
39 BranchTight(
const GEDData<UserNodeLabel, UserEdgeLabel> & ged_data) :
40 GEDMethod<UserNodeLabel, UserEdgeLabel>(ged_data),
44 upper_bound_option_{BEST},
45 naive_regularization_{
true},
47 time_limit_in_sec_{0.0} {}
50 template<
class UserNodeLabel,
class UserEdgeLabel>
57 upper_bound_option_ = BEST;
58 naive_regularization_ =
true;
60 time_limit_in_sec_ = 0.0;
63 template<
class UserNodeLabel,
class UserEdgeLabel>
67 return "[--iterations <arg>] [--range <arg>] [--epsilon <arg>] [--upper-bound <arg>] [--regularize <arg>] [--threads <arg>] [--time-limit <arg>]";
70 template<
class UserNodeLabel,
class UserEdgeLabel>
74 if (option ==
"iterations") {
76 max_itrs_ = std::stoul(arg);
79 throw Error(std::string(
"Invalid argument \"") + arg +
"\" for option iterations. Usage: options = \"[--iterations <convertible to int>] [...]");
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.");
86 else if (option ==
"time-limit") {
88 time_limit_in_sec_ = std::stod(arg);
91 throw Error(std::string(
"Invalid argument \"") + arg +
"\" for option time-limit. Usage: options = \"[--time-limit <convertible to double>] [...]");
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.");
98 else if (option ==
"range") {
100 range_ = std::stod(arg);
103 throw Error(std::string(
"Invalid argument \"") + arg +
"\" for option range. Usage: options = \"[--range <convertible to double>] [...]");
107 else if (option ==
"epsilon") {
109 epsilon_ = std::stod(arg);
112 throw Error(std::string(
"Invalid argument \"") + arg +
"\" for option epsilon. Usage: options = \"[--epsilon <convertible to double>] [...]");
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.");
119 else if (option ==
"upper-bound") {
120 if (arg ==
"FIRST") {
121 upper_bound_option_ = FIRST;
123 else if (arg ==
"LAST") {
124 upper_bound_option_ = LAST;
126 else if (arg ==
"BEST") {
127 upper_bound_option_ = BEST;
129 else if (arg ==
"NO") {
130 upper_bound_option_ = NO;
133 throw ged::Error(std::string(
"Invalid argument ") + arg +
" for option upper-bound. Usage: options = \"[--upper-bound NO|FIRST|LAST|BEST]\"");
137 else if (option ==
"regularize") {
138 if (arg ==
"K-FACTOR") {
139 naive_regularization_ =
false;
141 else if (arg ==
"NAIVE") {
142 naive_regularization_ =
true;
145 throw Error(std::string(
"Invalid argument \"") + arg +
"\" for option range. Usage: options = \"[--regularize NAIVE|K-FACTOR] [...]");
149 else if (option ==
"threads") {
151 num_threads_ = std::stoi(arg);
154 throw Error(std::string(
"Invalid argument \"") + arg +
"\" for option threads. Usage: options = \"[--threads <convertible to int greater 0>] [...]");
156 if (max_itrs_ <= 0) {
157 throw Error(std::string(
"Invalid argument \"") + arg +
"\" for option threads. Usage: options = \"[--threads <convertible to int greater 0>] [...]");
164 template<
class UserNodeLabel,
class UserEdgeLabel>
169 Timer timer(time_limit_in_sec_);
174 std::size_t degree{};
175 if (naive_regularization_) {
176 degree = naive_regularize_(g, h);
179 degree = regularize_(g, h);
184 SubproblemSolvers_ subproblem_solvers(static_cast<std::size_t>(g.
num_nodes()), degree);
185 init_subproblems_(g, h, subproblem_solvers);
187 init_node_costs_(g, h, node_costs);
188 Weights_ weights(static_cast<std::size_t>(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++) {
196 if ((current_itr > 1) and timer.
expired()) {
201 if (current_itr > 1) {
202 update_weights_(master_problem_solver, degree, subproblem_solvers, weights);
203 update_subproblem_costs_(weights, degree, subproblem_solvers);
205 subproblem_solvers.solve(num_threads_);
208 update_master_problem_costs_(subproblem_solvers, node_costs, master_problem);
209 master_problem_solver.solve();
212 if (current_itr > 1) {
219 if (upper_bound_option_ == BEST or (upper_bound_option_ == FIRST and current_itr == 1)) {
230 if (upper_bound_option_ == LAST) {
240 template<
class UserNodeLabel,
class UserEdgeLabel>
247 if ((max_itrs_ > 0) and (max_itrs_ < current_itr)) {
250 if ((epsilon_ > 0) and (epsilon_ > last_improvement)) {
253 if ((range_ > 0) and (range_ < result.
lower_bound())) {
259 template<
class UserNodeLabel,
class UserEdgeLabel>
263 for (std::size_t row_master{0}; row_master < subproblems_solver.get_size(); row_master++) {
268 for (std::size_t col_master{0}; col_master < subproblems_solver.get_size(); col_master++) {
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);
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);
285 for (
auto ij = incident_edges_i.first; ij != incident_edges_i.second; ij++) {
287 for (
auto kl = incident_edges_k.first; kl != incident_edges_k.second; kl++) {
296 template<
class UserNodeLabel,
class UserEdgeLabel>
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();
307 template<
class UserNodeLabel,
class UserEdgeLabel>
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++) {
318 template<
class UserNodeLabel,
class UserEdgeLabel>
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)};
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);
338 template<
class UserNodeLabel,
class UserEdgeLabel>
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;
358 template<
class UserNodeLabel,
class UserEdgeLabel>
364 if (this->
ged_data_.quasimetric_costs(g, h)) {
365 fill_up_smaller_graph_(g, h);
368 fill_up_both_graphs_(g, h);
374 construct_complement_graph_(g, complement_g, complement_to_g);
377 construct_complement_graph_(h, complement_h, complement_to_h);
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++;
386 KFactor_ k_factor_g(size_k_factor_g);
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++;
393 KFactor_ k_factor_h(size_k_factor_h);
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();
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();
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);
417 return (num_nodes - 1 - k);
420 template<
class UserNodeLabel,
class UserEdgeLabel>
426 if (this->
ged_data_.quasimetric_costs()) {
427 fill_up_smaller_graph_(g, h);
430 fill_up_both_graphs_(g, h);
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)) {
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)) {
458 template<
class UserNodeLabel,
class UserEdgeLabel>
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)) {
467 if (not k_factor.contains_edge(graph_to_k_factor.at(*node_1), graph_to_k_factor.at(*node_2))) {
476 template<
class UserNodeLabel,
class UserEdgeLabel>
496 template<
class UserNodeLabel,
class UserEdgeLabel>
501 for (std::size_t counter{0}; counter < h.
num_nodes(); counter++) {
506 for (std::size_t counter{0}; counter < g_num_nodes; counter++) {
513 template<
class UserNodeLabel,
class UserEdgeLabel>
518 for (
auto node = graph.
nodes().first; node != graph.
nodes().second; node++) {
520 graph_to_complement[*node] = complement_node;
521 complement_to_graph[complement_node] = *node;
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));
533 template<
class UserNodeLabel,
class UserEdgeLabel>
539 GEDGraph transformed_complement_graph{};
547 if (not construct_transformed_complement_graph_(complement_graph, k, transformed_complement_graph, transformed_to_original_nodes)) {
560 boost::edmonds_maximum_cardinality_matching(transformed_complement_graph.get_adjacency_list(), MateMap_(matching));
561 for (
auto assignment = matching.begin(); assignment != matching.end(); assignment++) {
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));
574 template<
class UserNodeLabel,
class UserEdgeLabel>
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) {
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));
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>();
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());
610 for (std::size_t counter{0}; counter < degrees.at(*node); counter++) {
611 inner_nodes[*node].push_back(transformed_complement_graph.
add_node());
615 for (std::size_t counter{0}; counter < degrees.at(*node) - k; counter++) {
616 core_nodes[*node].push_back(transformed_complement_graph.
add_node());
619 for (std::size_t counter{0}; counter < degrees.at(*node); counter++) {
621 outer_nodes[*node].push_back(transformed_node);
622 transformed_to_original_nodes[transformed_node] = *node;
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);
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);
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();
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)++);
661 template<
class UserNodeLabel,
class UserEdgeLabel>
665 num_nodes_{num_nodes},
666 is_edge_(num_nodes_ * num_nodes_,
false) {}
668 template<
class UserNodeLabel,
class UserEdgeLabel>
673 return is_edge_.at(id_i + id_k * num_nodes_);
676 template<
class UserNodeLabel,
class UserEdgeLabel>
684 template<
class UserNodeLabel,
class UserEdgeLabel>
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;
693 template<
class UserNodeLabel,
class UserEdgeLabel>
698 for (
auto edge_info = is_edge_.begin(); edge_info != is_edge_.end(); edge_info++) {
704 template<
class UserNodeLabel,
class UserEdgeLabel>
708 size_master_{size_master},
709 weights_(size_master_ * size_master_ * size_master_ * size_master_, 0.0){}
711 template<
class UserNodeLabel,
class UserEdgeLabel>
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);
719 template<
class UserNodeLabel,
class UserEdgeLabel>
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;
728 template<
class UserNodeLabel,
class UserEdgeLabel>
732 size_master_{size_master},
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);
743 template<
class UserNodeLabel,
class UserEdgeLabel>
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);
751 template<
class UserNodeLabel,
class UserEdgeLabel>
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);
759 template<
class UserNodeLabel,
class UserEdgeLabel>
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);
767 template<
class UserNodeLabel,
class UserEdgeLabel>
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);
775 template<
class UserNodeLabel,
class UserEdgeLabel>
776 typename BranchTight<UserNodeLabel, UserEdgeLabel>::SizeTSizeTMap_ &
780 return rows_sub_to_master_.at(row_in_master + size_master_ * col_in_master);
783 template<
class UserNodeLabel,
class UserEdgeLabel>
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);
791 template<
class UserNodeLabel,
class UserEdgeLabel>
792 typename BranchTight<UserNodeLabel, UserEdgeLabel>::SizeTSizeTMap_ &
796 return cols_sub_to_master_.at(row_in_master + size_master_ * col_in_master);
799 template<
class UserNodeLabel,
class UserEdgeLabel>
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);
807 template<
class UserNodeLabel,
class UserEdgeLabel>
815 template<
class UserNodeLabel,
class UserEdgeLabel>
823 template<
class UserNodeLabel,
class UserEdgeLabel>
827 solve(std::size_t num_threads) {
829 omp_set_num_threads(num_threads - 1);
830 #pragma omp parallel for if(num_threads > 1) 832 for (std::size_t i = 0; i < subproblem_solvers_.size(); i++) {
833 subproblem_solvers_.at(i).solve();
std::map< NodeID, NodeID > NodeNodeMap
Map that assigns node IDs to node IDs.
std::size_t num_nodes() const
Returns the number of nodes.
EdgeID add_edge(NodeID tail, NodeID head)
Adds a new edge to the graph.
double upper_bound() const
Returns the upper bound for GED.
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.
A timer class that can be used by methods that support time limits.
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.
std::size_t degree(NodeID node) const
Returns node degree.
double lower_bound() const
Returns the lower bound for GED.
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 ...
std::size_t maxdeg() const
Returns the maximum degree of the graph.
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...
std::size_t num_cols() const
Returns the number of columns.
std::size_t LabelID
Internally used type of node and edge labels.
Matrix< double > DMatrix
Matrix with double entries.
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.
LabelID get_edge_label(EdgeID edge) const
Returns the label of a given edge.
void set_label(NodeID node, LabelID label)
Sets a node's label.
std::pair< incident_edge_iterator, incident_edge_iterator > incident_edges(NodeID node) const
Provides access to all incident edges of a node.
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.
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.
Global namespace for GEDLIB.
NodeID add_node()
Adds a new vertex to the graph.
This class solves LSAP instances by calling the library lsape available at https://bougleux.users.greyc.fr/lsape/.
NodeMap & node_map(std::size_t index_node_map)
Provides access to a node map.
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...
std::size_t NodeID
Internally used vertex ID type.
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.
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.