usearchtree 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []