require 'rgl/dijkstra_visitor' require 'rgl/edge_properties_map' require 'rgl/path_builder' module RGL # Bellman-Ford shortest paths algorithm has the following event points: # # * examine_edge # * edge_relaxed # * edge_not_relaxed # * edge_minimized # * edge_not_minimized # class BellmanFordVisitor < DijkstraVisitor def_event_handlers :edge_minimized, :edge_not_minimized def initialize(graph) super(graph) # by default, through an exception if a negative-weight cycle is detected @edge_not_minimized_event_handler = lambda do |u, v| raise ArgumentError.new("there is a negative-weight cycle including edge (#{u}, #{v})") end end end # This class implements {Graph#bellman_ford_shortest_paths}. class BellmanFordAlgorithm # Initializes Bellman-Ford algorithm for a _graph_ with provided edges weights map. # def initialize(graph, edge_weights_map, visitor) @graph = graph @edge_weights_map = EdgePropertiesMap.new(edge_weights_map, @graph.directed?) @visitor = visitor end # Finds the shortest path form the _source_ to every other vertex of the graph. # # Returns the shortest paths map that contains the shortest path (if it # exists) from the source to any vertex of the graph. # # @return [Hash[Object,Array]] def shortest_paths(source) init(source) relax_edges PathBuilder.new(source, @visitor.parents_map).paths(@graph.vertices) end private def init(source) @visitor.set_source(source) end def relax_edges (@graph.size - 1).times do @graph.each_edge do |u, v| relax_edge(u, v) relax_edge(v, u) unless @graph.directed? end end @graph.each_edge do |u, v| if @visitor.distance_map[u] + @edge_weights_map.edge_property(u, v) < @visitor.distance_map[v] @visitor.handle_edge_not_minimized(u, v) else @visitor.handle_edge_minimized(u, v) end end end def relax_edge(u, v) @visitor.handle_examine_edge(u, v) new_v_distance = @visitor.distance_map[u] + @edge_weights_map.edge_property(u, v) if new_v_distance < @visitor.distance_map[v] @visitor.distance_map[v] = new_v_distance @visitor.parents_map[v] = u @visitor.handle_edge_relaxed(u, v) else @visitor.handle_edge_not_relaxed(u, v) end end end # class BellmanFordAlgorithm module Graph # Finds the shortest paths from the _source_ to each vertex of the graph. # # Returns a Hash that maps each vertex of the graph to an Array of vertices that represents the shortest path # from the _source_ to the vertex. If the path doesn't exist, the corresponding hash value is nil. For the _source_ # vertex returned hash contains a trivial one-vertex path - [source]. # # Unlike Dijkstra algorithm, Bellman-Ford shortest paths algorithm works with negative edge weights. # # Raises ArgumentError if an edge weight is undefined. # # Raises ArgumentError or the graph has negative-weight cycles. This behavior can be overridden my a custom handler # for visitor's _edge_not_minimized_ event. # @return [Hash[Object,Array]] def bellman_ford_shortest_paths(edge_weights_map, source, visitor = BellmanFordVisitor.new(self)) BellmanFordAlgorithm.new(self, edge_weights_map, visitor).shortest_paths(source) end end # module Graph end # module RGL