27 #ifndef SRC_METHODS_IPFP_IPP_ 28 #define SRC_METHODS_IPFP_IPP_ 33 template<
class UserNodeLabel,
class UserEdgeLabel>
34 IPFP<UserNodeLabel, UserEdgeLabel>::
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},
45 time_limit_in_sec_{0.0},
50 template<
class UserNodeLabel,
class UserEdgeLabel>
56 Timer timer(time_limit_in_sec_);
59 output_node_map = initial_node_map;
64 node_map_to_matrix_(initial_node_map, x);
65 double linear_cost_x{compute_induced_linear_cost_(qap_instance_, x)};
67 bool x_is_integral{
true};
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};
77 double step_width{0.0};
80 DMatrix linear_problem(qap_instance_.num_rows(), qap_instance_.num_cols());
84 if (quadratic_model_ == QAPE) {
90 double min_linear_problem{0.0};
93 DMatrix linear_cost_matrix(qap_instance_.num_rows(), qap_instance_.num_cols());
94 init_linear_cost_matrix_(qap_instance_, linear_cost_matrix);
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++) {
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;
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()) +
".");
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;
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);
130 if (beta <= 0.00001 or step_width >= 1) {
131 x_is_integral =
true;
133 overall_cost_x = overall_cost_b;
134 linear_cost_x = linear_cost_b;
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);
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();
160 this->
ged_data_.compute_induced_cost(g, h, projected_node_map);
162 output_node_map = projected_node_map;
167 template<
class UserNodeLabel,
class UserEdgeLabel>
171 omega_ = this->
ged_data_.max_edit_cost(g, h) + 10.0;
172 qap_instance_.init(
this, g, h);
175 template<
class UserNodeLabel,
class UserEdgeLabel>
179 quadratic_model_ = QAPE;
183 time_limit_in_sec_ = 0.0;
186 template<
class UserNodeLabel,
class UserEdgeLabel>
190 return "[--lsape-model <arg>] [--quadratic-model <arg>] [--iterations <arg>] [--time-limit <arg>] [--epsilon <arg>]";
193 template<
class UserNodeLabel,
class UserEdgeLabel>
197 if (option ==
"lsape-model") {
201 else if (arg ==
"FLWC") {
204 else if (arg ==
"FLCC") {
207 else if (arg ==
"FBP") {
210 else if (arg ==
"SFBP") {
213 else if (arg ==
"FBP0") {
216 else if (arg ==
"ECBP") {
220 throw ged::Error(std::string(
"Invalid argument ") + arg +
" for option lsape-model. Usage: options = \"[--lsape-model ECBP|EBP|FLWC|FLCC|FBP|SFBP|FBP0] [...]\"");
224 else if (option ==
"time-limit") {
226 time_limit_in_sec_ = std::stod(arg);
229 throw Error(std::string(
"Invalid argument \"") + arg +
"\" for option time-limit. Usage: options = \"[--time-limit <convertible to double>] [...]");
233 else if (option ==
"quadratic-model") {
235 quadratic_model_ = QAPE;
237 else if (arg ==
"B-QAP") {
238 quadratic_model_ = B_QAP;
240 else if (arg ==
"C-QAP") {
241 quadratic_model_ = C_QAP;
244 throw ged::Error(std::string(
"Invalid argument ") + arg +
" for option quadratic-model. Usage: options = \"[--quadratic-model QAPE|B-QAP|C-QAP] [...]\"");
248 else if (option ==
"iterations") {
250 max_itrs_ = std::stoi(arg);
253 throw Error(std::string(
"Invalid argument \"") + arg +
"\" for option iterations. Usage: options = \"[--iterations <convertible to int>] [...]");
257 else if (option ==
"epsilon") {
259 epsilon_ = std::stod(arg);
262 throw Error(std::string(
"Invalid argument \"") + arg +
"\" for option epsilon. Usage: options = \"[--epsilon <convertible to double greater 0>] [...]");
265 throw Error(std::string(
"Invalid argument \"") + arg +
"\" for option epsilon. Usage: options = \"[--epsilon <convertible to double greater 0>] [...]");
274 template<
class UserNodeLabel,
class UserEdgeLabel>
278 matrix.
resize(qap_instance_.num_rows(), qap_instance_.num_cols());
280 std::vector<NodeMap::Assignment> relation;
282 for (
auto assignment : relation) {
286 row = assignment.first;
289 col = assignment.second;
292 matrix(row, col) = 1.0;
295 if (quadratic_model_ == QAPE) {
296 matrix(row, matrix.
num_cols() - 1) = 1.0;
298 else if (quadratic_model_ == B_QAP){
299 matrix(row, row + qap_instance_.num_nodes_h()) = 1.0;
303 if (quadratic_model_ == QAPE) {
304 matrix(matrix.
num_rows() - 1, col) = 1.0;
306 else if (quadratic_model_ == B_QAP){
307 matrix(col + qap_instance_.num_nodes_g(), col) = 1.0;
313 template<
class UserNodeLabel,
class UserEdgeLabel>
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);
325 template<
class UserNodeLabel,
class UserEdgeLabel>
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);
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);
345 linear_problem += linear_cost_matrix;
348 template<
class UserNodeLabel,
class UserEdgeLabel>
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) {
355 if (lower_bound >= upper_bound) {
361 if (max_itrs_ > 0 and current_itr > max_itrs_) {
364 if (min_linear_problem < 0.00001) {
365 return std::fabs(alpha) < epsilon_;
367 return std::fabs(alpha / min_linear_problem) < epsilon_;
370 template<
class UserNodeLabel,
class UserEdgeLabel>
374 double & min_linear_problem,
double & linear_cost_b,
double & overall_cost_b,
DMatrix & b)
const {
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);
382 template<
class UserNodeLabel,
class UserEdgeLabel>
386 double & min_linear_problem,
double & linear_cost_b,
double & overall_cost_b,
DMatrix & b)
const {
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);
394 template<
class UserNodeLabel,
class UserEdgeLabel>
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);
407 template<
class UserNodeLabel,
class UserEdgeLabel>
412 for (std::size_t row{0}; row < solver.
num_rows(); row++) {
419 template<
class UserNodeLabel,
class UserEdgeLabel>
424 for (std::size_t row{0}; row < solver.
num_rows(); row++) {
427 for (std::size_t col{0}; col < solver.
num_cols(); col++) {
434 template<
class UserNodeLabel,
class UserEdgeLabel>
438 double linear_cost{0.0};
439 for (std::size_t row{0}; row < solver.
num_rows(); row++) {
447 template<
class UserNodeLabel,
class UserEdgeLabel>
451 double linear_cost{0.0};
452 for (std::size_t row{0}; row < solver.
num_rows(); row++) {
455 for (std::size_t col{0}; col < solver.
num_cols(); col++) {
463 template<
class UserNodeLabel,
class UserEdgeLabel>
467 double quadratic_cost{0.0};
468 std::vector<NodeMap::Assignment> assignments;
469 for (std::size_t row{0}; row < solver.
num_rows(); row++) {
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);
479 return quadratic_cost;
482 template<
class UserNodeLabel,
class UserEdgeLabel>
486 double quadratic_cost{0.0};
487 std::vector<NodeMap::Assignment> assignments;
488 for (std::size_t row{0}; row < solver.
num_rows(); row++) {
491 for (std::size_t col{0}; col < solver.
num_cols(); col++) {
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);
501 return quadratic_cost;
505 template<
class UserNodeLabel,
class UserEdgeLabel>
514 translation_factor_{0.0} {}
516 template<
class UserNodeLabel,
class UserEdgeLabel>
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_));
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_));
536 template<
class UserNodeLabel,
class UserEdgeLabel>
541 switch(ipfp_->quadratic_model_) {
543 return static_cast<std::size_t
>(num_nodes_g_) + 1;
545 return static_cast<std::size_t
>(num_nodes_g_) + static_cast<std::size_t>(num_nodes_h_);
547 return static_cast<std::size_t
>(num_nodes_g_);
551 template<
class UserNodeLabel,
class UserEdgeLabel>
556 switch(ipfp_->quadratic_model_) {
558 return static_cast<std::size_t
>(num_nodes_h_) + 1;
560 return static_cast<std::size_t
>(num_nodes_h_) + static_cast<std::size_t>(num_nodes_g_);
562 return static_cast<std::size_t
>(num_nodes_h_);
566 template<
class UserNodeLabel,
class UserEdgeLabel>
574 template<
class UserNodeLabel,
class UserEdgeLabel>
582 template<
class UserNodeLabel,
class UserEdgeLabel>
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_.");
590 double return_val{0.0};
591 switch(ipfp_->quadratic_model_) {
593 return_val += ipfp_->ged_data_.node_cost(g_->get_node_label(row), h_->get_node_label(col));
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());
600 else if (num_nodes_g_ < num_nodes_h_) {
601 return_val -= ipfp_->ged_data_.node_cost(
dummy_label(), h_->get_node_label(col));
603 if (num_nodes_g_ != num_nodes_h_) {
604 return_val += translation_factor_;
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));
611 else if (row < num_nodes_g_) {
612 return_val += ipfp_->ged_data_.node_cost(g_->get_node_label(row),
dummy_label());
614 else if (col < num_nodes_h_) {
615 return_val += ipfp_->ged_data_.node_cost(
dummy_label(), h_->get_node_label(col));
619 if (row < num_nodes_g_ and col >= num_nodes_h_ and col != row + num_nodes_h_) {
620 return_val += ipfp_->omega_;
622 else if (row >= num_nodes_g_ and col < num_nodes_h_ and row != col + num_nodes_g_) {
623 return_val += ipfp_->omega_;
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));
628 else if (row < num_nodes_g_) {
629 return_val += ipfp_->ged_data_.node_cost(g_->get_node_label(row),
dummy_label());
631 else if (col < num_nodes_h_) {
632 return_val += ipfp_->ged_data_.node_cost(
dummy_label(), h_->get_node_label(col));
639 template<
class UserNodeLabel,
class UserEdgeLabel>
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) {
647 double return_val{0.0};
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_;
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_;
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_;
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_;
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_) {
664 return_val += ipfp_->ged_data_.edge_cost(g_->get_edge_label(edge_g), h_->get_edge_label(edge_h));
667 return_val += ipfp_->ged_data_.edge_cost(g_->get_edge_label(edge_g),
dummy_label());
670 return_val += ipfp_->ged_data_.edge_cost(
dummy_label(), h_->get_edge_label(edge_h));
673 else if (row_1 < num_nodes_g_ and row_2 < num_nodes_g_) {
675 return_val += ipfp_->ged_data_.edge_cost(g_->get_edge_label(edge_g),
dummy_label());
678 else if (col_1 < num_nodes_h_ and col_2 < num_nodes_h_) {
680 return_val += ipfp_->ged_data_.edge_cost(
dummy_label(), h_->get_edge_label(edge_h));
686 template<
class UserNodeLabel,
class UserEdgeLabel>
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) {
694 double return_val{0.0};
698 return_val += ipfp_->
ged_data_.edge_cost(g_->get_edge_label(edge_g), h_->get_edge_label(edge_h));
701 return_val += ipfp_->ged_data_.edge_cost(g_->get_edge_label(edge_g),
dummy_label());
704 return_val += ipfp_->ged_data_.edge_cost(
dummy_label(), h_->get_edge_label(edge_h));
707 return_val -= 3 * ipfp_->ged_data_.edge_cost(g_->get_edge_label(edge_g),
dummy_label());
710 return_val -= 3 * ipfp_->ged_data_.edge_cost(
dummy_label(), h_->get_edge_label(edge_h));
712 if (num_nodes_g_ != num_nodes_h_) {
713 return_val += translation_factor_;
718 template<
class UserNodeLabel,
class UserEdgeLabel>
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) {
726 double return_val{0.0};
730 return_val += ipfp_->
ged_data_.edge_cost(g_->get_edge_label(edge_g), h_->get_edge_label(edge_h));
733 return_val += ipfp_->ged_data_.edge_cost(g_->get_edge_label(edge_g),
dummy_label());
736 return_val += ipfp_->ged_data_.edge_cost(
dummy_label(), h_->get_edge_label(edge_h));
741 template<
class UserNodeLabel,
class UserEdgeLabel>
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) {
749 if (row_1 == row_2 and row_1 < num_nodes_g_) {
752 if (col_1 == col_2 and col_1 < num_nodes_h_) {
755 double return_val{0.0};
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_) {
760 return_val += ipfp_->
ged_data_.edge_cost(g_->get_edge_label(edge_g), h_->get_edge_label(edge_h));
763 return_val += ipfp_->ged_data_.edge_cost(g_->get_edge_label(edge_g),
dummy_label());
766 return_val += ipfp_->ged_data_.edge_cost(
dummy_label(), h_->get_edge_label(edge_h));
769 else if (row_1 < num_nodes_g_ and row_2 < num_nodes_g_) {
771 return_val += ipfp_->ged_data_.edge_cost(g_->get_edge_label(edge_g),
dummy_label());
774 else if (col_1 < num_nodes_h_ and col_2 < num_nodes_h_) {
776 return_val += ipfp_->ged_data_.edge_cost(
dummy_label(), h_->get_edge_label(edge_h));
782 template<
class UserNodeLabel,
class UserEdgeLabel>
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_.");
790 double return_val{0.0};
791 switch(ipfp_->quadratic_model_) {
793 return_val += quadratic_cost_qap_(row_1, col_1, row_2, col_2);
796 return_val += quadratic_cost_c_qap_(row_1, col_1, row_2, col_2);
799 return_val += quadratic_cost_qape_(row_1, col_1, row_2, col_2);
802 return_val += quadratic_cost_b_qap_(row_1, col_1, row_2, col_2);
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.
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.
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.
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.
const GEDData< UserNodeLabel, UserEdgeLabel > & ged_data_
The data on which the method is run.
bool expired() const
Checks if the time limit has expired.
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.
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...
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.
static EdgeID dummy_edge()
Returns a dummy edge.
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:...
double induced_cost() const
Returns the induced cost.
std::size_t num_cols() const
Returns the number of columns.
void solve(int num_solutions=1)
Solves the LSAP problem instance.
static NodeID dummy_node()
Returns a dummy node.
Adaption of Hungarian Algorithm to LSAPE.
Eigen::Matrix< ScalarT, Eigen::Dynamic, Eigen::Dynamic > & matrix()
Returns reference to the internal Eigen matrix.
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.
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.
void set_induced_cost(double induced_cost)
Sets the induced cost of the node map.
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/.
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.
void set_to_val(const ScalarT &val)
Sets all cells to val.
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...
virtual void ls_runtime_init_(const GEDGraph &g, const GEDGraph &h) final
Initializes the method for a run between two graphs.
std::size_t num_cols() const
Returns the number of columns of the LSAP problem instance.
Computes an upper bound for general edit costs.
Reduction to compact LSAP instance for quasimetric costs.
std::size_t num_rows() const
Returns the number of rows.