compsci 0.1.1.1 → 0.2.0.1

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.
@@ -1,6 +1,8 @@
1
- require 'compsci/node'
1
+ # require 'compsci/node'
2
2
 
3
3
  module CompSci
4
+ # for now at least, this assumes children simply stack up
5
+ # children like: [nil, child1, child2] are not supported
4
6
  class Tree
5
7
  attr_reader :root
6
8
 
@@ -8,6 +10,7 @@ module CompSci
8
10
  @root = node_class.new val
9
11
  end
10
12
 
13
+ # does not support children gaps
11
14
  def df_search(node: nil, &blk)
12
15
  node ||= @root
13
16
  return node if yield node
@@ -18,6 +21,7 @@ module CompSci
18
21
  nil
19
22
  end
20
23
 
24
+ # does not support children gaps
21
25
  def bf_search(node: nil, &blk)
22
26
  node ||= @root
23
27
  destinations = [node]
@@ -38,6 +42,15 @@ module CompSci
38
42
  end
39
43
 
40
44
  class NaryTree < Tree
45
+ # thanks apeiros
46
+ # https://gist.github.com/rickhull/d0b579aa08c85430b0dc82a791ff12d6
47
+ def self.power_of?(num, base)
48
+ return false if base <= 1
49
+ mod = 0
50
+ num, mod = num.divmod(base) until num == 1 || mod > 0
51
+ mod == 0
52
+ end
53
+
41
54
  attr_reader :child_slots
42
55
 
43
56
  def initialize(node_class, val, child_slots:)
@@ -0,0 +1,59 @@
1
+ require 'compsci/complete_tree'
2
+ require 'compsci/timer'
3
+ require 'compsci/fit'
4
+
5
+ include CompSci
6
+
7
+ timing = {}
8
+ ns = [10, 100, 1000, 10_000, 100_000]
9
+
10
+ # Note, CompleteTree is a very thin wrapper around Array, so we are just
11
+ # testing ruby's inherent Array performance here.
12
+ # Append / push / insert is constant for ruby Arrays.
13
+
14
+ puts <<EOF
15
+
16
+ #
17
+ # timing CompleteTree(N)#push where N is the size of the tree
18
+ #
19
+
20
+ EOF
21
+
22
+ ns.each { |n|
23
+ h = CompleteBinaryTree.new
24
+ n.times { h.push rand }
25
+ _val, secs = Timer.loop_avg {
26
+ h.push rand
27
+ }
28
+ puts "CompleteTree(%i) push: %0.8f" % [n, secs]
29
+ timing[n] = secs
30
+ break if secs > 1
31
+ }
32
+
33
+ a, b, r2, fn = Fit.best timing.keys, timing.values
34
+
35
+ puts "best fit: #{fn} (%0.3f); a = %0.6f, b = %0.6f" % [r2, a, b]
36
+
37
+
38
+
39
+ puts <<EOF
40
+
41
+ #
42
+ # timing CompleteTree#push where N is the count of pushes
43
+ #
44
+
45
+ EOF
46
+
47
+ ns.each { |n|
48
+ h = CompleteBinaryTree.new
49
+ _val, secs = Timer.loop_avg {
50
+ n.times { h.push rand }
51
+ }
52
+ puts "%ix CompleteTree#push: %0.8f" % [n, secs]
53
+ timing[n] = secs
54
+ break if secs > 1
55
+ }
56
+
57
+ a, b, r2, fn = Fit.best timing.keys, timing.values
58
+
59
+ puts "best fit: #{fn} (%0.3f); a = %0.6f, b = %0.6f" % [r2, a, b]
@@ -134,10 +134,6 @@ if BENCHMARK_IPS
134
134
  Fibonacci.dynamic(num)
135
135
  }
136
136
 
137
- b.report("Fibonacci.dynamic_fast(#{num})") {
138
- Fibonacci.dynamic_fast(num)
139
- }
140
-
141
137
  b.report("Fibonacci.matrix(#{num})") {
142
138
  Fibonacci.matrix(num)
143
139
  }
