27 #ifndef SRC_METHODS_WALKS_IPP_ 28 #define SRC_METHODS_WALKS_IPP_ 33 template<
class UserNodeLabel,
class UserEdgeLabel>
34 Walks<UserNodeLabel, UserEdgeLabel>::
37 template<
class UserNodeLabel,
class UserEdgeLabel>
38 Walks<UserNodeLabel, UserEdgeLabel>::
39 Walks(
const GEDData<UserNodeLabel, UserEdgeLabel> & ged_data) :
40 LSAPEBasedMethod<UserNodeLabel, UserEdgeLabel>(ged_data),
51 template<
class UserNodeLabel,
class UserEdgeLabel>
58 infile_ = std::string(
"");
59 outfile_ = std::string(
"");
62 template<
class UserNodeLabel,
class UserEdgeLabel>
66 return "[--depth-range <arg>] [--load <arg>] [--save <arg>]";
69 template<
class UserNodeLabel,
class UserEdgeLabel>
73 if (option ==
"load") {
77 else if (option ==
"save") {
81 else if (option ==
"depth-range") {
82 std::stringstream depth_range(arg);
83 std::string min_depth, max_depth;
84 if (std::getline(depth_range, min_depth,
',') and std::getline(depth_range, max_depth,
',')) {
86 min_depth_ = std::stoi(min_depth);
89 throw Error(std::string(
"Invalid argument \"") + arg +
"\" for option depth-range. Usage: options = \"[--depth-range <smaller convertible to int greater 0>,<larger convertible to int greater 0>] [...]");
92 max_depth_ = std::stoi(max_depth);
95 throw Error(std::string(
"Invalid argument \"") + arg +
"\" for option depth-range. Usage: options = \"[--depth-range <smaller convertible to int greater 0>,<larger convertible to int greater 0>] [...]");
97 if ((min_depth_ > max_depth_) or (min_depth_ <= 0) or (max_depth_ <= 0)) {
98 throw Error(std::string(
"Invalid argument \"") + arg +
"\" for option depth-range. Usage: options = \"[--depth-range <smaller convertible to int greater 0>,<larger convertible to int greater 0>] [...]");
102 throw Error(std::string(
"Invalid argument \"") + arg +
"\" for option depth-range. Usage: options = \"[--depth-range <smaller convertible to int greater 0>,<larger convertible to int greater 0>] [...]");
109 template<
class UserNodeLabel,
class UserEdgeLabel>
113 adj_graphs_[graph.
id()] = AdjGraph_(graph);
116 template<
class UserNodeLabel,
class UserEdgeLabel>
120 if (load_config_file_()) {
121 std::map<std::string, std::string> options;
123 depth_ = std::stod(options.at(
"depth"));
126 depth_ = (min_depth_ + max_depth_) / 2;
130 template<
class UserNodeLabel,
class UserEdgeLabel>
136 if (load_config_file_() or (min_depth_ == max_depth_)) {
141 std::size_t num_runs{(max_depth_ - min_depth_ + 1) * (this->
ged_data_.num_graphs() * this->
ged_data_.num_graphs())};
143 std::cout <<
"\r" << progress_bar << std::flush;
144 double best_avg_ub{std::numeric_limits<double>::infinity()};
145 std::size_t best_depth{min_depth_};
148 for (depth_ = min_depth_; depth_ <= max_depth_; depth_++) {
151 if (this->
ged_data_.is_shuffled_graph_copy(g->id())) {
155 if (this->
ged_data_.is_shuffled_graph_copy(h->id())) {
158 NodeMap node_map(g->num_nodes(), h->num_nodes());
159 DMatrix lsape_instance(g->num_nodes() + 1, h->num_nodes() + 1, 0.0);
161 if (this->
ged_data_.shuffled_graph_copies_available() and (g->id() == h->id())) {
164 lsape_solver.
solve();
166 this->
ged_data_.compute_induced_cost(*g, this->
ged_data_.graph(id_shuffled_graph_copy), node_map);
170 lsape_solver.
solve();
172 this->
ged_data_.compute_induced_cost(*g, *h, node_map);
174 avg_ub += node_map.induced_cost();
176 std::cout <<
"\r" << progress_bar << std::flush;
179 if (avg_ub < best_avg_ub) {
180 best_avg_ub = avg_ub;
188 if (outfile_ !=
"") {
189 std::map<std::string, std::string> options;
190 options[
"depth"] = std::to_string(depth_);
195 template<
class UserNodeLabel,
class UserEdgeLabel>
201 std::set<LabelID> node_labels;
202 init_node_labels_(g, h, node_labels);
203 AdjGraph_ adj_graph_g(adj_graphs_.at(g.
id()));
204 AdjGraph_ adj_graph_h(adj_graphs_.at(h.
id()));
205 ProductGraph_ product_graph(g, h);
208 adj_graph_g.compute_num_walks_(node_labels, depth_);
209 adj_graph_h.compute_num_walks_(node_labels, depth_);
210 product_graph.compute_num_unmatched_walks_(node_labels, adj_graph_g, adj_graph_h, depth_);
213 double mean_node_subs_cost{this->
ged_data_.mean_node_subs_cost(g, h)};
214 double mean_edge_subs_cost{this->
ged_data_.mean_edge_subs_cost(g, h)};
215 double mean_node_ins_del_cost{this->
ged_data_.mean_node_ins_cost(h) *
static_cast<double>(h.
num_nodes())};
216 mean_node_ins_del_cost += this->
ged_data_.mean_node_del_cost(g) *
static_cast<double>(g.
num_nodes());
220 double mean_edge_ins_del_cost{this->
ged_data_.mean_edge_ins_cost(h) *
static_cast<double>(h.
num_edges())};
221 mean_edge_ins_del_cost += this->
ged_data_.mean_edge_del_cost(g) *
static_cast<double>(g.
num_edges());
227 double depth{
static_cast<double>(depth_)};
230 #pragma omp parallel for if(this->num_threads_ > 1) 232 for (std::size_t row = 0; row < master_problem.
num_rows(); row++) {
233 for (std::size_t col = 0; col < master_problem.
num_cols(); col++) {
236 master_problem(row,col) = product_graph.num_substituted_walks_ending_at_same_label(row, col) * ((delta_1 + depth - 1) * mean_node_subs_cost + depth * mean_edge_subs_cost);
237 master_problem(row,col) += product_graph.num_substituted_walks_ending_at_different_labels(row, col) * ((delta_1 + depth) * mean_node_subs_cost + depth * mean_edge_subs_cost);
238 master_problem(row,col) += product_graph.num_inserted_or_deleted_walks(row, col) * ((delta_1 + depth) * mean_node_ins_del_cost + depth * mean_edge_ins_del_cost);
241 master_problem(row, h.
num_nodes()) = product_graph.num_inserted_or_deleted_walks(row, master_problem.
num_cols() - 1) * ((1 + depth) * mean_node_ins_del_cost + depth * mean_edge_ins_del_cost);
244 master_problem(g.
num_nodes(), col) = product_graph.num_inserted_or_deleted_walks(master_problem.
num_rows() - 1, col) * ((1 + depth) * mean_node_ins_del_cost + depth * mean_edge_ins_del_cost);
251 template<
class UserNodeLabel,
class UserEdgeLabel>
255 adj_matrix_(adj_graph.adj_matrix_),
256 num_walks_from_nodes_to_labels_(adj_graph.num_walks_from_nodes_to_labels_),
257 nodes_(adj_graph.nodes_),
258 inverse_label_index_(adj_graph.inverse_label_index_){}
260 template<
class UserNodeLabel,
class UserEdgeLabel>
265 num_walks_from_nodes_to_labels_(),
267 inverse_label_index_() {}
269 template<
class UserNodeLabel,
class UserEdgeLabel>
274 num_walks_from_nodes_to_labels_(),
276 inverse_label_index_() {
278 for (
auto node = g.
nodes().first; node != g.
nodes().second; node++) {
281 if (inverse_label_index_.find(label) == inverse_label_index_.end()) {
282 inverse_label_index_[label] = std::vector<std::size_t>{
id++};
285 inverse_label_index_[label].push_back(
id++);
288 for (std::size_t row{0}; row < adj_matrix_.num_rows(); row++) {
289 for (std::size_t col{0}; col < adj_matrix_.num_cols(); col++) {
290 if (not g.
is_edge(node(row), node(col))) {
291 adj_matrix_(row, col) = 0;
294 adj_matrix_(row, col) = 1;
299 template<
class UserNodeLabel,
class UserEdgeLabel>
304 adj_matrix_ = adj_graph.adj_matrix_;
305 num_walks_from_nodes_to_labels_ = adj_graph.num_walks_from_nodes_to_labels_;
306 nodes_ = adj_graph.nodes_;
307 inverse_label_index_ = adj_graph.inverse_label_index_;
310 template<
class UserNodeLabel,
class UserEdgeLabel>
314 node(std::size_t node_id)
const {
315 return nodes_.at(node_id);
318 template<
class UserNodeLabel,
class UserEdgeLabel>
319 std::pair<std::vector<std::size_t>::const_iterator, std::vector<std::size_t>::const_iterator>
323 if (inverse_label_index_.find(label) != inverse_label_index_.end()) {
324 return std::make_pair(inverse_label_index_.at(label).cbegin(), inverse_label_index_.at(label).cend());
326 return std::make_pair(inverse_label_index_.begin()->second.cend(), inverse_label_index_.begin()->second.cend());
329 template<
class UserNodeLabel,
class UserEdgeLabel>
334 return nodes_.size();
337 template<
class UserNodeLabel,
class UserEdgeLabel>
338 const typename Walks<UserNodeLabel, UserEdgeLabel>::Histogram_ &
342 return num_walks_from_nodes_to_labels_.at(node_id);
345 template<
class UserNodeLabel,
class UserEdgeLabel>
350 adj_matrix_.power(depth);
351 for (std::size_t start_node{0}; start_node < size(); start_node++) {
352 Histogram_ num_walks_from_node_to_labels;
353 for (
auto label = node_labels.begin(); label != node_labels.end(); label++) {
354 num_walks_from_node_to_labels[*label] = 0.0;
355 for (
auto end_node = nodes_with_label(*label).first; end_node != nodes_with_label(*label).second; end_node++) {
356 num_walks_from_node_to_labels[*label] += adj_matrix_(start_node, *end_node);
359 num_walks_from_nodes_to_labels_.push_back(num_walks_from_node_to_labels);
363 template<
class UserNodeLabel,
class UserEdgeLabel>
367 operator() (std::size_t row, std::size_t col)
const {
368 return static_cast<double>(adj_matrix_(row, col));
372 template<
class UserNodeLabel,
class UserEdgeLabel>
381 inverse_label_index_(),
382 product_node_to_id_() {
384 for (
auto node_g = g.
nodes().first; node_g != g.
nodes().second; node_g++) {
386 for (
auto node_h = h.
nodes().first; node_h != h.
nodes().second; node_h++) {
388 if (label_g == label_h) {
389 nodes_.push_back(std::make_pair(*node_g, *node_h));
390 if (inverse_label_index_.find(label_g) == inverse_label_index_.end()) {
391 inverse_label_index_[label_g] = std::vector<std::size_t>{
id++};
394 inverse_label_index_[label_g].push_back(
id++);
399 adj_matrix_.resize(nodes_.size(), nodes_.size());
400 for (std::size_t row{0}; row < adj_matrix_.num_rows(); row++) {
401 for (std::size_t col{0}; col < adj_matrix_.num_cols(); col++) {
402 if (not g.
is_edge(nodes_.at(row).first, nodes_.at(col).first)) {
403 adj_matrix_(row, col) = 0;
406 if (not h.
is_edge(nodes_.at(row).second, nodes_.at(col).second)) {
407 adj_matrix_(row, col) = 0;
411 adj_matrix_(row, col) = 0;
414 adj_matrix_(row, col) = 1;
419 template<
class UserNodeLabel,
class UserEdgeLabel>
420 std::pair<std::vector<std::size_t>::const_iterator, std::vector<std::size_t>::const_iterator>
424 if (inverse_label_index_.find(label) != inverse_label_index_.end()) {
425 return std::make_pair(inverse_label_index_.at(label).cbegin(), inverse_label_index_.at(label).cend());
427 return std::make_pair(inverse_label_index_.begin()->second.cend(), inverse_label_index_.begin()->second.cend());
430 template<
class UserNodeLabel,
class UserEdgeLabel>
435 ProductNode_ node{std::make_pair(node_g, node_h)};
436 if (product_node_to_id_.find(node) != product_node_to_id_.end()) {
437 return product_node_to_id_.at(node);
442 template<
class UserNodeLabel,
class UserEdgeLabel>
446 compute_num_unmatched_walks_(
const std::set<LabelID> & node_labels,
const AdjGraph_ & adj_graph_g,
const AdjGraph_ & adj_graph_h, std::size_t depth) {
447 adj_matrix_.power(depth);
448 for (std::size_t start_node_g{0}; start_node_g < adj_graph_g.size(); start_node_g++) {
449 for (std::size_t start_node_h{0}; start_node_h < adj_graph_h.size(); start_node_h++) {
452 Histogram_ num_unmatched_walks_from_node_to_labels_g(adj_graph_g.num_walks_from_node_to_labels(start_node_g));
453 Histogram_ num_unmatched_walks_from_node_to_labels_h(adj_graph_h.num_walks_from_node_to_labels(start_node_h));
454 std::size_t start_node_product{node_id(adj_graph_g.node(start_node_g), adj_graph_h.node(start_node_h))};
455 if (start_node_product != undefined_()) {
456 for (
auto label = node_labels.begin(); label != node_labels.end(); label++) {
457 double num_shared_paths_from_node_to_label{0.0};
458 for (
auto end_node_product = nodes_with_label(*label).first; end_node_product != nodes_with_label(*label).second; end_node_product++) {
459 num_shared_paths_from_node_to_label +=
static_cast<double>(adj_matrix_(start_node_product, *end_node_product));
461 num_shared_paths_from_node_to_label = std::sqrt(num_shared_paths_from_node_to_label);
462 num_shared_paths_from_node_to_label = std::min(num_shared_paths_from_node_to_label, num_unmatched_walks_from_node_to_labels_g.at(*label));
463 num_shared_paths_from_node_to_label = std::min(num_shared_paths_from_node_to_label, num_unmatched_walks_from_node_to_labels_h.at(*label));
464 num_unmatched_walks_from_node_to_labels_g[*label] -= num_shared_paths_from_node_to_label;
465 num_unmatched_walks_from_node_to_labels_h[*label] -= num_shared_paths_from_node_to_label;
470 double subs_same_sum{0.0};
471 double r_row_col{0.0};
472 double r_col_row{0.0};
473 for (
auto label = node_labels.begin(); label != node_labels.end(); label++) {
474 double subs_same{std::min(num_unmatched_walks_from_node_to_labels_g.at(*label), num_unmatched_walks_from_node_to_labels_h.at(*label))};
475 subs_same_sum += subs_same;
476 r_row_col += num_unmatched_walks_from_node_to_labels_g.at(*label) - subs_same;
477 r_col_row += num_unmatched_walks_from_node_to_labels_h.at(*label) - subs_same;
479 num_substituted_walks_ending_at_same_label_(start_node_g, start_node_h) = subs_same_sum;
480 num_substituted_walks_ending_at_different_labels_(start_node_g, start_node_h) = std::min(r_row_col, r_col_row);
481 num_inserted_or_deleted_walks_(start_node_g, start_node_h) = std::fabs(r_row_col - r_col_row);
486 for (std::size_t node_id_1{0}; node_id_1 < adj_graph_g.size(); node_id_1++) {
487 for (std::size_t node_id_2{0}; node_id_2 < adj_graph_g.size(); node_id_2++) {
488 num_inserted_or_deleted_walks_(node_id_1, adj_graph_h.size()) += adj_graph_g(node_id_1, node_id_2);
493 for (std::size_t node_id_1{0}; node_id_1 < adj_graph_h.size(); node_id_1++) {
494 for (std::size_t node_id_2{0}; node_id_2 < adj_graph_h.size(); node_id_2++) {
495 num_inserted_or_deleted_walks_(adj_graph_g.size(), node_id_1) += adj_graph_h(node_id_1, node_id_2);
500 template<
class UserNodeLabel,
class UserEdgeLabel>
505 return num_substituted_walks_ending_at_same_label_(row, col);
508 template<
class UserNodeLabel,
class UserEdgeLabel>
513 return num_substituted_walks_ending_at_different_labels_(row, col);
516 template<
class UserNodeLabel,
class UserEdgeLabel>
521 return num_inserted_or_deleted_walks_(row, col);
525 template<
class UserNodeLabel,
class UserEdgeLabel>
530 for (
auto node_g = g.
nodes().first; node_g != g.
nodes().second; node_g++) {
533 for (
auto node_h = h.
nodes().first; node_h != h.
nodes().second; node_h++) {
538 template<
class UserNodeLabel,
class UserEdgeLabel>
542 return (infile_ !=
"");
void set_model(const Model &model)
Makes the solver use a specific model for optimal solving.
std::size_t num_nodes() const
Returns the number of nodes.
std::size_t num_threads_
The number of threads to be used.
This class solves LSAPE instances by calling the library lsape available at https://bougleux.users.greyc.fr/lsape/.
virtual void lsape_init_graph_(const GEDGraph &graph) final
Initializes global variables for one graph.
virtual void lsape_set_default_options_() final
Sets all options that are not among the ones shared by all derived classes of ged::LSAPEBasedMethod t...
std::vector< GEDGraph >::size_type GraphID
Type of internally used graph IDs.
Computes an upper bound for general edit costs.
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.
virtual void lsape_init_() final
Initializes the method after initializing the global variables for the graphs.
bool is_edge(NodeID tail, NodeID head) const
Checks if an edge exists.
void solve(int num_solutions=1)
Solves the LSAPE problem instance.
virtual void lsape_populate_instance_(const GEDGraph &g, const GEDGraph &h, DMatrix &master_problem) final
Populates the LSAPE instance.
std::size_t num_cols() const
Returns the number of columns.
std::size_t LabelID
Internally used type of node and edge labels.
LabelID get_node_label(NodeID node) const
Returns the label of a given node.
std::size_t num_edges() const
Returns the number of edges.
void parse_config_file(const std::string &filename, std::map< std::string, std::string > &options)
Parses a configuration file.
bool compute_lower_bound_
Flag that should be set to true if and only if the method computes a lower bound. ...
LSAPESolver::Model lsape_model_
Specifies model for optimal LSAPE solver.
LabelID get_edge_label(EdgeID edge) const
Returns the label of a given edge.
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.
virtual void lsape_pre_graph_init_(bool called_at_runtime) final
Initializes the method at runtime or during initialization before initializing the global variables f...
Global namespace for GEDLIB.
void increment()
Increments the number of solved tasks.
virtual std::string lsape_valid_options_string_() const final
Returns string of all valid options that are not among the ones shared by all derived classes of ged:...
void save_as_config_file(const std::string &filename, const std::map< std::string, std::string > &options)
Saves a string map as a configuration file as expected by parse_config_file().
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...
void set_problem(const DMatrix *cost_matrix)
Sets the LSAPE problem instance.
std::size_t NodeID
Internally used vertex ID type.
GraphID id() const
Returns the ID of the graph.
std::size_t num_rows() const
Returns the number of rows.
virtual bool lsape_parse_option_(const std::string &option, const std::string &arg) final
Parses one option that is not among the ones shared by all derived classes of ged::LSAPEBasedMethod.