diff --git a/alib2graph_algo/src/shortest_path/GreedyBestFS.cpp b/alib2graph_algo/src/shortest_path/GreedyBestFS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d30a54c1ab4d06dca01b3ee90cc12040fa4d5bcf --- /dev/null +++ b/alib2graph_algo/src/shortest_path/GreedyBestFS.cpp @@ -0,0 +1,94 @@ +// GreedyBestFS.cpp +// +// Created on: 21. 02. 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 "GreedyBestFS.hpp" + +#include <registration/AlgoRegistration.hpp> + +namespace { + +// --------------------------------------------------------------------------------------------------------------------- + +auto GreedyBestFS1 = registration::AbstractRegister<shortest_path::GreedyBestFS, + ext::pair<ext::vector<DefaultNodeType>, DefaultWeightType>, + const graph::WeightedUndirectedGraph<> &, + const DefaultNodeType &, + const DefaultNodeType &, + std::function<DefaultWeightType(const DefaultNodeType &, + const DefaultNodeType &)> >( + shortest_path::GreedyBestFS::findPathRegistration); + +auto GreedyBestFS2 = registration::AbstractRegister<shortest_path::GreedyBestFS, + ext::pair<ext::vector<DefaultNodeType>, DefaultWeightType>, + const graph::WeightedUndirectedMultiGraph<> &, + const DefaultNodeType &, + const DefaultNodeType &, + std::function<DefaultWeightType(const DefaultNodeType &, + const DefaultNodeType &)> >( + shortest_path::GreedyBestFS::findPathRegistration); + +auto GreedyBestFS3 = registration::AbstractRegister<shortest_path::GreedyBestFS, + ext::pair<ext::vector<DefaultNodeType>, DefaultWeightType>, + const graph::WeightedDirectedGraph<> &, + const DefaultNodeType &, + const DefaultNodeType &, + std::function<DefaultWeightType(const DefaultNodeType &, + const DefaultNodeType &)> >( + shortest_path::GreedyBestFS::findPathRegistration); + +auto GreedyBestFS4 = registration::AbstractRegister<shortest_path::GreedyBestFS, + ext::pair<ext::vector<DefaultNodeType>, DefaultWeightType>, + const graph::WeightedDirectedMultiGraph<> &, + const DefaultNodeType &, + const DefaultNodeType &, + std::function<DefaultWeightType(const DefaultNodeType &, + const DefaultNodeType &)> >( + shortest_path::GreedyBestFS::findPathRegistration); + +auto GreedyBestFS5 = registration::AbstractRegister<shortest_path::GreedyBestFS, + ext::pair<ext::vector<DefaultNodeType>, DefaultWeightType>, + const graph::WeightedMixedGraph<> &, + const DefaultNodeType &, + const DefaultNodeType &, + std::function<DefaultWeightType(const DefaultNodeType &, + const DefaultNodeType &)> >( + shortest_path::GreedyBestFS::findPathRegistration); + +auto GreedyBestFS6 = registration::AbstractRegister<shortest_path::GreedyBestFS, + ext::pair<ext::vector<DefaultNodeType>, DefaultWeightType>, + const graph::WeightedMixedMultiGraph<> &, + const DefaultNodeType &, + const DefaultNodeType &, + std::function<DefaultWeightType(const DefaultNodeType &, + const DefaultNodeType &)> >( + shortest_path::GreedyBestFS::findPathRegistration); + +auto GreedyBestFSGrid1 = registration::AbstractRegister<shortest_path::GreedyBestFS, + ext::pair<ext::vector<DefaultSquareGridNodeType>, + DefaultWeightType>, + const grid::WeightedSquareGrid4<> &, + const DefaultSquareGridNodeType &, + const DefaultSquareGridNodeType &, + std::function<DefaultWeightType(const DefaultSquareGridNodeType &, + const DefaultSquareGridNodeType &)> >( + shortest_path::GreedyBestFS::findPathRegistration); + +auto GreedyBestFSGrid2 = registration::AbstractRegister<shortest_path::GreedyBestFS, + ext::pair<ext::vector<DefaultSquareGridNodeType>, + DefaultWeightType>, + const grid::WeightedSquareGrid8<> &, + const DefaultSquareGridNodeType &, + const DefaultSquareGridNodeType &, + std::function<DefaultWeightType(const DefaultSquareGridNodeType &, + const DefaultSquareGridNodeType &)> >( + shortest_path::GreedyBestFS::findPathRegistration); + +// --------------------------------------------------------------------------------------------------------------------- + +} diff --git a/alib2graph_algo/src/shortest_path/GreedyBestFS.hpp b/alib2graph_algo/src/shortest_path/GreedyBestFS.hpp new file mode 100644 index 0000000000000000000000000000000000000000..9f53bbbec02b2c3f0230676102e5f9839c9bcee1 --- /dev/null +++ b/alib2graph_algo/src/shortest_path/GreedyBestFS.hpp @@ -0,0 +1,194 @@ +// GreedyBestFS.hpp +// +// Created on: 21. 02. 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_GREEDYBESTFS_HPP +#define ALIB2_GREEDYBESTFS_HPP + +#include <functional> +#include <alib/vector> +#include <alib/pair> +#include <alib/set> +#include <alib/map> + +#include <common/ReconstructPath.hpp> +#include <common/SupportFunction.hpp> + +namespace shortest_path { + +class GreedyBestFS { +// --------------------------------------------------------------------------------------------------------------------- + + public: + + /// Find the shortest path using Greedy Best-first search algorithm from the \p start node to the \p goal node in the \p graph. + /// \note The founded path is not necessary the optimal one. + /// + /// Whenever node is opened, \p f_user is called with two parameters (the opened node and value of currently shortest path). + /// + /// \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::set<ext::pair<TWeight, TNode>> queue; // priority queue + ext::map<TNode, TWeight> g; // G score + ext::map<TNode, TNode> p; // parents + }; + +// --------------------------------------------------------------------------------------------------------------------- + + template<typename FEdges, typename TNode, typename TWeight, typename F1, typename F2, typename F3> + static bool relaxation(FEdges successor_edges, + Data<TNode, TWeight> &data, + F1 f_heuristic, + F2 f_user, + F3 f_stop); + +// --------------------------------------------------------------------------------------------------------------------- + + template<typename TNode, typename TWeight, typename F> + inline static void init(GreedyBestFS::Data<TNode, TWeight> &data, const TNode &start, F f_heuristic); + +// --------------------------------------------------------------------------------------------------------------------- + +}; + +// ===================================================================================================================== + +template<typename TGraph, typename TNode, typename F1, typename F2> +ext::pair<ext::vector<TNode>, typename TGraph::edge_type::weight_type> +GreedyBestFS::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; + + // Init search + init(data, start, f_heuristic); + + while (!data.queue.empty()) { + bool stop = relaxation([&](const auto &node) -> auto { return graph.successorEdges(node); }, + data, + f_heuristic, + f_user, + [&goal](const TNode &n) -> bool { return goal == n; }); + + if (stop) { + break; + } + } + + return common::ReconstructPath::reconstructWeightedPath(data.p, data.g, start, goal); +} + +// --------------------------------------------------------------------------------------------------------------------- + +template<typename FEdges, typename TNode, typename TWeight, typename F1, typename F2, typename F3> +bool +GreedyBestFS::relaxation(FEdges successor_edges, + GreedyBestFS::Data<TNode, TWeight> &data, + F1 f_heuristic, + F2 f_user, + F3 f_stop) { + TNode n = data.queue.begin()->second; + data.queue.erase(data.queue.begin()); + + // Run user's function + f_user(n, data.g[n]); + + // Stop if reach the goal + if (f_stop(n)) { + return true; + } + + for (const auto &s_edge: successor_edges(n)) { + const TNode &s = common::SupportFunction::other(s_edge, n); // successor + + // Check for negative edge + if (s_edge.weight() < 0) { + throw std::out_of_range("GreedyBestFS: Detect negative weight on edge in graph."); + } + + // Calculate new G score + TWeight gscore = data.g.at(n) + s_edge.weight(); + + // Search if the node s was already visited + auto search_g = data.g.find(s); + + // If not or the G score can be improve do relaxation + if (search_g == data.g.end() || data.g.at(s) > gscore) { + data.g[s] = gscore; + data.p.insert_or_assign(s, n); + if (search_g == data.g.end()) { + data.queue.insert(ext::make_pair(f_heuristic(s), s)); + } + } + } + + return false; +} + +// --------------------------------------------------------------------------------------------------------------------- + +template<typename TNode, typename TWeight, typename F> +void GreedyBestFS::init(GreedyBestFS::Data<TNode, TWeight> &data, const TNode &start, F f_heuristic) { + data.g[start] = 0; + data.p.insert_or_assign(start, start); + data.queue.insert(ext::make_pair(f_heuristic(start), start)); +} + +// --------------------------------------------------------------------------------------------------------------------- + +} // namespace shortest_path +#endif //ALIB2_GREEDYBESTFS_HPP