@@ -0,0 +1,141 @@
1
+ require 'compsci/simplex'
2
+
3
+ include CompSci
4
+
5
+ BENCH_IPS = true
6
+ BENCH_OBJECT_SPACE = true
7
+
8
+ SIMPLEX_PARAMS = [
9
+ # 1 (index 0)
10
+ [[1, 1],
11
+ [[2, 1],
12
+ [1, 2]],
13
+ [4, 3]],
14
+
15
+ [[3, 4],
16
+ [[1, 1],
17
+ [2, 1]],
18
+ [4, 5]],
19
+
20
+ [[2, -1],
21
+ [[1, 2],
22
+ [3, 2],],
23
+ [6, 12]],
24
+
25
+ [[60, 90, 300],
26
+ [[1, 1, 1],
27
+ [1, 3, 0],
28
+ [2, 0, 1]],
29
+ [600, 600, 900]],
30
+
31
+ # 5
32
+ [[70, 210, 140],
33
+ [[1, 1, 1],
34
+ [5, 4, 4],
35
+ [40, 20, 30]],
36
+ [100, 480, 3200]],
37
+
38
+ [[2, -1, 2],
39
+ [[2, 1, 0],
40
+ [1, 2, -2],
41
+ [0, 1, 2]],
42
+ [10, 20, 5]],
43
+
44
+ [[11, 16, 15],
45
+ [[1, 2, Rational(3, 2)],
46
+ [Rational(2, 3), Rational(2, 3), 1],
47
+ [Rational(1, 2), Rational(1, 3), Rational(1, 2)]],
48
+ [12_000, 4_600, 2_400]],
49
+
50
+ [[5, 4, 3],
51
+ [[2, 3, 1],
52
+ [4, 1, 2],
53
+ [3, 4, 2]],
54
+ [5, 11, 8]],
55
+
56
+ [[3, 2, -4],
57
+ [[1, 4, 0],
58
+ [2, 4,-2],
59
+ [1, 1,-2]],
60
+ [5, 6, 2]],
61
+
62
+ # 10
63
+ [[2, -1, 8],
64
+ [[2, -4, 6],
65
+ [-1, 3, 4],
66
+ [0, 0, 2]],
67
+ [3, 2, 1]],
68
+
69
+ [[100_000, 40_000, 18_000],
70
+ [[20, 6, 3],
71
+ [0, 1, 0],
72
+ [-1,-1, 1],
73
+ [-9, 1, 1]],
74
+ [182, 10, 0, 0]],
75
+
76
+ [[1, 2, 1, 2],
77
+ [[1, 0, 1, 0],
78
+ [0, 1, 0, 1],
79
+ [1, 1, 0, 0],
80
+ [0, 0, 1, 1]],
81
+ [1, 4, 2, 2]],
82
+
83
+ [[10, -57, -9, -24],
84
+ [[0.5, -5.5, -2.5, 9],
85
+ [0.5, -1.5, -0.5, 1],
86
+ [ 1, 0, 0, 0]],
87
+ [0, 0, 1]],
88
+
89
+ # 14 (index 13)
90
+ [[25, 20],
91
+ [[20, 12],
92
+ [1, 1]],
93
+ [1800, 8*15]],
94
+ ]
95
+
96
+ def new_simplices
97
+ SIMPLEX_PARAMS.map { |c, a, b| Simplex.new(c, a, b) }
98
+ end
99
+
100
+ if BENCH_IPS
101
+ require 'benchmark/ips'
102
+
103
+ Benchmark.ips do |b|
104
+ b.config time: 3, warmup: 0.5
105
+
106
+ b.report("Simplex init") {
107
+ new_simplices
108
+ }
109
+
110
+ b.report("init, solve") {
111
+ new_simplices.each { |s| s.solution }
112
+ }
113
+
114
+ #b.report("Simplex Matrix") {
115
+ #}
116
+
117
+ b.compare!
118
+ end
119
+ end
120
+
121
+ if BENCH_OBJECT_SPACE
122
+ require 'compsci/timer'
123
+ require 'objspace'
124
+
125
+ def disp_memsize(var, label = '')
126
+ "memsize(%s): %i" % [label, ObjectSpace.memsize_of(var)]
127
+ end
128
+
129
+ simplices = SIMPLEX_PARAMS.map { |c, a, b|
130
+ Simplex.new(c, a, b)
131
+ }
132
+
133
+ puts "SIMPLEX_PARAMS.size = #{SIMPLEX_PARAMS.size}"
134
+ puts "simplices.size = #{simplices.size}"
135
+
136
+ puts disp_memsize SIMPLEX_PARAMS, 'SIMPLEX_PARAMS'
137
+ puts disp_memsize simplices, 'simplices'
138
+ results = simplices.map { |s| s.solution }
139
+ puts disp_memsize simplices, 'simplices after solving'
140
+ puts disp_memsize results, 'results'
141
+ end
@@ -1,26 +1,31 @@
1
+ require 'compsci/node'
1
2
  require 'compsci/tree'
