From 40ca4d47154667ff53907c057aaaf6c068aa44e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Uhl=C3=ADk?= <jan@uhlik.me> Date: Thu, 29 Mar 2018 20:57:11 +0200 Subject: [PATCH] Add BellmanFord algorithm. --- .../src/shortest_path/BellmanFord.cpp | 67 +++++ .../src/shortest_path/BellmanFord.hpp | 261 ++++++++++++++++++ 2 files changed, 328 insertions(+) create mode 100644 alib2graph_algo/src/shortest_path/BellmanFord.cpp create mode 100644 alib2graph_algo/src/shortest_path/BellmanFord.hpp diff --git a/alib2graph_algo/src/shortest_path/BellmanFord.cpp b/alib2graph_algo/src/shortest_path/BellmanFord.cpp new file mode 100644 index 0000000000..db06141651 --- /dev/null +++ b/alib2graph_algo/src/shortest_path/BellmanFord.cpp @@ -0,0 +1,67 @@ +// BellmanFord.cpp +// +// Created on: 05. 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 "BellmanFord.hpp" + +#include <registration/AlgoRegistration.hpp> + +namespace { + +auto BellmanFord1 = registration::AbstractRegister<shortest_path::BellmanFord, + ext::pair<ext::vector<DefaultNodeType>, DefaultWeightType>, + const graph::WeightedUndirectedGraph<> &, + const DefaultNodeType &, + const DefaultNodeType &>(shortest_path::BellmanFord::findPathRegistration); + +auto BellmanFord2 = registration::AbstractRegister<shortest_path::BellmanFord, + ext::pair<ext::vector<DefaultNodeType>, DefaultWeightType>, + const graph::WeightedUndirectedMultiGraph<> &, + const DefaultNodeType &, + const DefaultNodeType &>(shortest_path::BellmanFord::findPathRegistration); + +auto BellmanFord3 = registration::AbstractRegister<shortest_path::BellmanFord, + ext::pair<ext::vector<DefaultNodeType>, DefaultWeightType>, + const graph::WeightedDirectedGraph<> &, + const DefaultNodeType &, + const DefaultNodeType &>(shortest_path::BellmanFord::findPathRegistration); + +auto BellmanFord4 = registration::AbstractRegister<shortest_path::BellmanFord, + ext::pair<ext::vector<DefaultNodeType>, DefaultWeightType>, + const graph::WeightedDirectedMultiGraph<> &, + const DefaultNodeType &, + const DefaultNodeType &>(shortest_path::BellmanFord::findPathRegistration); + +auto BellmanFord5 = registration::AbstractRegister<shortest_path::BellmanFord, + ext::pair<ext::vector<DefaultNodeType>, DefaultWeightType>, + const graph::WeightedMixedGraph<> &, + const DefaultNodeType &, + const DefaultNodeType &>(shortest_path::BellmanFord::findPathRegistration); + +auto BellmanFord6 = registration::AbstractRegister<shortest_path::BellmanFord, + ext::pair<ext::vector<DefaultNodeType>, DefaultWeightType>, + const graph::WeightedMixedMultiGraph<> &, + const DefaultNodeType &, + const DefaultNodeType &>(shortest_path::BellmanFord::findPathRegistration); + +auto BellmanFordGrid1 = registration::AbstractRegister<shortest_path::BellmanFord, + ext::pair<ext::vector<DefaultSquareGridNodeType>, + DefaultWeightType>, + const grid::WeightedSquareGrid4<> &, + const DefaultSquareGridNodeType &, + const DefaultSquareGridNodeType &>(shortest_path::BellmanFord::findPathRegistration); + +auto BellmanFordGrid2 = registration::AbstractRegister<shortest_path::BellmanFord, + ext::pair<ext::vector<DefaultSquareGridNodeType>, + DefaultWeightType>, + const grid::WeightedSquareGrid8<> &, + const DefaultSquareGridNodeType &, + const DefaultSquareGridNodeType &>(shortest_path::BellmanFord::findPathRegistration); + +} + diff --git a/alib2graph_algo/src/shortest_path/BellmanFord.hpp b/alib2graph_algo/src/shortest_path/BellmanFord.hpp new file mode 100644 index 0000000000..d2d6c265a8 --- /dev/null +++ b/alib2graph_algo/src/shortest_path/BellmanFord.hpp @@ -0,0 +1,261 @@ +// BellmanFord.hpp +// +// Created on: 05. 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_BELLMANFORD_HPP +#define ALIB2_BELLMANFORD_HPP + +#include <alib/list> +#include <alib/set> +#include <alib/map> +#include <alib/vector> +#include <queue> +#include <stdexcept> +#include <functional> + +#include <common/ReconstructPath.hpp> +#include <common/SupportFunction.hpp> + +namespace shortest_path { + +class BellmanFord { +// --------------------------------------------------------------------------------------------------------------------- + public: + + /// Run BellmanFord algorithm from the \p start node in the \p graph. + /// + /// 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 f_user function which is called for every opened node with value of currently shortest path. + /// + /// \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 negative cycle. + /// + template< + typename TGraph, + typename TNode, + typename F = std::function<void(const TNode &, + const typename TGraph::edge_type::weight_type &)> > + static + void + run(const TGraph &graph, + const TNode &start, + F f_user = [](const TNode &, + const typename TGraph::edge_type::weight_type &) -> void {}); + +// --------------------------------------------------------------------------------------------------------------------- + + /// Find the shortest path using BellmanFord algorithm from the \p start node to the \p goal node in the \p graph. + /// + /// 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_user function which is called for every open 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 negative cycle. + /// + template<typename TGraph, typename TNode, typename F = 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, + F f_user = [](const TNode &, + const typename TGraph::edge_type::weight_type &) {}); + + template<typename TGraph, typename TNode> + static + ext::pair<ext::vector<TNode>, typename TGraph::edge_type::weight_type> + findPathRegistration(const TGraph &graph, + const TNode &start, + const TNode &goal) { + return findPath(graph, start, goal); + } + +// ===================================================================================================================== + private: + + template<typename TNode, typename TWeight> + struct Data { + ext::map<TNode, TWeight> g; // distance (aka G score) + ext::map<TNode, TNode> p; // parents + ext::set<TNode> state1; // optimization Yen + ext::set<TNode> state2; // optimization Yen + }; + +// --------------------------------------------------------------------------------------------------------------------- + + template<typename TGraph, typename TNode, typename F> + static + Data<TNode, typename TGraph::edge_type::weight_type> + impl(const TGraph &graph, + const TNode &start, + F f_user); + +// --------------------------------------------------------------------------------------------------------------------- + + template<typename TGraph, typename TNode, typename F> + static + void + relaxation(const TGraph &graph, + ext::set<TNode> &nodes, + BellmanFord::Data<TNode, typename TGraph::edge_type::weight_type> &data, + ext::set<TNode> &state1, + ext::set<TNode> &state2, + F f_user); + +// --------------------------------------------------------------------------------------------------------------------- + + template<typename TNode, typename TWeight> + inline static void init(BellmanFord::Data<TNode, TWeight> &data, const TNode &start); + +// --------------------------------------------------------------------------------------------------------------------- + +}; + +// ===================================================================================================================== + +template<typename TGraph, typename TNode, typename F> +void BellmanFord::run(const TGraph &graph, const TNode &start, F f_user) { + impl(graph, start, f_user); +} + +// --------------------------------------------------------------------------------------------------------------------- + +template<typename TGraph, typename TNode, typename F> +ext::pair<ext::vector<TNode>, typename TGraph::edge_type::weight_type> +BellmanFord::findPath(const TGraph &graph, + const TNode &start, + const TNode &goal, + F f_user) { + using weight_type = typename TGraph::edge_type::weight_type; + + Data<TNode, weight_type> data = impl(graph, start, f_user); + + if (data.g.find(goal) == data.g.end()) { + return ext::make_pair(ext::vector<TNode>(), std::numeric_limits<weight_type>::max()); + } + + return ext::make_pair(common::ReconstructPath::reconstructPath(data.p, start, goal), data.g[goal]); +} + +// --------------------------------------------------------------------------------------------------------------------- + +template<typename TGraph, typename TNode, typename F> +BellmanFord::Data<TNode, typename TGraph::edge_type::weight_type> +BellmanFord::impl(const TGraph &graph, const TNode &start, F f_user) { + + using weight_type = typename TGraph::edge_type::weight_type; + + Data<TNode, weight_type> data; + + // Init data + init(data, start); + + // Run user's function + f_user(start, 0); + + auto nodes = graph.getNodes(); + size_t nodes_cnt = nodes.size(); + + for (size_t i = 1; i < nodes_cnt; ++i) { + if (i % 2 == 1) { + data.state2.clear(); + relaxation(graph, nodes, data, data.state1, data.state2, f_user); + } else { + data.state1.clear(); + relaxation(graph, nodes, data, data.state2, data.state1, f_user); + } + + if (((i % 2 == 1) && data.state2.empty()) || + ((i % 2 == 0) && data.state1.empty())) + break; // Optimization early stop + } + + for (const auto &n: nodes) { + for (const auto &s_edge: graph.successorEdges(n)) { + const TNode &s = common::SupportFunction::other(s_edge, n); // successor + + auto search = data.g.find(s); + + if (search != data.g.end() && data.g.at(n) + s_edge.weight() < data.g.at(s)) { + throw std::out_of_range("BellmanFord: Detected negative weight cycle."); + } + } + } + + return data; +} + +// --------------------------------------------------------------------------------------------------------------------- + +template<typename TGraph, typename TNode, typename F> +void BellmanFord::relaxation(const TGraph &graph, + ext::set<TNode> &nodes, + BellmanFord::Data<TNode, typename TGraph::edge_type::weight_type> &data, + ext::set<TNode> &state1, + ext::set<TNode> &state2, + F f_user) { + using weight_type = typename TGraph::edge_type::weight_type; + + for (const auto &n: nodes) { + + if (state1.find(n) == state1.end()) { + continue; + } + + for (const auto &s_edge: graph.successorEdges(n)) { + const TNode &s = common::SupportFunction::other(s_edge, n); // successor + + auto search = data.g.find(s); + + // Relaxation + if (search == data.g.end() || data.g.at(n) + s_edge.weight() < data.g.at(s)) { + weight_type gscore = data.g.at(n) + s_edge.weight(); + + // Run user's function + f_user(s, gscore); + + data.g[s] = gscore; + data.p.insert_or_assign(s, n); + + state1.insert(s); // Yen optimization + state2.insert(s); // Yen optimization + } + } + } + +} + +// --------------------------------------------------------------------------------------------------------------------- + +template<typename TNode, typename TWeight> +void BellmanFord::init(BellmanFord::Data<TNode, TWeight> &data, const TNode &start) { + data.g[start] = 0; + data.p.insert(std::make_pair(start, start)); + data.state1.insert(start); +} + +// --------------------------------------------------------------------------------------------------------------------- + +// ===================================================================================================================== + +} // namespace shortest_path +#endif //ALIB2_BELLMANFORD_HPP -- GitLab