usearchtree 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,8 @@
1
+ require 'usearchtree/search'
2
+ require 'usearchtree/breadth'
3
+ require 'usearchtree/depth'
4
+ require 'usearchtree/edge'
5
+ require 'usearchtree/graph'
6
+ require 'usearchtree/graphloader'
7
+ require 'usearchtree/node'
8
+ require 'usearchtree/uniform'
@@ -0,0 +1,22 @@
1
+ class BreadthFirstSearch < SearchAlgorithm
2
+ def search
3
+ @list = [@start]
4
+ until @list.empty?
5
+ @history << @list.clone
6
+ parent = @list.shift
7
+ @traversal << parent
8
+ @cache[parent.key] = parent
9
+ if parent == @goal
10
+ return
11
+ end
12
+ parent.edges.each do |edge|
13
+ child = edge.node
14
+ if @cache and not @cache.has_key?(child.key)
15
+ @cache[child.key] = child
16
+ @tree[child] = parent
17
+ @list << child
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ class DepthFirstSearch < SearchAlgorithm
2
+ def search
3
+ @list = [@start]
4
+ until @list.empty?
5
+ @history << @list.clone
6
+ parent_node = @list.pop
7
+ @traversal << parent_node
8
+ @cache[parent_node.key] = parent_node
9
+ if parent_node == @goal
10
+ return
11
+ end
12
+ parent_node.edges.reverse_each do |edge|
13
+ node = edge.node
14
+ cost = edge.cost
15
+ if not @cache or not @cache.has_key? node.key
16
+ @cache[node.key] = node
17
+ @tree[node] = parent_node
18
+ @list.push node
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,68 @@
1
+ # Represents any weighted, directed graph.
2
+ #
3
+ # Author: Johnny Lee Othon
4
+ class Graph
5
+
6
+ def initialize
7
+ @nodes = Array.new
8
+ @next_id = 0
9
+ end
10
+
11
+ # Returns the list of nodes
12
+ def to_s
13
+ @nodes.to_s
14
+ end
15
+
16
+ # Returns the graph as a list of adjacencies
17
+ def to_adjacency_lists
18
+ if @nodes.empty?
19
+ "Empty graph"
20
+ else
21
+ @nodes.collect{|node| node.to_adjacency_list}.join("\n")
22
+ end
23
+ end
24
+
25
+ def node key
26
+ if key.kind_of? Integer
27
+ @nodes[key]
28
+ else
29
+ @nodes.find{|n| n.name == key}
30
+ end
31
+ end
32
+
33
+ # Adds a node to the graph and optionally sets a name.
34
+ def add_node name=nil
35
+ @nodes << Node.new(self.next_id, name)
36
+ end
37
+
38
+ # Adds an edge from node i to node j. Where i and j can be an index or a
39
+ # name.
40
+ def add_edge i, j, cost
41
+ self.node(i).add_edge self.node(j), cost
42
+ end
43
+
44
+ # Labels a single node with index i. Fails if the node does not exist.
45
+ def label_node i, name
46
+ @nodes[i].name = name
47
+ end
48
+
49
+ # Gets the number of nodes in the graph
50
+ def length
51
+ @nodes.length
52
+ end
53
+
54
+ # Gets the cost from node at i to node at j.
55
+ def cost i, j
56
+ nodeI = (i.kind_of? Node) ? i : self.node(i)
57
+ nodeJ = (j.kind_of? Node) ? j : self.node(j)
58
+ nodeI.cost(nodeJ)
59
+ end
60
+
61
+ # Gets and increases the next id.
62
+ def next_id
63
+ id = @next_id
64
+ @next_id += 1
65
+ id
66
+ end
67
+
68
+ end
@@ -0,0 +1,41 @@
1
+ # Consumes a node_labels.txt file
2
+ # e.g. http://people.sc.fsu.edu/~jburkardt%20/data/graph_representation/mst_node_labels.txt
3
+ def load_label_nodes graph, file
4
+ File.open(file, 'r') do |f|
5
+ f.lazy.reject do |line|
6
+ line.chomp.empty? or line.start_with? '#'
7
+ end.with_index do |line, i|
8
+ line.chomp!
9
+ if not line.empty? and not line.start_with? '#'
10
+ unless graph.length > i
11
+ graph.add_node
12
+ end
13
+ graph.label_node(i, line)
14
+ end
15
+ end
16
+ end
17
+ return graph
18
+ end
19
+
20
+ # Loads a distance matrix. Rows correspond to source nodes and columns
21
+ # correspond to destination nodes. -1 indicates a non-connection, 0 indicates
22
+ # the same node, and positive values indicate an edge.
23
+ def load_distance_matrix graph, file
24
+ File.open(file, 'r') do |f|
25
+ f.each.reject do |line|
26
+ line.empty? or line.start_with? '#'
27
+ end.each.with_index do |line, i|
28
+ costs = line.split
29
+ costs = costs.collect &:to_i
30
+ costs.each.with_index do |cost, j|
31
+ unless graph.length > j
32
+ graph.add_node
33
+ end
34
+ unless cost.zero? or cost == -1
35
+ graph.add_edge i, j, cost
36
+ end
37
+ end
38
+ end
39
+ end
40
+ return graph
41
+ end
@@ -0,0 +1,66 @@
1
+ # Represents to a node in the graph
2
+ #
3
+ # Author: Johnny Lee Othon
4
+ class Node
5
+
6
+ attr_accessor :id, :name
7
+
8
+ include Comparable
9
+
10
+ def initialize id, name=nil
11
+ @name = name
12
+ @id = id
13
+ @edges = Array.new
14
+ end
15
+
16
+ # Returns the name of the node or its id as a string.
17
+ def to_s
18
+ @name or "#{@id}"
19
+ end
20
+
21
+ # Returns the name of the node or its id as a string.
22
+ def inspect
23
+ self.to_s
24
+ end
25
+
26
+ def <=> other
27
+ return @id <=> other.id
28
+ end
29
+
30
+ # Returns the node in the format <code><id:name></code>.
31
+ def to_adjacency_list
32
+ s = "#{name}"
33
+ unless @name.nil? or @name.empty?
34
+ s += "(#{@id})"
35
+ end
36
+ unless @edges.empty?
37
+ s += ": "
38
+ end
39
+ nodes = @edges.map do |edge|
40
+ "#{edge.node}=>#{edge.cost}"
41
+ end.join(", ")
42
+ s += nodes
43
+ end
44
+
45
+ def add_edge node, cost
46
+ @edges << Edge.new(node, cost)
47
+ end
48
+
49
+ def cost node
50
+ edge = @edges.find{|e| e.node == node}
51
+ if edge
52
+ edge.cost
53
+ end
54
+ end
55
+
56
+ # Gets the name if exists or the id
57
+ def key
58
+ @name or @id
59
+ end
60
+
61
+ # Gets an enumerator of the edges
62
+ def edges
63
+ return @edges.each
64
+ end
65
+
66
+ end
@@ -0,0 +1,58 @@
1
+ # Author: Johnny Lee Othon
2
+ class SearchAlgorithm
3
+
4
+ attr_reader :goal, :history, :space, :start, :traversal, :tree
5
+
6
+ # Initializes a search algorithm.
7
+ # Parameters:
8
+ # [space] a graph
9
+ # [start] the id or name of the starting node
10
+ # [goal] the id or name of the goal node
11
+ # [cache] <code>true</code> a cache of visited nodes is desired
12
+ def initialize space, start, goal, caches=true
13
+ @space = space
14
+ @start = space.node(start)
15
+ @goal = space.node(goal)
16
+ # node.key => node
17
+ @cache = (Hash.new if caches)
18
+ # order in which the graph nodes were traversed
19
+ @traversal = Array.new
20
+ # parent => parent_node
21
+ @tree = {@start => nil}
22
+ # lazy list of nodes from start to goal
23
+ @path = Array.new
24
+ # lazy total of cost from start to goal
25
+ @cost = nil
26
+ # history to see the history of the stack
27
+ @history = Array.new
28
+ end
29
+
30
+ # Performs the search and populates <code>traversal</code> and
31
+ # <code>tree</code>
32
+ def search
33
+ throw "Unimplemented"
34
+ end
35
+
36
+ # Returns a list of all nodes in order from start to goal.
37
+ def path
38
+ return @path if @path.any?
39
+ node = @goal
40
+ @cost = 0
41
+ while node
42
+ parent = @tree[node]
43
+ @path.insert(0, node)
44
+ @cost += parent ? parent.cost(node) : 0
45
+ node = parent
46
+ end
47
+ return @path
48
+ end
49
+
50
+ # Retruns the total cost from start to goal following the path specifed in
51
+ # <code>SearchAlgorithm#Path</code>.
52
+ def cost
53
+ self.path if @path.empty?
54
+ return @cost
55
+ end
56
+
57
+
58
+ end
@@ -0,0 +1,24 @@
1
+ class UniformCostSearch < SearchAlgorithm
2
+ def search
3
+ @list = [[0, @start]]
4
+ until @list.empty?
5
+ @history << @list.clone
6
+ cost, parent_node = @list.shift
7
+ @cache[parent_node.key] = parent_node.key
8
+ @traversal << [cost, parent_node]
9
+ return if parent_node == @goal
10
+ pcost = cost
11
+ parent_node.edges.each do |edge|
12
+ node = edge.node
13
+ cost = edge.cost + pcost
14
+ if not @cache or not @cache.has_key? node.key
15
+ @cache[node.key] = node
16
+ unless @tree.has_key? node
17
+ @tree[node] = parent_node
18
+ end
19
+ @list << [cost, node]
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
metadata ADDED
@@ -0,0 +1,53 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: usearchtree
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Johnny Lee Othon
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-02-14 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Uninformed search trees!
15
+ email: jleeothon@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/usearchtree.rb
21
+ - lib/usearchtree/breadth.rb
22
+ - lib/usearchtree/depth.rb
23
+ - lib/usearchtree/graph.rb
24
+ - lib/usearchtree/graphloader.rb
25
+ - lib/usearchtree/node.rb
26
+ - lib/usearchtree/search.rb
27
+ - lib/usearchtree/uniform.rb
28
+ homepage: https://github.com/jleeothon/usearchtree
29
+ licenses:
30
+ - MIT
31
+ post_install_message:
32
+ rdoc_options: []
33
+ require_paths:
34
+ - lib
35
+ required_ruby_version: !ruby/object:Gem::Requirement
36
+ none: false
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ required_rubygems_version: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ requirements: []
48
+ rubyforge_project:
49
+ rubygems_version: 1.8.23
50
+ signing_key:
51
+ specification_version: 3
52
+ summary: Uninformed search trees!
53
+ test_files: []