2
- require 'minitest/autorun'
3
- require 'minitest/benchmark'
3
+ require 'benchmark/ips'
4
4
 
5
5
  include CompSci
6
6
 
7
- describe "BinaryTree#push Benchmark" do
8
- bench_range do
9
- # note, 5000 takes way too long and is definitely not constant time
10
- # TODO: BUG?
11
- # [10, 100, 1000, 2000, 5000]
12
- [10, 100, 1000, 2000]
7
+ Benchmark.ips do |b|
8
+ b.config time: 3, warmup: 0.5
9
+
10
+ b.report("99x BinaryTree(ChildNode)#push") do
11
+ tree = BinaryTree.new(ChildFlexNode, 42)
12
+ 99.times { tree.push rand 99 }
13
13
  end
14
14
 
15
- bench_performance_constant "BinaryTree#push (constant)" do |n|
16
- tree = NaryTree.new(ChildNode, 42, child_slots: 2)
17
- n.times { tree.push rand 99 }
15
+ b.report("99x BinaryTree(FlexNode)#push") do
16
+ tree = BinaryTree.new(FlexNode, 42)
17
+ 99.times { tree.push rand 99 }
18
18
  end
19
19
 
20
- bench_performance_linear "BinaryTree#push (linear)" do |n|
21
- skip "this fails with r^2 around 0.91"
20
+ b.report("99x TernaryTree(ChildFlexNode)#push") do
21
+ tree = TernaryTree.new(ChildFlexNode, 42)
22
+ 99.times { tree.push rand 99 }
23
+ end
22
24
 
23
- tree = NaryTree.new(ChildNode, 42, child_slots: 2)
24
- n.times { tree.push rand 99 }
25
+ b.report("99x TernaryTree(FlexNode)#push") do
26
+ tree = TernaryTree.new(FlexNode, 42)
27
+ 99.times { tree.push rand 99 }
25
28
  end
29
+
30
+ b.compare!
26
31
  end
