Git Product home page Git Product logo

rgl's Introduction

Ruby Graph Library (RGL)

Test Doc Version Gitpod ready-to-code Code Climate

RGL is a framework for graph data structures and algorithms.

The design of the library is much influenced by the Boost Graph Library (BGL) which is written in C++. Refer to https://www.boost.org/libs/graph/doc for further links and documentation on graph data structures and algorithms and the design rationales of BGL.

A comprehensive summary of graph terminology can be found in the graph section of the Dictionary of Algorithms and Data Structures at https://www.nist.gov/dads/HTML/graph.html or Wikipedia.

Design principles

This document concentrates on the special issues of the implementation in Ruby. The main design goals directly taken from the BGL design are:

  • An interface for how the structure of a graph can be accessed using a generic interface that hides the details of the graph data structure implementation. This interface is defined by the module {RGL::Graph}, which should be included in concrete classes.

  • A standardized generic interface for traversing graphs {RGL::GraphIterator}

RGL provides some general purpose graph classes that conform to this interface, but they are not meant to be the only graph classes. As in BGL I believe that the main contribution of the RGL is the formulation of this interface.

The BGL graph interface and graph components are generic in the sense of the C++ Standard Template Library (STL). In Ruby other techniques are available to express the generic character of the algorithms and data structures mainly using mixins and iterators. The BGL documentation mentions three means to achieve genericity:

  • Algorithm/Data-Structure Interoperability
  • Extension through Function Objects and Visitors
  • Element Type Parameterization
  • Vertex and Edge Property Multi-Parameterization

The first is easily achieved in RGL using mixins, which of course is not as efficient than C++ templates (but much more readable :-). The second one is even more easily implemented using standard iterators with blocks or using the stream module. The third one is no issue since Ruby is dynamically typed: Each object can be a graph vertex. There is no need for a vertex (or even edge type). In the current version of RGL properties of vertices are simply attached using hashes. At first there seems to be not much need for the graph property machinery.

Algorithms

RGL current contains a core set of algorithm patterns:

  • Breadth First Search {RGL::BFSIterator}
  • Depth First Search {RGL::DFSIterator}

The algorithm patterns by themselves do not compute any meaningful quantities over graphs, they are merely building blocks for constructing graph algorithms. The graph algorithms in RGL currently include:

  • Topological Sort {RGL::TopsortIterator}
  • Connected Components {RGL::Graph#each_connected_component}
  • Strongly Connected Components {RGL::Graph#strongly_connected_components}
  • Transitive Closure {RGL::Graph#transitive_closure}
  • Dijkstras Shortest Path Algorithm {RGL::DijkstraAlgorithm}
  • Bellman Ford Algorithm {RGL::BellmanFordAlgorithm}

Data Structures

RGL currently provides two graph classes that implement a generalized adjacency list and an edge list adaptor.

  • {RGL::AdjacencyGraph}
  • {RGL::ImplicitGraph}

The AdjacencyGraph class is the general purpose swiss army knife of graph classes. It is highly parameterized so that it can be optimized for different situations: the graph is directed or undirected, allow or disallow parallel edges, efficient access to just the out-edges, fast vertex insertion and removal at the cost of extra space overhead, etc.

Differences to BGL

The concepts of IncidenceGraph, AdjacencyGraph and VertexListGraph (see IncidenceGraph) are bundled in RGL's base graph module. Most methods of IncidenceGraph should be standard in the base module Graph. The complexity guarantees can not necessarily provided (see BGL's Graph Concepts).

Installation

% gem install rgl

or download the latest sources from the git repository.

If you are going to use the drawing functionalities install Graphviz.

Running tests

Checkout RGL git repository and go to the project directory. First, install RGL dependencies with bundler:

% bundle install

After that you can run the tests:

% rake test

Example irb session with RGL

% irb -Ilib

irb> require 'rgl/adjacency'
irb> dg=RGL::DirectedAdjacencyGraph[1,2 ,2,3 ,2,4, 4,5, 6,4, 1,6]
# Use DOT to visualize this graph:
irb> require 'rgl/dot'
irb> dg.write_to_graphic_file('jpg')
"graph.jpg"

The result:

Example

You can control the graph layout by passing layout parameters to write_to_graphic_file. See TestDot::test_to_dot_digraph_with_options for an example using a feature implemented by Lia Skalkos (see PR #41).

irb> dg.directed?
true
irb> dg.vertices
[5, 6, 1, 2, 3, 4]
irb> dg.has_vertex? 4
true

Every object could be a vertex (there is no class Vertex), even the class object Object:

irb> dg.has_vertex? Object
false
irb> dg.edges.sort.to_s
"(1-2)(1-6)(2-3)(2-4)(4-5)(6-4)"
irb> dg.to_undirected.edges.sort.to_s
"(1=2)(1=6)(2=3)(2=4)(5=4)(6=4)"

Add inverse edge (4-2) to directed graph:

irb> dg.add_edge 4,2

(4-2) == (2-4) in the undirected graph:

irb> dg.to_undirected.edges.sort.to_s
"(1=2)(1=6)(2=3)(2=4)(5=4)(6=4)"

(4-2) != (2-4) in directed graphs:

irb> dg.edges.sort.to_s
"(1-2)(1-6)(2-3)(2-4)(4-2)(4-5)(6-4)"
irb> dg.remove_edge 4,2
true

Check whether a path exists between vertices 1 and 5

irb> require 'rgl/path'
irb> dg.path?(1, 5)
true

Topological sort is implemented as an iterator:

require 'rgl/topsort'
irb> dg.topsort_iterator.to_a
[1, 2, 3, 6, 4, 5]

A more elaborated example showing implicit graphs:

require 'rgl/implicit'
def module_graph
  RGL::ImplicitGraph.new { |g|
    g.vertex_iterator { |b|
      ObjectSpace.each_object(Module, &b)
    }
    g.adjacent_iterator { |x, b|
      x.ancestors.each { |y|
        b.call(y) unless x == y || y == Kernel || y == Object
      }
    }
    g.directed = true
  }
end

This function creates a directed graph, with vertices being all loaded modules:

g = module_graph

We only want to see the ancestors of {RGL::AdjacencyGraph}:

require 'rgl/traversal'
tree = g.bfs_search_tree_from(RGL::AdjacencyGraph)

Now we want to visualize this component of g with DOT. We therefore create a subgraph of the original graph, using a filtered graph:

g = g.vertices_filtered_by {|v| tree.has_vertex? v}
g.write_to_graphic_file('jpg')

creates the following graph image with DOT:

Module graph

This graph shows all loaded RGL modules:

RGL Modules

Look for more in examples directory.

(Optional) Configuring Graphviz DOT output options

The default graph will use standard DOT output visuals.

If you wish to configure the styling of the diagram, module {RGL::DOT} adds the methods {RGL::Graph#set_edge_options} and {RGL::Graph#set_vertex_options} for this purpose. You can use any options from the {RGL::DOT::NODE_OPTS} and {RGL::DOT::EDGE_OPTS} constants in {RGL::DOT}. Use the exact option name as an argument in your method call.

You can also configure the overall appearance of the graph by passing a hash of options from {RGL::DOT::GRAPH_OPTS} to the output method. The example below shows styling of vertices, edges and setting some basic graph options.

The available options are described in the GraphViz DOT Spec

colored diagram

require 'rgl/adjacency'
require 'rgl/dot'

graph = RGL::DirectedAdjacencyGraph['a','b', 'c','d', 'a','c']

graph.set_vertex_options('a', label: 'This is A', shape: 'box3d', fontcolor: 'green', fontsize: 16)
graph.set_vertex_options('b', label: 'This is B', shape: 'tab', fontcolor: 'red', fontsize: 14)
graph.set_vertex_options('c', shape: 'tab', fontcolor: 'blue')

graph.set_edge_options('a', 'b', label: 'NotCapitalEdge', style: 'dotted', dir: 'back', color: 'magenta')
graph.set_edge_options('a', 'c', weight: 5, color: 'blue')

graph_options = {
    "rankdir"  => "LR",
    "labelloc" => "t",
    "label"    => "Graph\n (generated #{Time.now.utc})"
}

graph.write_to_graphic_file('png', 'graph', graph_options)

Credits

Many thanks to Robert Feldt which also worked on a graph library (https://rockit.sf.net/subprojects/graphr) who pointed me to BGL and many other graph resources.

Robert kindly allowed to integrate his work on graphr, which I did not yet succeed. Especially his work to output graphs for GraphViz is much more elaborated than the minimal support in dot.rb.

Jeremy Siek one of the authors of the nice book The Boost Graph Library kindly allowed to use the BGL documentation as a cheap reference for RGL. He and Robert also gave feedback and many ideas for RGL.

Dave Thomas for RDoc which generated what you read and matz for Ruby. Dave included in the latest version of RDoc (alpha9) the module {RGL::DOT} which is used instead of Roberts module to visualize graphs.

Jeremy Bopp, John Carter, Sascha Doerdelmann, Shawn Garbett, Andreas Schörk, Dan Čermák, Kirill Lashuk and Markus Napp for contributing additions, test cases and bugfixes. The complete list of contributers is here.

Links

  • See {file:CHANGELOG.md} for major/breaking updates.
  • To contribute, please read {file:.github/CONTRIBUTING.md} first.
  • Please open an issue if anything is missing or unclear in this documentation.

Copying

RGL is Copyright (c) 2002,2004,2005,2008,2013,2015,2019,2020,2022,2023 by Horst Duchene. It is free software, and may be redistributed under the {file:LICENSE} and terms specified in the LICENSE file.

rgl's People

Contributors

artkirienko avatar asivasub14 avatar carlosantoniodasilva avatar ch4s3 avatar dcermak avatar dependabot[bot] avatar github-actions[bot] avatar gorn avatar hahcho avatar hlascelles avatar javanthropus avatar jsjuni avatar kl-7 avatar krallin avatar louismrose avatar lskalkos avatar matiasbattocchia avatar matiaskorhonen avatar monora avatar movermeyer avatar r0ckarong avatar spgarbet avatar ujihisa avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rgl's Issues

syntax error in dot file for undirected graph

A bug reported by Sean Harger:

require 'rgl/adjacency'
require 'rgl/dot'
g = RGL::AdjacencyGraph[1,2]
g.write_to_graphic_file
Error: graph.dot:1: syntax error near line 1
context:  >>> subgraph <<<  RGL__AdjacencyGraph {
=> "graph.png"

Reproduced with ruby version 1.9.3 and RGL 0.4.0

Implicit graph example fails

David Inman filed this bug at rubyforge
http://rubyforge.org//tracker/?func=detail&atid=511&aid=29788&group_id=110

Conditions:

  • I'm a novice with RGL!
  • rgl (0.4.0)
  • stream (0.5)
  • Scientific Linux 5.6
  • ruby 1.9.3p194
  • 'gem install' installed stream and rgl w/out errors

irb(main):001:0> require 'rgl/implicit'
=> true
irb(main):002:0> def module_graph
irb(main):003:1> RGL::ImplicitGraph.new { |g|
irb(main):004:2* g.vertex_iterator { |b|
irb(main):005:3* ObjectSpace.each_object(Module, &b)
irb(main):006:3> }
irb(main):007:2> g.adjacent_iterator { |x, b|
irb(main):008:3* x.ancestors.each { |y|
irb(main):009:4* b.call(y) unless x == y || y == Kernel || y == Object
irb(main):010:4> }
irb(main):011:3> }
irb(main):012:2> g.directed = true
irb(main):013:2> }
irb(main):014:1> end
=> nil
irb(main):015:0> g = module_graph
ArgumentError: comparison of RGL::Edge::DirectedEdge with RGL::Edge::DirectedEdge failed
from /home/davei/.gem/ruby/1.9.3/gems/rgl-0.4.0/lib/rgl/base.rb:193:in sort' from /home/davei/.gem/ruby/1.9.3/gems/rgl-0.4.0/lib/rgl/base.rb:193:into_s'
from /usr/pack/ruby-1.9.3-jn/x86_64-Linux-2.6/bin/irb:12:in `

'
irb(main):016:0>

code snippet from base.rb:
# Utility method to show a string representation of the edges of the graph.
def to_s
edges.sort.to_s # <<<<----- line 193 >>>>
end

This looks like a fantastic piece of work. I'm excited to use it.
Thanks,
Dave

`depth_first_search` Doesn't work

Hi!

I tried to use:

visitor = @rgl_graph.dfs_iterator(@root_vertex_id)
visitor.attach_distance_map(@rgl_edge_weights)
visitor.set_examine_vertex_event_handler { |v|
    puts ">>> Visitor: #{v}"
}
@rgl_graph.depth_first_search(visitor) {
    puts ">>> DFS handler: #{v}"
}

But if fails with an exception saying

undefined method `handle_start_vertex' for #<RGL::DFSIterator:0x000000010f5f08d8 @graph=#<RGL::DirectedAdjacencyGraph:0x000000010f5f1a08 @edgelist_class=Set, @vertices_dict={"1039ea5f-2b61-450a-8f38-16d9443295ca"=>#<Set: {}>, "9fbc9bdb-0270-4f64-8e7a-864d2ce7f17c"=>#<Set: {"1039ea5f-2b61-450a-8f38-16d9443295ca"}>}>, @color_map={"1039ea5f-2b61-450a-8f38-16d9443295ca"=>:GRAY}, @start_vertex="1039ea5f-2b61-450a-8f38-16d9443295ca", @waiting=["1039ea5f-2b61-450a-8f38-16d9443295ca"], @distance_map=[{["9fbc9bdb-0270-4f64-8e7a-864d2ce7f17c", "1039ea5f-2b61-450a-8f38-16d9443295ca"]=>8.73408059691218}], @examine_vertex_event_handler=#<Proc:0x000000010f5f0540

Which leads to:

vis.handle_start_vertex(u)

When I use content of depth_first_search method directly (minus handle_start_vertex part) it looks like it works

    @rgl_graph.each_vertex do |u|
      unless visitor.finished_vertex?(u)
        @rgl_graph.depth_first_visit(u, visitor) { |v|
          puts ">>> DFS handler: #{v}"
        }
      end
    end

How to find the longest path between 2 vertices ?

@monora Thanks for your effort to create this gem.

The solution I found that to use -ve weight for edges and then find shortest path and that will be equal to longest path. In docs I found that -ve weight for edges will return error for various shortest path algorithm methods.

How should I overcome this ?

How can I add a subgraph?

I've just recently started using this to automatically generate some overview diagrams for file relationships but I'm running into some problems with placing the nodes in a human readable way. I find subgraphs over and over to possibly be the solution. Does the library in the current state support subgraphs and/or clustered nodes? The documentation is not conclusive on this.

dijkstra_shortest_path strangely fails to find a path

There is some really strange error in dijkstra_shortest_path (or I am missing something very obvious).

irb(main):001:0> require 'rgl/adjacency'
=> true
irb(main):002:0> require 'rgl/dijkstra'
=> true
irb(main):003:0> g = RGL::AdjacencyGraph[2,53, 2,3, 3,8, 3,28, 3,39, 29,58, 8,35, 12,39, 10,29, 62,15, 15,32,  32,58, 58,44, 44,53]
=> #<RGL::AdjacencyGraph:0x00000001c6ff88 @edgelist_class=Set, @vertices_dict={2=>#<Set: {53, 3}>, 53=>#<Set: {2, 44}>, 3=>#<Set: {2, 8, 28, 39}>, 8=>#<Set: {3, 35}>, 28=>#<Set: {3}>, 39=>#<Set: {3, 12}>, 29=>#<Set: {58, 10}>, 58=>#<Set: {29, 32, 44}>, 35=>#<Set: {8}>, 12=>#<Set: {39}>, 10=>#<Set: {29}>, 62=>#<Set: {15}>, 15=>#<Set: {62, 32}>, 32=>#<Set: {15, 58}>, 44=>#<Set: {58, 53}>}>
irb(main):004:0> g.dijkstra_shortest_path(Hash.new(1),53,62)
=> nil

But there is a path [53, 44, 58, 32, 15, 62]. I tried to minimalize the graph furher, but strangely after removing any edge it suddenly behaves correctly (either finds path or not when there is not one).

PS: I can fork the repo and request pull with the test, if it helps.

Rubygems release?

Hi there,

Just curious if you had plans to make a new Rubygems release?

We have a library we'd like to open-source that depends on rgl, but would like to ensure that users that adopt this library don't run into this (fixed) bug: #36 😄

Thanks!

how i can use this in laravel ?

hello . i work with big tree in multi level marketing with php with model it work good but i have issue for big tree about 1M branch , do you can help me or way for work with fast speed ?

Outdated release ?

When i try to install it via gem install rgl, my version is 0.5.1.
It still has problems with heap fixed in master. So, is there a plan for release ?

Is rgl thread safe?

Hi - thanks for supporting a great library.

We're thinking of using rgl on one of our projects and we were wondering whether it's thread safe?

Thanks

set_to_begin incorrect for graph iterator

The following test currently fails for bfs_iterator:

def test_bfs_stream_protocol
    it = @dg.bfs_iterator(1)
    assert_true(it.at_beginning?)

    it.set_to_end()
    assert_true(it.at_end?)

    it.set_to_begin()
    # Initial state is not correctly set. Therefor this check fails:
    assert_true(it.at_beginning?)
end

Dependency on algorithms

Would it be possible to bump the algorithms gem to 6.x? I think they added support for JRuby. It would be awesome to have this great gem available for JRuby as well.

Licence specification

We have been asked to review the licences of gems we use. Can we get clarification of the licence for rgl? Thank you!

DOT keywords as labels cause DOT syntax errors

The keywords node, edge, graph, digraph, subgraph, and strict (case insensitive) cause syntax errors in the DOT format when they're used as labels.

The DOT language documentation notes that: 'There is no semantic difference between abc_2 and "abc_2", or between 2.34 and "2.34". Obviously, to use a keyword as an ID, it must be quoted.' (http://www.graphviz.org/doc/info/lang.html) As far as I can tell, the same applies to labels. If this is the case, it seems that it would be best to double-quote all labels generated by the to_dot_graph function and escape any double-quotes in the labels themselves.

New tagged version

There have been a handful of changes since the last tagged version, and a v0.6.0 would be nice.

write_to_graphic_file('jpg') does not produce a jpg file

I ran the below example from the readme verbatim, and I did not got a graph.jpg file, instead it created a graph.dot file.

Example from README.md

irb> require 'rgl/adjacency'
irb> dg=RGL::DirectedAdjacencyGraph[1,2 ,2,3 ,2,4, 4,5, 6,4, 1,6]
# Use DOT to visualize this graph:
irb> require 'rgl/dot'
irb> dg.write_to_graphic_file('jpg')
"graph.jpg"

Spurious nodes in DOT when vertices are strings

require 'rgl/adjacency'
require 'rgl/dot'

puts RGL::DirectedAdjacencyGraph["a", "b"].to_dot_graph

Output:

digraph RGL__DirectedAdjacencyGraph {
    70319033367680 [
        fontsize = 8,
        label = a
    ]

    70319033367400 [
        fontsize = 8,
        label = b
    ]

    70319033367680 -> 70319033367380 [
        fontsize = 8
    ]
}

Note the extra node; the "b" node is given a new name in the edge definition.

This happens because the adjacency list for each vertex is stored as a Set. Set uses Hash as internal storage, and Hash copies non-frozen string keys. Therefore when "b" is added to the adjacency list of "a," a new object is created. The DOT module (dot.rb) uses the Ruby object ID as the node name and therefore generates two "b" nodes.

dot.rb should probably be changed not to use object_id, for consistency with the rest of the library.

README outdated?

It seems to me that the README is somehow outdated. Namely first example did not generate any jpg for me (only dot text file) and second one returned (after dg.edges.sort.to_s) something like
"[#<RGL::Edge::UnDirectedEdge:0x000000029867f8 @source=1, @target=2>, #<RGL::Edge::UnDirectedEdge:0x00000002986618 @source=3, @target=4>, #<RGL::Edge::UnDirectedEdge:0x00000002986438 @source=5, @target=6>]"

instead of textual representation of the graph.

Gem is not loading

I appreciate this library exists, but I could not use it

ruby-3.2.2
Rails 7.1.2
Mac M1

rails new testrgl --api --skip-action-mailer --skip-action-cable --skip-test --skip-active-record

gem install rgl

irb/rails console

require 'rgl'

=> .rvm/rubies/ruby-3.2.2/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:38:in `require': cannot load such file -- rgl (LoadError)
RGL # or any RGL constant
=> (irb):1:in `<main>': uninitialized constant RGL (NameError)

undefined method `set_vertex_options'

When I execute the snippet from https://github.com/monora/rgl/blob/master/README.md#optional-configuring-graphviz-dot-output-options I get the follwing error

dot.rb:6:in `<main>': undefined method `set_vertex_options' for #<RGL::DirectedAdjacencyGraph:0x00000001044a3e18 @edgelist_class=Set, @vertices_dict={"a"=>#<Set: {"b", "c"}>, "b"=>#<Set: {}>, "c"=>#<Set: {"d"}>, "d"=>#<Set: {}>}> (NoMethodError)

graph.set_vertex_options('a', label: 'This is A', shape: 'box3d', fontcolor: 'green', fontsize: 16)

What am I missing?

Remove superfluous :GRAY assignment in depth first visit

Discussed in #65

Originally posted by delphaber October 12, 2022
Isn't line 200 superfluous? We are already marking the vertex as :GRAY in line 192 when we go deeper in the recursion level at line 201.

Just wondering if I'm missing something :)

rgl/lib/rgl/traversal.rb

Lines 191 to 201 in 3cdf5c2

def depth_first_visit(u, vis = DFSVisitor.new(self), &b)
vis.color_map[u] = :GRAY
vis.handle_examine_vertex(u)
each_adjacent(u) do |v|
vis.handle_examine_edge(u, v)
if vis.follow_edge?(u, v) # (u,v) is a tree edge
vis.handle_tree_edge(u, v) # also discovers v
vis.color_map[v] = :GRAY # color of v was :WHITE
depth_first_visit(v, vis, &b)

RuntimeError: Couldn't delete node from stored nodes hash in `dijkstra_shortest_path`

The following code

g = RGL::AdjacencyGraph[9,14, 13,19, 17,20, 2,25, 10,25, 4,25, 23,25, 22,25, 9,22, 13,21, 6,17, 2,7, 3,10, 4,11, 5,23]
g.dijkstra_shortest_path(Hash.new(1),25,14)

fails with "RuntimeError: Couldn't delete node from stored nodes hash" with backtrace:

         # /var/lib/gems/2.1.0/gems/algorithms-0.6.1/lib/containers/heap.rb:236:in `pop'
         # /var/lib/gems/2.1.0/gems/rgl-0.5.1/lib/rgl/dijkstra.rb:64:in `relax_edges'
         # /var/lib/gems/2.1.0/gems/rgl-0.5.1/lib/rgl/dijkstra.rb:32:in `shortest_path'
         # /var/lib/gems/2.1.0/gems/rgl-0.5.1/lib/rgl/dijkstra.rb:141:in `dijkstra_shortest_path'

Unclear how to use bellman_ford_shortest_paths method

I'm trying to figure out how to use this method with no luck. I'm a bit new to Ruby but as far as I can tell AdjacencyGraph should mix in this method by virtue of inheriting from DirectedAdjacencyGraph and its mix-ins. Here is a session of me trying to make a trivial graph and then access the method:

[1] pry(main)> require 'rgl/adjacency'
=> true
[2] pry(main)> RGL::AdjacencyGraph
RGL::AdjacencyGraph
[2] pry(main)> g = RGL::AdjacencyGraph.new
=> #<RGL::AdjacencyGraph:0x000055820af89cd8 @edgelist_class=Set, @vertices_dict={}>
[3] pry(main)> g.add_edge(:a, :b)
=> #<Set: {:a}>
[4] pry(main)> g.add_edge(:b, :d)
=> #<Set: {:b}>
[5] pry(main)> g
=> #<RGL::AdjacencyGraph:0x000055820af89cd8 @edgelist_class=Set, @vertices_dict={:a=>#<Set: {:b}>, :b=>#<Set: {:a, :d}>, :d=>#<Set: {:b}>}>
[6] pry(main)> g.add_edge(:b, :c)
=> #<Set: {:b}>
[7] pry(main)> g.add_edge(:c, :e)
=> #<Set: {:c}>
[8] pry(main)> g.edges
=> [(a-b), (b-d), (b-c), (c-e)]
[9] pry(main)> g.bellman_ford_shortest_paths
NoMethodError: undefined method `bellman_ford_shortest_paths' for #<RGL::AdjacencyGraph:0x000055820af89cd8>
from (pry):9:in `__pry__'

I'm also not sure how edge_weights_map is specified.

Vertices with duplicated labels not shown in DOT visualisation

Suppose we have the following graph containing 3 vertices:

class Person
  attr_reader :name
  def initialize(name)
    @name = name
  end

  def to_s
    name
  end
end

dg=RGL::DirectedAdjacencyGraph.new
dg.add_vertex(Person.new("Alice"))
dg.add_vertex(Person.new("Bob"))
dg.add_vertex(Person.new("Alice"))

When the graph is visualised via dg.write_to_graphic_file('jpg') it has only two vertices:

graph

Refactor edge/vertex settings to no longer require individual options initialization

In the current implementation the user will use the methods set_vertex_options and set_edge_options location in dot.rb to set the options for the respective element of the graph. For this to work properly the user needs to initialize two procs and two hashes with all the options they want to use.

require 'rgl/adjacency'
require 'rgl/dot'

graph = RGL::DirectedAdjacencyGraph['a', 'b', 'b', 'c']

graph.add_edge('a', 'b')
graph.add_edge('a', 'c')

# Vertex Settings
graph.set_vertex_options('a', label: 'This is A', shape: 'box3d', fontcolor: 'green', fontsize: 16)
graph.set_vertex_options('b', label: 'This is B', shape: 'tab', fontcolor: 'red', fontsize: 14)
graph.set_vertex_options('c', shape: 'tab', fontcolor: 'blue')

# Edge Settings
graph.set_edge_options('a', 'b', label: 'NotCapitalEdge', style: 'dotted', direction: 'back', color: 'yellow')
graph.set_edge_options('a', 'c', weight: 5, color: 'blue')

# puts @vertex_options
# puts @edge_options

get_vertex_setting = proc { |v| graph.vertex_options[v] }
get_edge_setting = proc { |b, e| graph.edge_options[graph.edge_class.new(b, e)] }

# To configure more options, add the respective keys and the proc call here
# Then provide the respective key:value to set_vertex_options
# Any hard coded values for a key will be applied to all nodes
vertex_options = {
  'label' => get_vertex_setting,
  'shape' => get_vertex_setting,
  'fontcolor' => get_vertex_setting,
  'fontsize' => get_vertex_setting
}

# To configure more options, add the respective keys and the proc call here
# Then provide the respective key:value to set_edge_options
# Any hard coded values for a key will be applied to all edges
edge_options = {
  'label' => get_edge_setting,
  'dir' => get_edge_setting,
  'color' => get_edge_setting,
  'style' => get_edge_setting,
  'weight' => get_edge_setting,
  'constraint' => get_edge_setting,
  'headlabel' => get_edge_setting,
  'taillabel' => get_edge_setting
}

# Options
  dot_options = { 'edge' => edge_options, 'vertex' => vertex_options }

graph.write_to_graphic_file('png', 'graph', dot_options)

We should move vertex_options and edge_options and the respective procs somewhere into the initialize phase for the graph so they get populated with all valid options automatically and the user does not have to concern themselves with it and just pass the respective option combination to the methods. I have not yet found the right place in the classes where this would apply to all graph types and can be used properly.

#89 (comment)
#89 (comment)

Example of Edge Properties Map

Could you give me an example of how to set properties to edges? Or an example of adding new edges with weight and other attributes?

rgl/enumerable_ext.rb breaks unrelated library

Hi there,

We're using rgl in one of our apps, but it appears the Enumerable extension in lib/rgl/enumerable_ext.rb is breaking some unrelated libraries 😢.

The underlying problem is that you'd normally expect length to be an idempotent method (e.g. calling 'some string'.length doesn't change the string), and this assumption is present in various libraries, including the AWS SDK (that's where we are having issues 😄, but it's likely this isn't the only library that calls #length): https://github.com/aws/aws-sdk-ruby/blob/589f8cd569d3c365189945dd2e7a10bc9df775ea/lib/aws/s3/data_options.rb#L100

This turns out to be a problem when you provide an enumerable whose inject method isn't idempotent.

In our case, we're passing an IO Pipe to the AWS SDK, which is enumerable. As a result, the AWS SDK calls #length on the pipe since it appears to respond to that method (because #length is defined by RGL), and then the pipe ends up exhausted when it later tries to read from it (and bad things ensue).

This method appears to be only used on TopsortIterator; perhaps it could be defined there (or in GraphIterator if it's moe generally useful)?

Thanks!

The License of the Project is Unclear

At the end of the README.md there's the text

RGL is Copyright (c) 2002,2004,2005,2008,2013,2015,2019 
by Horst Duchene. It is free software, and may be redistributed 
under the terms specified in the README file of the Ruby distribution.

but it does not really specify, whether the "free software"
is licensed under something as restrictive as the GPL or
is it licensed under something BSD-like. I suggest the use of

https://spdx.dev/ids/

The idea is that at each source code there is a
special string, a license label, as part of the source code header.
That label duplicates the human-readable license information.
For example, for one of the variants of the BSD license the string is:

SPDX-License-Identifier: BSD-3-Clause-Clear

As of 2020_12_04 the list of different license IDs can be found from

https://spdx.org/licenses/

depth_first_search ?

Either the documentation is incomplete or I'd expect a depth_first result in the case below:
Is there any code which does sort the edges to force the desired result?

    require 'rgl/adjacency'
    require 'rgl/traversal'


    # sample graph DAG, two roots:
    #1
    #    2
    #       3
    #4
    #    5
    #       6


    graph = RGL::DirectedAdjacencyGraph[
      # starting with a non root node
      5,6, 4,5,
      1,2, 2,3
    ]
    graph.depth_first_search {|v|
      puts v
    }

    # output:
    # || 6
    # || 5
    # || 4
    # || 3
    # || 2
    # || 1
    # I'd expect it to start with either 1 or 4

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.