27 #ifndef SRC_METHODS_RING_ML_IPP_ 28 #define SRC_METHODS_RING_ML_IPP_ 33 template<
class UserNodeLabel,
class UserEdgeLabel>
34 RingML<UserNodeLabel, UserEdgeLabel>::
37 template<
class UserNodeLabel,
class UserEdgeLabel>
38 RingML<UserNodeLabel, UserEdgeLabel>::
39 RingML(
const GEDData<UserNodeLabel, UserEdgeLabel> & ged_data) :
40 MLBasedMethod<UserNodeLabel, UserEdgeLabel>(ged_data),
42 led_method_{LSAPE_OPTIMAL},
43 sort_method_{COUNTING},
44 use_topological_features_{
true},
45 use_global_features_{
true},
50 template<
class UserNodeLabel,
class UserEdgeLabel>
57 template<
class UserNodeLabel,
class UserEdgeLabel>
61 led_method_ = LSAPE_OPTIMAL;
62 sort_method_ = COUNTING;
63 use_topological_features_ =
true;
64 use_global_features_ =
true;
67 template<
class UserNodeLabel,
class UserEdgeLabel>
71 return "[--led-method <arg>] [--sort-method <arg>] [--topological-features <arg>] [--global-features <arg>]";
74 template<
class UserNodeLabel,
class UserEdgeLabel>
78 if (option ==
"led-method") {
79 if (arg ==
"LSAPE_OPTIMAL") {
80 led_method_ = LSAPE_OPTIMAL;
82 else if (arg ==
"LSAPE_GREEDY") {
83 led_method_ = LSAPE_GREEDY;
85 else if (arg ==
"GAMMA") {
89 throw Error(std::string(
"Invalid argument \"") + arg +
"\" for option led-method. Usage: options = \"[--led-method LSAPE_OPTIMAL|LSAPE_GREEDY|GAMMA] [...]\"");
93 else if (option ==
"sort-method") {
94 if (arg ==
"COUNTING") {
95 sort_method_ = COUNTING;
97 else if (arg ==
"STD") {
101 throw Error(std::string(
"Invalid argument \"") + arg +
"\" for option sort-method. Usage: options = \"[--sort-method COUNTING|STD] [...]\"");
105 else if (option ==
"topological-features") {
107 use_topological_features_ =
true;
109 else if (arg ==
"FALSE") {
110 use_topological_features_ =
false;
113 throw Error(std::string(
"Invalid argument \"") + arg +
"\" for option topological-features. Usage: options = \"[--topological-features TRUE|FALSE] [...]\"");
117 else if (option ==
"global-features") {
119 use_global_features_ =
true;
121 else if (arg ==
"FALSE") {
122 use_global_features_ =
false;
125 throw Error(std::string(
"Invalid argument \"") + arg +
"\" for option global-features. Usage: options = \"[--global-features TRUE|FALSE] [...]\"");
133 template<
class UserNodeLabel,
class UserEdgeLabel>
137 if (use_global_features_) {
138 global_features_.clear();
139 global_features_.push_back(static_cast<double>(g.
num_nodes()));
140 global_features_.push_back(static_cast<double>(h.
num_nodes()));
141 global_features_.push_back(this->
ged_data_.mean_node_subs_cost(g, h));
142 global_features_.push_back(this->
ged_data_.mean_node_del_cost(g));
143 global_features_.push_back(this->
ged_data_.mean_node_ins_cost(h));
144 global_features_.push_back(static_cast<double>(g.
num_edges()));
145 global_features_.push_back(static_cast<double>(h.
num_edges()));
146 global_features_.push_back(this->
ged_data_.mean_edge_subs_cost(g, h));
147 global_features_.push_back(this->
ged_data_.mean_edge_del_cost(g));
148 global_features_.push_back(this->
ged_data_.mean_edge_ins_cost(h));
152 template<
class UserNodeLabel,
class UserEdgeLabel>
156 feature_vector.clear();
157 add_global_features_(feature_vector);
158 const Ring_ & ring_i = rings_.at(g.
id()).at(i);
159 const Ring_ & ring_k = rings_.at(h.
id()).at(k);
160 for (std::size_t level{0}; level < num_layers_; level++) {
161 add_layer_substitution_features_(ring_i, ring_k, level, feature_vector);
165 template<
class UserNodeLabel,
class UserEdgeLabel>
169 feature_vector.clear();
170 add_global_features_(feature_vector);
171 const Ring_ & ring_i = rings_.at(g.
id()).at(i);
172 for (std::size_t level{0}; level < num_layers_; level++) {
173 add_layer_deletion_features_(ring_i, level, feature_vector);
177 template<
class UserNodeLabel,
class UserEdgeLabel>
181 feature_vector.clear();
182 add_global_features_(feature_vector);
183 const Ring_ & ring_k = rings_.at(h.
id()).at(k);
184 for (std::size_t level{0}; level < num_layers_; level++) {
185 add_layer_deletion_features_(ring_k, level, feature_vector);
189 template<
class UserNodeLabel,
class UserEdgeLabel>
197 if (use_global_features_) {
198 num_local_features -= 10;
200 if (use_topological_features_) {
201 num_layers_ = num_local_features / 6;
204 num_layers_ = num_local_features / 3;
208 template<
class UserNodeLabel,
class UserEdgeLabel>
213 std::size_t num_features{num_layers_};
214 if (use_topological_features_) {
220 if (use_global_features_) {
227 template<
class UserNodeLabel,
class UserEdgeLabel>
230 Layer_(std::size_t level) :
234 outer_edge_labels() {}
237 template<
class UserNodeLabel,
class UserEdgeLabel>
244 template<
class UserNodeLabel,
class UserEdgeLabel>
248 rings_[graph.
id()] = NodeRingMap_();
249 for (
auto node = graph.
nodes().first; node != graph.
nodes().second; node++) {
250 build_ring_(graph, *node, rings_.at(graph.
id()));
254 template<
class UserNodeLabel,
class UserEdgeLabel>
258 std::map<GEDGraph::NodeID, int> distance_to_root;
259 for (
auto node = graph.
nodes().first; node != graph.
nodes().second; node++) {
260 distance_to_root[*node] = -1;
262 distance_to_root[root] = 0;
264 std::map<GEDGraph::EdgeID, bool> discovered_edge;
265 for (
auto edge = graph.
edges().first; edge != graph.
edges().second; edge++) {
266 discovered_edge[*edge] =
false;
269 Layer_ current_layer(0);
270 std::queue<GEDGraph::NodeID> queue;
272 while (not queue.empty()) {
275 if (static_cast<int>(current_layer.level) < distance_to_root.at(current_node)) {
276 if (led_method_ == GAMMA) {
277 if (sort_method_ == COUNTING) {
279 util::counting_sort(current_layer.inner_edge_labels.begin(), current_layer.inner_edge_labels.end());
280 util::counting_sort(current_layer.outer_edge_labels.begin(), current_layer.outer_edge_labels.end());
283 std::sort(current_layer.node_labels.begin(), current_layer.node_labels.end());
284 std::sort(current_layer.inner_edge_labels.begin(), current_layer.inner_edge_labels.end());
285 std::sort(current_layer.outer_edge_labels.begin(), current_layer.outer_edge_labels.end());
288 rings[root].layers.push_back(current_layer);
289 current_layer = Layer_(current_layer.level + 1);
291 current_layer.node_labels.push_back(graph.
get_node_label(current_node));
294 if (distance_to_root.at(next_node) == -1) {
295 distance_to_root[next_node] = current_layer.level + 1;
296 if (current_layer.level < num_layers_) {
297 queue.push(next_node);
300 if (not discovered_edge.at(*edge)) {
301 discovered_edge[*edge] =
true;
302 if (distance_to_root.at(current_node) == distance_to_root.at(next_node)) {
303 current_layer.inner_edge_labels.push_back(graph.
get_edge_label(*edge));
305 else if (distance_to_root.at(current_node) < distance_to_root.at(next_node)) {
306 current_layer.outer_edge_labels.push_back(graph.
get_edge_label(*edge));
309 throw Error(std::string(
"Error when building ring rooted at ") + std::to_string(root) +
310 " for graph " + std::to_string(graph.
id()) +
": dist(" +
311 std::to_string(current_node) +
") = " + std::to_string(distance_to_root.at(current_node)) +
312 " > dist(" + std::to_string(next_node) +
") = " + std::to_string(distance_to_root.at(next_node)));
317 if (led_method_ == GAMMA) {
318 if (sort_method_ == COUNTING) {
320 util::counting_sort(current_layer.inner_edge_labels.begin(), current_layer.inner_edge_labels.end());
321 util::counting_sort(current_layer.outer_edge_labels.begin(), current_layer.outer_edge_labels.end());
324 std::sort(current_layer.node_labels.begin(), current_layer.node_labels.end());
325 std::sort(current_layer.inner_edge_labels.begin(), current_layer.inner_edge_labels.end());
326 std::sort(current_layer.outer_edge_labels.begin(), current_layer.outer_edge_labels.end());
329 rings[root].layers.push_back(current_layer);
332 template<
class UserNodeLabel,
class UserEdgeLabel>
337 for (
auto graph_rings_pair = rings_.begin(); graph_rings_pair != rings_.end(); graph_rings_pair++) {
338 for (
auto ring = graph_rings_pair->second.begin(); ring != graph_rings_pair->second.end(); ring++) {
339 num_layers_ = std::max(num_layers_, ring->second.layers.size());
344 template<
class UserNodeLabel,
class UserEdgeLabel>
348 for (
auto feature = global_features_.begin(); feature != global_features_.end(); feature++) {
349 feature_vector.push_back(*feature);
353 template<
class UserNodeLabel,
class UserEdgeLabel>
357 if ((ring_i.layers.size() > level) and (ring_k.layers.size() > level)) {
358 add_layer_features_(ring_i.layers.at(level), ring_k.layers.at(level), feature_vector);
360 else if (ring_i.layers.size() > level) {
361 add_layer_features_(ring_i.layers.at(level), Layer_(0), feature_vector);
363 else if (ring_k.layers.size() > level) {
364 add_layer_features_(Layer_(0), ring_k.layers.at(level), feature_vector);
367 std::size_t num_layer_features = use_topological_features_ ? 6 : 3;
368 for (std::size_t layer_feature{0}; layer_feature < num_layer_features; layer_feature++) {
369 feature_vector.push_back(0.0);
374 template<
class UserNodeLabel,
class UserEdgeLabel>
378 if (ring.layers.size() > level) {
379 add_layer_features_(ring.layers.at(level), Layer_(0), feature_vector);
382 std::size_t num_layer_features = use_topological_features_ ? 6 : 3;
383 for (std::size_t layer_feature{0}; layer_feature < num_layer_features; layer_feature++) {
384 feature_vector.push_back(0.0);
389 template<
class UserNodeLabel,
class UserEdgeLabel>
393 if (ring.layers.size() > level) {
394 add_layer_features_(Layer_(0), ring.layers.at(level), feature_vector);
397 std::size_t num_layer_features = use_topological_features_ ? 6 : 3;
398 for (std::size_t layer_feature{0}; layer_feature < num_layer_features; layer_feature++) {
399 feature_vector.push_back(0.0);
404 template<
class UserNodeLabel,
class UserEdgeLabel>
407 add_layer_features_(
const Layer_ & lhs,
const Layer_ & rhs, std::vector<double> & feature_vector)
const {
409 if (use_topological_features_) {
410 feature_vector.push_back(static_cast<double>(lhs.node_labels.size() - rhs.node_labels.size()));
411 feature_vector.push_back(static_cast<double>(lhs.inner_edge_labels.size() - rhs.inner_edge_labels.size()));
412 feature_vector.push_back(static_cast<double>(lhs.outer_edge_labels.size() - rhs.outer_edge_labels.size()));
415 switch (led_method_) {
417 feature_vector.push_back(gamma_multiset_cost_(lhs.node_labels, rhs.node_labels,
true));
418 feature_vector.push_back(gamma_multiset_cost_(lhs.inner_edge_labels, rhs.inner_edge_labels,
false));
419 feature_vector.push_back(gamma_multiset_cost_(lhs.outer_edge_labels, rhs.outer_edge_labels,
false));
422 feature_vector.push_back(lsape_multiset_cost_(lhs.node_labels, rhs.node_labels,
true));
423 feature_vector.push_back(lsape_multiset_cost_(lhs.inner_edge_labels, rhs.inner_edge_labels,
false));
424 feature_vector.push_back(lsape_multiset_cost_(lhs.outer_edge_labels, rhs.outer_edge_labels,
false));
429 template<
class UserNodeLabel,
class UserEdgeLabel>
432 lsape_multiset_cost_(
const std::vector<LabelID> & lhs,
const std::vector<LabelID> & rhs,
bool node_labels)
const {
434 if ((lhs.size() == 0) and (rhs.size() == 0)) {
438 if ((lhs.size() > 0) and (rhs.size() == 0)) {
440 for (std::size_t row{0}; row < lhs.size(); row++) {
451 if ((lhs.size() == 0) and (rhs.size() > 0)) {
453 for (std::size_t col{0}; col < rhs.size(); col++) {
465 DMatrix problem(lhs.size() + 1, rhs.size() + 1, 0.0);
467 for (std::size_t row{0}; row < lhs.size(); row++) {
477 for (std::size_t col{0}; col < rhs.size(); col++) {
487 for (std::size_t row{0}; row < lhs.size(); row++) {
488 for (std::size_t col{0}; col < rhs.size(); col++) {
490 problem(row, col) = this->
ged_data_.node_cost(lhs.at(row), rhs.at(col));
493 problem(row, col) = this->
ged_data_.edge_cost(lhs.at(row), rhs.at(col));
499 if (led_method_ == LSAPE_OPTIMAL) {
505 problem_solver.
solve();
510 template<
class UserNodeLabel,
class UserEdgeLabel>
513 gamma_multiset_cost_(
const std::vector<LabelID> & lhs,
const std::vector<LabelID> & rhs,
bool node_labels)
const {
517 if (lhs.size() < rhs.size()) {
518 double avg_ins_cost{0.0};
519 for (
auto label_rhs = rhs.begin(); label_rhs != rhs.end(); label_rhs++) {
527 avg_ins_cost /=
static_cast<double>(rhs.size());
528 cost +=
static_cast<double>(rhs.size() - lhs.size()) * avg_ins_cost;
532 if (lhs.size() > rhs.size()) {
533 double avg_del_cost{0.0};
534 for (
auto label_lhs = lhs.begin(); label_lhs != lhs.end(); label_lhs++) {
542 avg_del_cost /=
static_cast<double>(lhs.size());
543 cost +=
static_cast<double>(lhs.size() - rhs.size()) * avg_del_cost;
547 double avg_rel_cost{0.0};
548 std::size_t count_diff_labels{0};
549 for (
auto label_lhs = lhs.begin(); label_lhs != lhs.end(); label_lhs++) {
550 for (
auto label_rhs = rhs.begin(); label_rhs != rhs.end(); label_rhs++) {
551 if (*label_lhs != *label_rhs) {
554 avg_rel_cost += this->
ged_data_.node_cost(*label_lhs, *label_rhs);
557 avg_rel_cost += this->
ged_data_.edge_cost(*label_lhs, *label_rhs);
562 avg_rel_cost /=
static_cast<double>(count_diff_labels);
565 std::size_t intersection_size{0};
566 auto label_lhs = lhs.begin();
567 auto label_rhs = rhs.begin();
568 while ((label_lhs != lhs.end()) and (label_rhs != rhs.end())) {
569 if (*label_lhs == *label_rhs) {
574 else if (*label_lhs < *label_rhs) {
582 std::size_t gamma(std::min(lhs.size(), rhs.size()) - intersection_size);
584 cost +=
static_cast<double>(gamma) * avg_rel_cost;
void set_model(const Model &model)
Makes the solver use a specific model for optimal solving.
virtual void ml_init_for_num_features_() final
Initializes the derived class for running with feature vectors of size ged::MLBasedMethod::num_featur...
std::size_t num_nodes() const
Returns the number of nodes.
virtual void ml_populate_substitution_feature_vector_(const GEDGraph &g, const GEDGraph &h, GEDGraph::NodeID i, GEDGraph::NodeID k, std::vector< double > &feature_vector) final
Computes substitution feature vector.
This class solves LSAPE instances by calling the library lsape available at https://bougleux.users.greyc.fr/lsape/.
virtual std::string ml_valid_options_string_() const final
Returns string of all valid options that are not among the ones shared by all derived classes of ged:...
virtual void ml_set_default_options_() final
Sets all options that are not among the ones shared by all derived classes of ged::MLBasedMethod to d...
const GEDData< UserNodeLabel, UserEdgeLabel > & ged_data_
The data on which the method is run.
NodeID head(EdgeID edge) const
Returns the head of an edge.
void solve(int num_solutions=1)
Solves the LSAPE problem instance.
virtual void ml_populate_deletion_feature_vector_(const GEDGraph &g, GEDGraph::NodeID i, std::vector< double > &feature_vector) final
Computes deletion feature vector.
virtual bool ml_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::MLBasedMethod.
Uses ring structures for defining feature vectors for node edit operations.
void counting_sort(std::vector< LabelID >::iterator first, std::vector< LabelID >::iterator last)
Implementation of counting sort.
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.
std::size_t num_features_
The size of the feature vectors.
LSAPESolver::Model lsape_model_
Specifies model for optimal LSAPE solver.
virtual void ml_init_graph_(const GEDGraph &graph) final
Initializes global variables for one graph.
LabelID get_edge_label(EdgeID edge) const
Returns the label of a given edge.
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.
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 ml_populate_insertion_feature_vector_(const GEDGraph &h, GEDGraph::NodeID k, std::vector< double > &feature_vector) final
Computes insertion feature vector.
constexpr LabelID dummy_label()
Returns a dummy label.
constexpr std::size_t undefined()
Returns undefined size.
Global namespace for GEDLIB.
virtual std::size_t ml_get_num_features_() final
Returns the number of features.
LSAPESolver::GreedyMethod greedy_method_
Specifies method for greedy LSAPE solver.
std::size_t NodeID
Internally used vertex ID type.
void set_greedy_method(const GreedyMethod &greedy_method)
Makes the solver use a greedy method.
GraphID id() const
Returns the ID of the graph.
std::pair< edge_iterator, edge_iterator > edges() const
Provides access to all edge in the graph.
virtual void ml_init_feature_variables_(const GEDGraph &g, const GEDGraph &h, std::size_t num_threads) final
Initializes variables that are used for populating the feature vectors of assignments between two inp...