@@ -0,0 +1,106 @@
1
+ require 'compsci/node'
2
+ require 'compsci/binary_search_tree'
3
+ require 'compsci/names'
4
+ require 'minitest/autorun'
5
+
6
+ include CompSci
7
+
8
+ describe BinarySearchTree do
9
+ def ww2_names(count)
10
+ Array.new(count) { |i| Names::WW2[i].to_s }
11
+ end
12
+
13
+ def nato_names(count)
14
+ Array.new(count) { |i| Names::NATO[i].to_s }
15
+ end
16
+
17
+ before do
18
+ @keys = ww2_names(4)
19
+ @values = Array.new(4) { Names::SOLAR.sample }
20
+ @nodes = Array.new(4) { |i|
21
+ KeyNode.new(@values[i], key: @keys[i], children: 2)
22
+ }
23
+ @tree = BinarySearchTree.new(@nodes.first)
24
+
25
+ # tree will look like:
26
+ # A:val1
27
+ # B:val2
28
+ # C:val3
29
+ # D:val4
30
+ end
31
+
32
+ it "must display_level" do
33
+ str = BinarySearchTree.display_level nodes: @nodes, width: 80
34
+ str.size.must_be :>=, 80 # it can overflow
35
+
36
+ str = BinarySearchTree.display_level nodes: @nodes, width: 200
37
+ str.size.must_equal 200 # it won't overflow
38
+
39
+ @keys.each { |k| str.must_include k.to_s }
40
+ @values.each { |v| str.must_include v.to_s }
41
+ end
42
+
43
+ it "must provide a new_node" do
44
+ node = BinarySearchTree.new_node('the key', 'the value')
45
+ node.must_be_kind_of Node
46
+ node.must_be_kind_of KeyNode
47
+ node.key.must_equal 'the key'
48
+ node.value.must_equal 'the value'
49
+ end
50
+
51
+ it "must instantiate with key and value" do
52
+ tree = BinarySearchTree.new_with_kv('the key', 'the value')
53
+ node = tree.root
54
+ node.must_be_kind_of Node
55
+ node.must_be_kind_of KeyNode
56
+ node.key.must_equal 'the key'
57
+ node.value.must_equal 'the value'
58
+ end
59
+
60
+ it "must decide what to do with duplicate nodes" do
61
+ end
62
+
63
+ it "must decide what to do with duplicate keys" do
64
+ end
65
+
66
+ it "must insert nodes" do
67
+ 1.upto(@nodes.size - 1) { |i| @tree[@keys[i]] = @values[i] }
68
+ @tree.root.children.wont_be_empty
69
+ @tree.root.children[0].nil?.must_equal true
70
+ @tree.root.children[1].key.must_equal @keys[1]
71
+ @tree.root.children[1].children[0].nil?.must_equal true
72
+ @tree.root.children[1].children[1].value.must_equal @values[2]
73
+ end
74
+
75
+ it "must search nodes" do
76
+ tree = nil
77
+ new_order = (0..9).to_a.shuffle
78
+ new_order.each { |i|
79
+ k, v = Names::NATO[i], Names::SOLAR.sample
80
+ if tree.nil?
81
+ tree = BinarySearchTree.new_with_kv(k, v)
82
+ else
83
+ tree[k] = v
84
+ end
85
+ }
86
+
87
+ 2.times {
88
+ i = new_order.sample
89
+ key = Names::NATO[new_order.sample]
90
+ node = tree.search_iterative key
91
+ node.wont_be_nil
92
+ node.key.must_equal key
93
+ }
94
+
95
+ 2.times {
96
+ i = new_order.sample
97
+ key = Names::NATO[new_order.sample]
98
+ node = tree.search_recursive key
99
+ node.wont_be_nil
100
+ node.key.must_equal key
101
+ }
102
+
103
+ tree.search_iterative(Names::SOLAR.sample).must_be_nil
104
+ tree.search_recursive(Names::SOLAR.sample).must_be_nil
105
+ end
106
+ end
@@ -27,24 +27,18 @@ describe Fit do
27
27
  end
28
28
 
29
29
  # y = a
30
+ # Note: Thinking about dropping this.
31
+ # I don't know how to test the variance for constantness or any
32
+ # alternate measure. A low slope and r2 for linear fit, maybe.
33
+ #
30
34
  describe "Fit.constant" do
31
- it "must accept constant data" do
35
+ it "must stuff" do
32
36
  [0, 1, 10, 100, 1000, 9999].each { |a|
33
37
  y_bar, variance = Fit.constant(@xs, @xs.map { |x| a })
34
38
  y_bar.must_equal a
35
39
  variance.must_equal 0
36
40
  }
