From 96425d9c9f9ec948f5a64eca773f237101149563 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Uhl=C3=ADk?= <jan@uhlik.me>
Date: Thu, 29 Mar 2018 21:01:39 +0200
Subject: [PATCH] Add IDA* algorithm.

---
 alib2graph_algo/src/shortest_path/IDAStar.cpp |  92 ++++++++
 alib2graph_algo/src/shortest_path/IDAStar.hpp | 208 ++++++++++++++++++
 2 files changed, 300 insertions(+)
 create mode 100644 alib2graph_algo/src/shortest_path/IDAStar.cpp
 create mode 100644 alib2graph_algo/src/shortest_path/IDAStar.hpp

diff --git a/alib2graph_algo/src/shortest_path/IDAStar.cpp b/alib2graph_algo/src/shortest_path/IDAStar.cpp
new file mode 100644
index 0000000000..cf52bd4593
--- /dev/null
+++ b/alib2graph_algo/src/shortest_path/IDAStar.cpp
@@ -0,0 +1,92 @@
+// IDAStar.cpp
+//
+//     Created on: 08. 03. 2018
+//         Author: Jan Uhlik
+//    Modified by:
+//
+// Copyright (c) 2017 Czech Technical University in Prague | Faculty of Information Technology. All rights reserved.
+// Git repository: https://gitlab.fit.cvut.cz/algorithms-library-toolkit/automata-library
+
+#include "IDAStar.hpp"
+
+#include <registration/AlgoRegistration.hpp>
+
+namespace {
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+auto IDAStar1 = registration::AbstractRegister<shortest_path::IDAStar,
+                                               ext::pair<ext::vector<DefaultNodeType>, DefaultWeightType>,
+                                               const graph::WeightedUndirectedGraph<> &,
+                                               const DefaultNodeType &,
+                                               const DefaultNodeType &,
+                                               std::function<DefaultWeightType(const DefaultNodeType &,
+                                                                               const DefaultNodeType &)> >(
+    shortest_path::IDAStar::findPathRegistration);
+
+auto IDAStar2 = registration::AbstractRegister<shortest_path::IDAStar,
+                                               ext::pair<ext::vector<DefaultNodeType>, DefaultWeightType>,
+                                               const graph::WeightedUndirectedMultiGraph<> &,
+                                               const DefaultNodeType &,
+                                               const DefaultNodeType &,
+                                               std::function<DefaultWeightType(const DefaultNodeType &,
+                                                                               const DefaultNodeType &)> >(
+    shortest_path::IDAStar::findPathRegistration);
+
+auto IDAStar3 = registration::AbstractRegister<shortest_path::IDAStar,
+                                               ext::pair<ext::vector<DefaultNodeType>, DefaultWeightType>,
+                                               const graph::WeightedDirectedGraph<> &,
+                                               const DefaultNodeType &,
+                                               const DefaultNodeType &,
+                                               std::function<DefaultWeightType(const DefaultNodeType &,
+                                                                               const DefaultNodeType &)> >(
+    shortest_path::IDAStar::findPathRegistration);
+
+auto IDAStar4 = registration::AbstractRegister<shortest_path::IDAStar,
+                                               ext::pair<ext::vector<DefaultNodeType>, DefaultWeightType>,
+                                               const graph::WeightedDirectedMultiGraph<> &,
+                                               const DefaultNodeType &,
+                                               const DefaultNodeType &,
+                                               std::function<DefaultWeightType(const DefaultNodeType &,
+                                                                               const DefaultNodeType &)> >(
+    shortest_path::IDAStar::findPathRegistration);
+
+auto IDAStar5 = registration::AbstractRegister<shortest_path::IDAStar,
+                                               ext::pair<ext::vector<DefaultNodeType>, DefaultWeightType>,
+                                               const graph::WeightedMixedGraph<> &,
+                                               const DefaultNodeType &,
+                                               const DefaultNodeType &,
+                                               std::function<DefaultWeightType(const DefaultNodeType &,
+                                                                               const DefaultNodeType &)> >(
+    shortest_path::IDAStar::findPathRegistration);
+
+auto IDAStar6 = registration::AbstractRegister<shortest_path::IDAStar,
+                                               ext::pair<ext::vector<DefaultNodeType>, DefaultWeightType>,
+                                               const graph::WeightedMixedMultiGraph<> &,
+                                               const DefaultNodeType &,
+                                               const DefaultNodeType &,
+                                               std::function<DefaultWeightType(const DefaultNodeType &,
+                                                                               const DefaultNodeType &)> >(
+    shortest_path::IDAStar::findPathRegistration);
+
+auto IDAStarGrid1 = registration::AbstractRegister<shortest_path::IDAStar,
+                                                   ext::pair<ext::vector<DefaultSquareGridNodeType>, DefaultWeightType>,
+                                                   const grid::WeightedSquareGrid4<> &,
+                                                   const DefaultSquareGridNodeType &,
+                                                   const DefaultSquareGridNodeType &,
+                                                   std::function<DefaultWeightType(const DefaultSquareGridNodeType &,
+                                                                                   const DefaultSquareGridNodeType &)> >(
+    shortest_path::IDAStar::findPathRegistration);
+
+auto IDAStarGrid2 = registration::AbstractRegister<shortest_path::IDAStar,
+                                                   ext::pair<ext::vector<DefaultSquareGridNodeType>, DefaultWeightType>,
+                                                   const grid::WeightedSquareGrid8<> &,
+                                                   const DefaultSquareGridNodeType &,
+                                                   const DefaultSquareGridNodeType &,
+                                                   std::function<DefaultWeightType(const DefaultSquareGridNodeType &,
+                                                                                   const DefaultSquareGridNodeType &)> >(
+    shortest_path::IDAStar::findPathRegistration);
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+}
diff --git a/alib2graph_algo/src/shortest_path/IDAStar.hpp b/alib2graph_algo/src/shortest_path/IDAStar.hpp
new file mode 100644
index 0000000000..68d1359891
--- /dev/null
+++ b/alib2graph_algo/src/shortest_path/IDAStar.hpp
@@ -0,0 +1,208 @@
+// IDAStar.hpp
+//
+//     Created on: 08. 03. 2018
+//         Author: Jan Uhlik
+//    Modified by:
+//
+// Copyright (c) 2017 Czech Technical University in Prague | Faculty of Information Technology. All rights reserved.
+// Git repository: https://gitlab.fit.cvut.cz/algorithms-library-toolkit/automata-library
+
+#ifndef ALIB2_IDASTAR_HPP
+#define ALIB2_IDASTAR_HPP
+
+#include <functional>
+#include <alib/vector>
+#include <alib/set>
+#include <alib/pair>
+#include <limits>
+#include <algorithm>
+#include <stdexcept>
+
+#include <common/ReconstructPath.hpp>
+#include <common/SupportFunction.hpp>
+
+using namespace std;
+
+namespace shortest_path {
+
+class IDAStar {
+// ---------------------------------------------------------------------------------------------------------------------
+ public:
+
+  /// Find the shortest path using IDAStar algorithm from the \p start node to the \p goal node in the \p graph.
+  ///
+  /// The heuristic function must be admissible and monotone.
+  ///
+  /// The heuristic function must be admissible and monotone.
+  ///
+  /// \param graph to explore.
+  /// \param start initial node.
+  /// \param goal final node.
+  /// \param f_heuristic heuristic function which accept node and return edge_type::weight_type.
+  /// \param f_user function which is called for every opened node with value of currently shortest path.
+  ///
+  /// \returns pair where first := shortest path := distance of path, if there is no such path vector is empty and distance std::numeric_limits<edge_type:weight_type>::max().
+  ///
+  /// \note TEdge of \p graph must follow graph::edge::WeightedEdge interface.
+  /// \sa graph::edge_type::WeightedEdge.
+  ///
+  /// \throws std::out_of_range if \p graph contains an edge with a negative weight.
+  ///
+  template<
+      typename TGraph,
+      typename TNode,
+      typename F1 = std::function<typename TGraph::edge_type::weight_type(const TNode &)>,
+      typename F2 = std::function<void(const TNode &, const typename TGraph::edge_type::weight_type &)>>
+  static
+  ext::pair<ext::vector<TNode>, typename TGraph::edge_type::weight_type>
+  findPath(const TGraph &graph,
+           const TNode &start,
+           const TNode &goal,
+           F1 f_heuristic,
+           F2 f_user = [](const TNode &,
+                          const typename TGraph::edge_type::weight_type &) {});
+
+  template<
+      typename TGraph,
+      typename TNode,
+      typename F1 = std::function<typename TGraph::edge_type::weight_type(const TNode &, const TNode &)>
+  >
+  static
+  ext::pair<ext::vector<TNode>, typename TGraph::edge_type::weight_type>
+  findPathRegistration(const TGraph &graph,
+                       const TNode &start,
+                       const TNode &goal,
+                       F1 f_heuristic) {
+    return findPath(graph, start, goal, [&](const TNode &n) { return f_heuristic(goal, n); });
+
+  }
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+// =====================================================================================================================
+
+ private:
+
+  template<typename TNode, typename TWeight>
+  struct Data {
+    ext::vector<TNode> path; // current path
+    ext::set<TNode> path_set; // current path set for quick search
+    TWeight path_size; // size of found path
+  };
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+  template<typename TGraph, typename TNode, typename F1, typename F2>
+  static
+  ext::pair<bool, typename TGraph::edge_type::weight_type>
+  search(const TGraph &graph,
+         IDAStar::Data<TNode, typename TGraph::edge_type::weight_type> &data,
+         const TNode &goal,
+         typename TGraph::edge_type::weight_type gscore,
+         typename TGraph::edge_type::weight_type bound,
+         F1 f_heuristic,
+         F2 f_user);
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+};
+
+// =====================================================================================================================
+
+template<typename TGraph, typename TNode, typename F1, typename F2>
+ext::pair<ext::vector<TNode>, typename TGraph::edge_type::weight_type>
+IDAStar::findPath(const TGraph &graph,
+                  const TNode &start,
+                  const TNode &goal,
+                  F1 f_heuristic,
+                  F2 f_user) {
+  using weight_type = typename TGraph::edge_type::weight_type;
+
+  Data<TNode, weight_type> data;
+  data.path.push_back(start);
+  data.path_set.insert(start);
+
+  weight_type bound = f_heuristic(start);
+  while (1) {
+    bool found;
+    weight_type t;
+    std::tie(found, t) = search(graph, data, goal, 0, bound, f_heuristic, f_user);
+
+    if (found) {
+      return ext::make_pair(data.path, data.path_size); // Return found path
+    } else if (t == std::numeric_limits<weight_type>::max()) {
+      return ext::make_pair(ext::vector<TNode>(), std::numeric_limits<weight_type>::max()); // No path exists
+    }
+
+    bound = t;
+  }
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+template<typename TGraph, typename TNode, typename F1, typename F2>
+ext::pair<bool, typename TGraph::edge_type::weight_type>
+IDAStar::search(const TGraph &graph,
+                IDAStar::Data<TNode, typename TGraph::edge_type::weight_type> &data,
+                const TNode &goal,
+                typename TGraph::edge_type::weight_type gscore,
+                typename TGraph::edge_type::weight_type bound,
+                F1 f_heuristic,
+                F2 f_user) {
+  using weight_type = typename TGraph::edge_type::weight_type;
+
+  TNode n = data.path.back();
+  weight_type f = gscore + f_heuristic(n);
+
+  if (f > bound) {
+    return ext::make_pair(false, f);
+  }
+
+  // Run user function
+  f_user(n, gscore);
+
+  // Check for goal
+  if (n == goal) {
+    data.path_size = gscore;
+    return ext::make_pair(true, f);
+  }
+
+  weight_type min = std::numeric_limits<weight_type>::max();
+  for (const auto &s_edge: graph.successorEdges(n)) {
+    const TNode &s = common::SupportFunction::other(s_edge, n); // successor
+
+    // Check if node is not in path
+    if (data.path_set.find(s) != data.path_set.end()) {
+      continue;
+    }
+
+    // Check for negative edge
+    if (s_edge.weight() < 0) {
+      throw std::out_of_range("IDAStar: Detect negative weight on edge in graph.");
+    }
+
+    bool found;
+    weight_type t;
+
+    data.path.push_back(s); // Insert node to the path
+    data.path_set.insert(s);
+    std::tie(found, t) =
+        search(graph, data, goal, gscore + s_edge.weight(), bound, f_heuristic, f_user); // Run recursion
+
+    if (found) {
+      return ext::make_pair(true, t);
+    } else if (t < min) {
+      min = t; // Update min
+    }
+
+    data.path.pop_back(); // Pop node from the path
+    data.path_set.erase(data.path_set.find(s));
+  }
+
+  return ext::make_pair(false, min);
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+} // namespace shortest_path
+#endif //ALIB2_IDASTAR_HPP
-- 
GitLab