37
41
  end
38
-
39
- # note, this test can possibly fail depending on the uniformity of
40
- # rand's output for our sample
41
- it "must accept noisy constant data" do
42
- [0, 1, 10, 100, 1000, 9999].each { |a|
43
- y_bar, variance = Fit.constant(@xs, @xs.map { |x| a + noise() })
44
- y_bar.must_be_close_to a, 0.3
45
- (variance / @xs.size).must_be_close_to 0.1, 0.09
46
- }
47
- end
48
42
  end
49
43
 
50
44
  # y = a + b*ln(x)
@@ -16,6 +16,42 @@ describe Node do
16
16
  }
17
17
  end
18
18
 
19
+ it "must not respond to :parent" do
20
+ @martin_sheen.respond_to?(:parent).must_equal false
21
+ end
22
+ end
23
+
24
+ describe KeyNode do
25
+ before do
26
+ @martin_sheen = KeyNode.new 'martin', key: 'marty'
27
+ @charlie_sheen = KeyNode.new 'charlie', key: 'charles'
28
+ @emilio_estevez = KeyNode.new 'emilio', key: 'emile'
29
+ end
30
+
31
+ it "must start with no children" do
32
+ [@martin_sheen, @charlie_sheen, @emilio_estevez].each { |n|
33
+ n.children.must_be_empty
34
+ }
35
+ end
36
+
37
+ it "must not respond to :parent" do
38
+ @martin_sheen.respond_to?(:parent).must_equal false
39
+ end
40
+
41
+ it "must have a key" do
42
+ @martin_sheen.key.must_equal 'marty'
43
+ @charlie_sheen.key.must_equal 'charles'
44
+ @emilio_estevez.key.must_equal 'emile'
45
+ end
46
+ end
47
+
48
+ describe FlexNode do
49
+ before do
50
+ @martin_sheen = FlexNode.new 'martin'
51
+ @charlie_sheen = FlexNode.new 'charlie'
52
+ @emilio_estevez = FlexNode.new 'emilio'
53
+ end
54
+
19
55
  it "must track children" do
20
56
  @charlie_sheen.add_parent(@martin_sheen)
21
57
  @martin_sheen.children.must_include @charlie_sheen
@@ -25,10 +61,6 @@ describe Node do
25
61
  @martin_sheen.children.must_include @emilio_estevez
26
62
  end
27
63
 
28
- it "must not respond to :parent" do
29
- @martin_sheen.respond_to?(:parent).must_equal false
30
- end
31
-
32
64
  it "must create children from scalars" do
33
65
  @martin_sheen.new_child 'fake_emilio'
34
66
  @martin_sheen.children.size.must_equal 1
@@ -51,6 +83,25 @@ describe ChildNode do
51
83
  }
52
84
  end
53
85
 
86
+ it "must track parent and children" do
87
+ @charlie_sheen.set_parent(0, @martin_sheen)
88
+ @charlie_sheen.parent.must_equal @martin_sheen
89
+ @martin_sheen.children.must_include @charlie_sheen
90
+
91
+ @martin_sheen.children.wont_include @emilio_estevez
92
+ @martin_sheen.set_child(0, @emilio_estevez)
93
+ @martin_sheen.children.must_include @emilio_estevez
94
+ @emilio_estevez.parent.must_equal @martin_sheen
95
+ end
96
+ end
97
+
98
+ describe ChildFlexNode do
99
+ before do
100
+ @martin_sheen = ChildFlexNode.new 'martin'
101
+ @charlie_sheen = ChildFlexNode.new 'charlie'
102
+ @emilio_estevez = ChildFlexNode.new 'emilio'
103
+ end
104
+
54
105
  it "must track parent and children" do
55
106
  @charlie_sheen.add_parent(@martin_sheen)
56
107
  @charlie_sheen.parent.must_equal @martin_sheen