feldtruby 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +23 -0
- data/.gemtest +0 -0
- data/History.txt +4 -0
- data/Manifest.txt +44 -0
- data/README.md +63 -0
- data/README.txt +59 -0
- data/Rakefile +19 -0
- data/TODO +6 -0
- data/lib/feldtruby/array/basic_stats.rb +88 -0
- data/lib/feldtruby/array/count_by.rb +7 -0
- data/lib/feldtruby/array.rb +34 -0
- data/lib/feldtruby/file/file_change_watcher.rb +88 -0
- data/lib/feldtruby/file/tempfile.rb +25 -0
- data/lib/feldtruby/float.rb +17 -0
- data/lib/feldtruby/math/rand.rb +5 -0
- data/lib/feldtruby/net/html_doc_getter.rb +31 -0
- data/lib/feldtruby/optimize/differential_evolution.rb +186 -0
- data/lib/feldtruby/optimize/max_steps_termination_criterion.rb +24 -0
- data/lib/feldtruby/optimize/objective.rb +302 -0
- data/lib/feldtruby/optimize/optimizer.rb +145 -0
- data/lib/feldtruby/optimize/random_search.rb +9 -0
- data/lib/feldtruby/optimize/search_space.rb +69 -0
- data/lib/feldtruby/optimize/stdout_logger.rb +138 -0
- data/lib/feldtruby/optimize.rb +28 -0
- data/lib/feldtruby/string/to_iso.rb +7 -0
- data/lib/feldtruby/time.rb +22 -0
- data/lib/feldtruby/vector.rb +14 -0
- data/lib/feldtruby/visualization/circos.rb +25 -0
- data/lib/feldtruby/word_counter.rb +100 -0
- data/lib/feldtruby.rb +6 -0
- data/test/helper.rb +7 -0
- data/test/test_array.rb +71 -0
- data/test/test_array_basic_stats.rb +130 -0
- data/test/test_array_count_by.rb +13 -0
- data/test/test_float.rb +20 -0
- data/test/test_html_doc_getter.rb +16 -0
- data/test/test_optimize.rb +55 -0
- data/test/test_optimize_differential_evolution.rb +42 -0
- data/test/test_optimize_objective.rb +157 -0
- data/test/test_optimize_populationbasedoptimizer.rb +24 -0
- data/test/test_optimize_random_search.rb +46 -0
- data/test/test_optimize_search_space.rb +97 -0
- data/test/test_time.rb +27 -0
- data/test/test_vector.rb +98 -0
- data/test/test_word_counter.rb +57 -0
- metadata +149 -0
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'feldtruby/optimize'
|
2
|
+
|
3
|
+
class TestOptimize < MiniTest::Unit::TestCase
|
4
|
+
def test_rosenbrock_optimization_as_in_README
|
5
|
+
xbest, ybest = FeldtRuby::Optimize.optimize(0, 2, {:verbose => false}) {|x, y|
|
6
|
+
(1 - x)**2 + 100*(y - x*x)**2
|
7
|
+
}
|
8
|
+
assert_in_delta 1.0, xbest
|
9
|
+
assert_in_delta 1.0, ybest
|
10
|
+
end
|
11
|
+
|
12
|
+
def in_vicinity?(x, y, delta = 0.01)
|
13
|
+
(x-y).abs < delta
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_himmelsblau_minimization
|
17
|
+
# For details see: http://en.wikipedia.org/wiki/Himmelblau%27s_function
|
18
|
+
xbest, ybest = FeldtRuby::Optimize.minimize(-5, 5, {:maxNumSteps => 5000, :verbose => false}) {|x, y|
|
19
|
+
(x*x + y - 11)**2 + (x + y*y + - 7)**2
|
20
|
+
}
|
21
|
+
|
22
|
+
# There are 4 local minima:
|
23
|
+
# f( 3.000000, 2.000000) = 0.0
|
24
|
+
# f(-2.805118, 3.131312) = 0.0
|
25
|
+
# f(-3.779310, -3.283186) = 0.0
|
26
|
+
# f( 3.584428, -1.848126) = 0.0
|
27
|
+
# and it is unlikely that we are not in the vicinity of one of those after optimization.
|
28
|
+
|
29
|
+
if in_vicinity?(xbest, 3.000000)
|
30
|
+
assert_in_delta 3.000000, xbest, 0.1
|
31
|
+
assert_in_delta 2.000000, ybest, 0.1
|
32
|
+
elsif in_vicinity?(xbest, -2.805118)
|
33
|
+
assert_in_delta -2.805118, xbest, 0.1
|
34
|
+
assert_in_delta 3.131312, ybest, 0.1
|
35
|
+
elsif in_vicinity?(xbest, -3.779310)
|
36
|
+
assert_in_delta -3.779310, xbest, 0.1
|
37
|
+
assert_in_delta -3.283186, ybest, 0.1
|
38
|
+
elsif in_vicinity?(xbest, 3.584428)
|
39
|
+
assert_in_delta 3.584428, xbest, 0.1
|
40
|
+
assert_in_delta -1.848126, ybest, 0.1
|
41
|
+
else
|
42
|
+
assert false, "Solution [#{xbest}, #{ybest}] is not close to any minima"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_himmelsblau_maximization
|
47
|
+
# There is a local maxima that can be found if we search in a smaller box around 0.0.
|
48
|
+
# For details see: http://en.wikipedia.org/wiki/Himmelblau%27s_function
|
49
|
+
xbest, ybest = FeldtRuby::Optimize.maximize(-1, 1, {:maxNumSteps => 2000, :verbose => false}) {|x, y|
|
50
|
+
(x*x + y - 11)**2 + (x + y*y + - 7)**2
|
51
|
+
}
|
52
|
+
assert_in_delta -0.270845, xbest, 0.1
|
53
|
+
assert_in_delta -0.923039, ybest, 0.1
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'feldtruby/optimize/differential_evolution'
|
2
|
+
require 'feldtruby/array/basic_stats'
|
3
|
+
|
4
|
+
class MinimizeRMS < FeldtRuby::Optimize::Objective
|
5
|
+
def objective_min_rms(candidate)
|
6
|
+
candidate.rms
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class MinimizeRMSAndSum < MinimizeRMS
|
11
|
+
def objective_min_sum(candidate)
|
12
|
+
candidate.sum.abs
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class TestDifferentialEvolution < MiniTest::Unit::TestCase
|
17
|
+
include FeldtRuby::Optimize
|
18
|
+
def setup
|
19
|
+
@s2 = SearchSpace.new_symmetric(2, 1)
|
20
|
+
@s4 = SearchSpace.new_symmetric(4, 1)
|
21
|
+
|
22
|
+
@o1 = MinimizeRMS.new
|
23
|
+
@o2 = MinimizeRMSAndSum.new
|
24
|
+
|
25
|
+
@de1 = DEOptimizer.new(@o1, @s2, {:verbose => false, :maxNumSteps => 1000})
|
26
|
+
@de2 = DEOptimizer.new(@o2, @s4, {:verbose => false, :maxNumSteps => 1234})
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_de_for_small_vector_with_rms
|
30
|
+
@de1.optimize()
|
31
|
+
# Very unlikely we get a number over 0.30 (2 elements) after 1000 steps...
|
32
|
+
assert @de1.best.sum <= 0.40
|
33
|
+
assert_equal 1000, @de1.num_optimization_steps
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_de_for_small_vector_with_rms_and_sum_for_more_steps
|
37
|
+
@de2.optimize()
|
38
|
+
# Very unlikely we get a number over 0.40 (4 elements)...
|
39
|
+
assert @de2.best.sum <= 0.40
|
40
|
+
assert_equal 1234, @de2.num_optimization_steps
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'feldtruby/optimize/objective'
|
2
|
+
require 'feldtruby/array'
|
3
|
+
|
4
|
+
require 'pp'
|
5
|
+
|
6
|
+
class SingleObjective1 < FeldtRuby::Optimize::Objective
|
7
|
+
# Sum of candidate vector of values should be small
|
8
|
+
def objective_min_small_sum(candidate)
|
9
|
+
candidate.sum
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class TestSingleObjective < MiniTest::Unit::TestCase
|
14
|
+
def setup
|
15
|
+
@o = SingleObjective1.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_has_one_aspect
|
19
|
+
assert_equal 1, @o.num_aspects
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_quality_value
|
23
|
+
assert_equal 1, @o.quality_value([1])
|
24
|
+
assert_equal 3, @o.quality_value([1, 2])
|
25
|
+
assert_equal( -42, @o.quality_value([1, 2, -45]) )
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class TwoMinObjectives1 < FeldtRuby::Optimize::Objective
|
30
|
+
def objective_min_distance_between(candidate)
|
31
|
+
candidate.distance_between_elements.sum
|
32
|
+
end
|
33
|
+
def objective_min_sum(candidate)
|
34
|
+
candidate.sum
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class TestTwoObjectives < MiniTest::Unit::TestCase
|
39
|
+
def setup
|
40
|
+
@o = TwoMinObjectives1.new
|
41
|
+
end
|
42
|
+
def test_has_two_aspects
|
43
|
+
assert_equal 2, @o.num_aspects
|
44
|
+
end
|
45
|
+
def test_global_min_values_per_aspect
|
46
|
+
assert_equal [Float::INFINITY, Float::INFINITY], @o.global_min_values_per_aspect
|
47
|
+
end
|
48
|
+
def test_global_max_values_per_aspect
|
49
|
+
assert_equal [-Float::INFINITY, -Float::INFINITY], @o.global_max_values_per_aspect
|
50
|
+
end
|
51
|
+
def test_update_global_mins_and_maxs
|
52
|
+
@o.update_global_mins_and_maxs([1,2])
|
53
|
+
assert_equal [1,2], @o.global_min_values_per_aspect
|
54
|
+
assert_equal [1,2], @o.global_max_values_per_aspect
|
55
|
+
|
56
|
+
@o.update_global_mins_and_maxs([1,3])
|
57
|
+
assert_equal [1,2], @o.global_min_values_per_aspect
|
58
|
+
assert_equal [1,3], @o.global_max_values_per_aspect
|
59
|
+
|
60
|
+
@o.update_global_mins_and_maxs([0,8])
|
61
|
+
assert_equal [0,2], @o.global_min_values_per_aspect
|
62
|
+
assert_equal [1,8], @o.global_max_values_per_aspect
|
63
|
+
end
|
64
|
+
def test_sub_objective_values
|
65
|
+
assert_equal [1,3], @o.sub_objective_values([1,2])
|
66
|
+
assert_equal [3,7], @o.sub_objective_values([1,2,4])
|
67
|
+
assert_equal [4,8], @o.sub_objective_values([1,2,5])
|
68
|
+
end
|
69
|
+
def test_qv_mwgr
|
70
|
+
@o.update_global_mins_and_maxs([0, 0])
|
71
|
+
@o.update_global_mins_and_maxs([1, 3])
|
72
|
+
assert_equal 0.0, @o.qv_mwgr([1,2])
|
73
|
+
assert_equal 1.0, @o.qv_mwgr([0,0])
|
74
|
+
end
|
75
|
+
def test_qv_mwgr_complex
|
76
|
+
# Set first values => fitness is always zero
|
77
|
+
assert_equal 0.0, @o.qv_mwgr([1,2,3])
|
78
|
+
# Now we come with a worse candidate => still zero
|
79
|
+
assert_equal 0.0, @o.qv_mwgr([1,2,5])
|
80
|
+
# But now the previous value is the best candidate we have seen so gets maximum quality value, 2 aspects * 1.0 per aspect
|
81
|
+
assert_equal 1.0, @o.qv_mwgr([1,2,3])
|
82
|
+
# The previous worst is still the worst
|
83
|
+
assert_equal 0.0, @o.qv_mwgr([1,2,5])
|
84
|
+
# And now some complex ones that are between the prev best and worst
|
85
|
+
assert_equal( ((4.0 - 3.0)/(4-2) + (8.0 - 7)/(8-6))/2, @o.qv_mwgr([1,2,4]) )
|
86
|
+
assert_equal( ((4.0 - 3.5)/(4-2) + (8.0 - 7.5)/(8-6))/2, @o.qv_mwgr([1,2,4.5]) )
|
87
|
+
# Now extend the global best with a new best
|
88
|
+
assert_equal 1.0, @o.qv_mwgr([1,2,2]) # new global min = [1, 5] and max the same at [4, 8]
|
89
|
+
# And the in between candidates now have new values based on the new mins
|
90
|
+
assert_equal( ((4.0 - 3.0)/(4-1) + (8.0 - 7)/(8-5))/2, @o.qv_mwgr([1,2,4]) )
|
91
|
+
assert_equal( ((4.0 - 3.5)/(4-1) + (8.0 - 7.5)/(8-5))/2, @o.qv_mwgr([1,2,4.5]) )
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "Objective" do
|
96
|
+
before do
|
97
|
+
@o = SingleObjective1.new
|
98
|
+
@o2 = TwoMinObjectives1.new
|
99
|
+
@c = [1,2,3]
|
100
|
+
end
|
101
|
+
|
102
|
+
it "attaches quality value to an evaluated object" do
|
103
|
+
qv = @o.quality_value(@c)
|
104
|
+
@c._quality_value.must_equal qv
|
105
|
+
@c._objective.must_equal @o
|
106
|
+
end
|
107
|
+
|
108
|
+
it "overwrites quality value if evaluated again with another objective" do
|
109
|
+
@o.quality_value(@c)
|
110
|
+
qv2 = @o2.quality_value(@c)
|
111
|
+
@c._quality_value.must_equal qv2
|
112
|
+
@c._objective.must_equal @o2
|
113
|
+
end
|
114
|
+
|
115
|
+
it "is re-evaluated if the objective has changed since original evaluation" do
|
116
|
+
qv = @o2.quality_value(@c)
|
117
|
+
@o2.quality_value([1,2,3,4,5]) # Higher sum so max updated
|
118
|
+
qvnew = @c._quality_value
|
119
|
+
qvnew.wont_equal qv
|
120
|
+
end
|
121
|
+
|
122
|
+
describe "objects that have not been evaluated" do
|
123
|
+
it "has not attached quality values" do
|
124
|
+
c = [1,2,3]
|
125
|
+
c._quality_value.must_equal nil
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
describe "version numbers" do
|
130
|
+
it "has version number 0 when no evaluation has taken place" do
|
131
|
+
@o.current_version.must_equal 0
|
132
|
+
@o2.current_version.must_equal 0
|
133
|
+
end
|
134
|
+
|
135
|
+
it "never changes the version number for a single objective since ratios are not used" do
|
136
|
+
@o.quality_value([1])
|
137
|
+
@o.current_version.must_equal 0
|
138
|
+
end
|
139
|
+
|
140
|
+
it "increases the version number each time a quality aspect of a candidate is more extreme than previously seen (when multi-objective)" do
|
141
|
+
@o2.quality_value([1])
|
142
|
+
@o2.current_version.must_equal 4 # Both min and max changed for two objectives => 2*2
|
143
|
+
@o2.quality_value([2])
|
144
|
+
@o2.current_version.must_equal 5 # New max values for sum objective => +1
|
145
|
+
@o2.quality_value([1,2])
|
146
|
+
@o2.current_version.must_equal 7 # New max values for both objectives => +2
|
147
|
+
@o2.quality_value([0])
|
148
|
+
@o2.current_version.must_equal 8 # New min value for sum objective => +1
|
149
|
+
@o2.quality_value([-1])
|
150
|
+
@o2.current_version.must_equal 9 # New min value for sum objective => +1
|
151
|
+
@o2.quality_value([-2])
|
152
|
+
@o2.current_version.must_equal 10 # New min value for sum objective => +1
|
153
|
+
@o2.quality_value([1,2,3])
|
154
|
+
@o2.current_version.must_equal 12 # New max for both objectives => +1
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'feldtruby/optimize/optimizer'
|
2
|
+
|
3
|
+
class TestPopulationBasedOptimizer < MiniTest::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@o1 = MinimizeRMS.new
|
6
|
+
@pbo1 = FeldtRuby::Optimize::PopulationBasedOptimizer.new(@o1)
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_population_size
|
10
|
+
assert_equal 100, @pbo1.population_size
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_sample_population_indices_without_replacement
|
14
|
+
100.times do
|
15
|
+
num_samples = rand_int(@pbo1.population_size)
|
16
|
+
sampled_indices = @pbo1.sample_population_indices_without_replacement(num_samples)
|
17
|
+
assert_equal num_samples, sampled_indices.length
|
18
|
+
assert_equal num_samples, sampled_indices.uniq.length, "Some elements where the same in #{sampled_indices.inspect}"
|
19
|
+
sampled_indices.each do |i|
|
20
|
+
assert i >= 0 && i < @pbo1.population_size
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'feldtruby/optimize/random_search'
|
2
|
+
require 'feldtruby/array/basic_stats'
|
3
|
+
|
4
|
+
|
5
|
+
unless defined?(MinimizeRMS)
|
6
|
+
class MinimizeRMS < FeldtRuby::Optimize::Objective
|
7
|
+
def objective_min_rms(candidate)
|
8
|
+
candidate.rms
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
unless defined?(MinimizeRMSAndSum)
|
14
|
+
class MinimizeRMSAndSum < MinimizeRMS
|
15
|
+
def objective_min_sum(candidate)
|
16
|
+
candidate.sum.abs
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class TestRandomSearcher < MiniTest::Unit::TestCase
|
22
|
+
def setup
|
23
|
+
@s2 = FeldtRuby::Optimize::SearchSpace.new_symmetric(2, 1)
|
24
|
+
@s4 = FeldtRuby::Optimize::SearchSpace.new_symmetric(4, 1)
|
25
|
+
|
26
|
+
@o1 = MinimizeRMS.new
|
27
|
+
@o2 = MinimizeRMSAndSum.new
|
28
|
+
|
29
|
+
@rs1 = FeldtRuby::Optimize::RandomSearcher.new(@o1, @s2, {:verbose => false, :maxNumSteps => 1000})
|
30
|
+
@rs2 = FeldtRuby::Optimize::RandomSearcher.new(@o2, @s4, {:verbose => false, :maxNumSteps => 2187})
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_random_search_for_small_vector_with_rms
|
34
|
+
@rs1.optimize()
|
35
|
+
# Very unlikely we get a number over 0.40 (2 elements) after 1000 steps...
|
36
|
+
assert @rs1.best.sum <= 0.40
|
37
|
+
assert_equal 1000, @rs1.num_optimization_steps
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_random_search_for_small_vector_with_rms_and_sum_for_more_steps
|
41
|
+
@rs2.optimize()
|
42
|
+
# Very unlikely we get a number over 0.40 (3 elements)...
|
43
|
+
assert @rs2.best.sum <= 0.60
|
44
|
+
assert_equal 2187, @rs2.num_optimization_steps
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'minitest/spec'
|
2
|
+
require 'feldtruby/optimize/search_space'
|
3
|
+
|
4
|
+
describe "SearchSpace#bound" do
|
5
|
+
before do
|
6
|
+
@sp = FeldtRuby::Optimize::SearchSpace.new([-5, -3], [5, 7])
|
7
|
+
end
|
8
|
+
|
9
|
+
it "returns the values if they are INSIDE the search space boundaries" do
|
10
|
+
@sp.bound([-1, 0]).must_equal [-1, 0]
|
11
|
+
end
|
12
|
+
|
13
|
+
it "returns the values if they are ON the search space boundaries" do
|
14
|
+
@sp.bound([-5, -3]).must_equal [-5, -3]
|
15
|
+
@sp.bound([5, 7]).must_equal [5, 7]
|
16
|
+
@sp.bound([-5, 7]).must_equal [-5, 7]
|
17
|
+
@sp.bound([5, -3]).must_equal [5, -3]
|
18
|
+
end
|
19
|
+
|
20
|
+
it "generates a value INSIDE the search space boundaries when a value is given that is outside (negative, outside on one dimension)" do
|
21
|
+
l, h = @sp.bound([-10, 3.4])
|
22
|
+
h.must_equal 3.4
|
23
|
+
l.must_be :>=, -5
|
24
|
+
l.must_be :<=, 5
|
25
|
+
|
26
|
+
l, h = @sp.bound([-4.6, -4.1])
|
27
|
+
l.must_equal(-4.6)
|
28
|
+
h.must_be :>=, -3
|
29
|
+
h.must_be :<=, 7
|
30
|
+
end
|
31
|
+
|
32
|
+
it "generates a value INSIDE the search space boundaries when a value is given that is outside (positive, outside on one dimension)" do
|
33
|
+
l, h = @sp.bound([6, 2.7])
|
34
|
+
h.must_equal 2.7
|
35
|
+
l.must_be :>=, -5
|
36
|
+
l.must_be :<=, 5
|
37
|
+
|
38
|
+
l, h = @sp.bound([-4.6, 8.4])
|
39
|
+
l.must_equal(-4.6)
|
40
|
+
h.must_be :>=, -3
|
41
|
+
h.must_be :<=, 7
|
42
|
+
end
|
43
|
+
|
44
|
+
it "generates a value INSIDE the search space boundaries when a value is given that is outside (positive, outside on one dimension)" do
|
45
|
+
l, h = @sp.bound([-60.2, 1])
|
46
|
+
l.must_be :>=, -5
|
47
|
+
l.must_be :<=, 5
|
48
|
+
h.must_be :>=, -3
|
49
|
+
h.must_be :<=, 7
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class TestSearchSpace < MiniTest::Unit::TestCase
|
54
|
+
def setup
|
55
|
+
@s1 = FeldtRuby::Optimize::SearchSpace.new([-5], [5])
|
56
|
+
@s2 = FeldtRuby::Optimize::SearchSpace.new([-1, -1], [1, 1])
|
57
|
+
@s3 = FeldtRuby::Optimize::SearchSpace.new([-1, -5, -100], [1, 50, 1000])
|
58
|
+
@s4 = FeldtRuby::Optimize::SearchSpace.new_symmetric(4, 10)
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_num_variables
|
62
|
+
assert_equal 1, @s1.num_variables
|
63
|
+
assert_equal 2, @s2.num_variables
|
64
|
+
assert_equal 3, @s3.num_variables
|
65
|
+
assert_equal 4, @s4.num_variables
|
66
|
+
end
|
67
|
+
|
68
|
+
def assert_gen_candidate_and_is_candidate(ss, numRepetitions = 100)
|
69
|
+
numRepetitions.times do
|
70
|
+
c = ss.gen_candidate()
|
71
|
+
assert_equal ss.num_variables, c.length
|
72
|
+
c.length.times do |i|
|
73
|
+
assert ss.min_values[i] <= c[i]
|
74
|
+
assert ss.max_values[i] >= c[i]
|
75
|
+
end
|
76
|
+
assert ss.is_candidate?(c)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_gen_candidate_and_is_candidate
|
81
|
+
assert_gen_candidate_and_is_candidate(@s1)
|
82
|
+
assert_gen_candidate_and_is_candidate(@s2)
|
83
|
+
assert_gen_candidate_and_is_candidate(@s3)
|
84
|
+
assert_gen_candidate_and_is_candidate(@s4)
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_new_from_min_max
|
88
|
+
ss = FeldtRuby::Optimize::SearchSpace.new_from_min_max(2, -7, 2)
|
89
|
+
assert_equal 2, ss.num_variables
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_bound_returns_vector_if_supplied_a_vector
|
93
|
+
s1 = FeldtRuby::Optimize::SearchSpace.new([-5, -3], [5, 7])
|
94
|
+
b = s1.bound(Vector[-10, 5])
|
95
|
+
assert Vector, b.class
|
96
|
+
end
|
97
|
+
end
|
data/test/test_time.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'feldtruby/time'
|
2
|
+
|
3
|
+
class TestFeldtRubyTime < MiniTest::Unit::TestCase
|
4
|
+
def test_timestamp_short
|
5
|
+
str = Time.timestamp({:short => true})
|
6
|
+
assert_equal 15, str.length
|
7
|
+
assert str =~ /^\d{6} \d{2}:\d{2}\.\d{2}/
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_timestamp_long
|
11
|
+
str = Time.timestamp()
|
12
|
+
assert_equal 17, str.length
|
13
|
+
assert str =~ /^\d{8} \d{2}:\d{2}\.\d{2}/
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_human_readable_timestr
|
17
|
+
assert_equal "74.92usec", Time.human_readable_timestr(0.000074916)
|
18
|
+
assert_equal "0.75msec", Time.human_readable_timestr(0.00074916)
|
19
|
+
assert_equal "7.49msec", Time.human_readable_timestr(0.0074916)
|
20
|
+
assert_equal "74.92msec", Time.human_readable_timestr(0.074916)
|
21
|
+
assert_equal "0.75sec", Time.human_readable_timestr(0.74916)
|
22
|
+
assert_equal "7.49sec", Time.human_readable_timestr(7.4916)
|
23
|
+
assert_equal "1.25mins", Time.human_readable_timestr(74.916)
|
24
|
+
assert_equal "12.49mins", Time.human_readable_timestr(749.16)
|
25
|
+
assert_equal "2.08hours", Time.human_readable_timestr(7491.6)
|
26
|
+
end
|
27
|
+
end
|
data/test/test_vector.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'feldtruby/vector'
|
3
|
+
|
4
|
+
class TestVectorBasicStats < MiniTest::Unit::TestCase
|
5
|
+
def test_sum_normal
|
6
|
+
assert_equal 3, Vector[1,2].sum
|
7
|
+
assert_equal 6, Vector[1,2,3].sum
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_sum_one_element
|
11
|
+
assert_equal 1, Vector[1].sum
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_sum_empty_array
|
15
|
+
assert_equal 0, Vector[].sum
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_mean_normal
|
19
|
+
assert_equal 1.5, Vector[1,2].mean
|
20
|
+
assert_equal 2, Vector[1,2,3].mean
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_mean_one_element
|
24
|
+
assert_equal 1, Vector[1].mean
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_mean_empty_array
|
28
|
+
assert_equal 0, Vector[].mean
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_mean_from_wikipedia_def_page_for_stdev
|
32
|
+
assert_equal 2.0, Vector[2, 4, 4, 4, 5, 5, 7, 9].stdev
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_root_mean_square
|
36
|
+
assert_equal Math.sqrt((1*1 + 2*2)/2.0), Vector[1, 2].root_mean_square
|
37
|
+
assert_equal Math.sqrt((10*10 + 243*243)/2.0), Vector[10, 243].rms
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_weighted_sum_one_element
|
41
|
+
assert_equal 1, Vector[1].weighted_sum([1])
|
42
|
+
assert_equal 2, Vector[1].weighted_sum([2])
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_weighted_sum_two_elements
|
46
|
+
assert_equal 3, Vector[1, 2].weighted_sum([1, 1])
|
47
|
+
assert_equal 22, Vector[1, 4].weighted_sum([2, 5])
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_weighted_mean_one_elements
|
51
|
+
assert_equal 1, Vector[1].weighted_mean([1])
|
52
|
+
assert_equal 4, Vector[4].weighted_mean([2])
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_weighted_mean_two_elements
|
56
|
+
assert_equal 1.5, Vector[1, 2].weighted_mean([1, 1])
|
57
|
+
assert_equal 22.0/7, Vector[1, 4].weighted_mean([2, 5])
|
58
|
+
|
59
|
+
assert_equal 1.5, Vector[1, 2].weighted_mean()
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe Vector do
|
64
|
+
describe "Slicing a vector" do
|
65
|
+
before do
|
66
|
+
@v = Vector[1,2,3,4,5]
|
67
|
+
end
|
68
|
+
|
69
|
+
it "does not mess up normal indexing" do
|
70
|
+
@v[0].must_equal 1
|
71
|
+
@v[1].must_equal 2
|
72
|
+
@v[2].must_equal 3
|
73
|
+
@v[3].must_equal 4
|
74
|
+
@v[4].must_equal 5
|
75
|
+
end
|
76
|
+
|
77
|
+
it "works in the middle of a vector" do
|
78
|
+
@v[1,1].must_equal Vector[2]
|
79
|
+
@v[1,2].must_equal Vector[2,3]
|
80
|
+
@v[2,3].must_equal Vector[3,4,5]
|
81
|
+
end
|
82
|
+
|
83
|
+
it "works at the start of a vector" do
|
84
|
+
@v[0,1].must_equal Vector[1]
|
85
|
+
@v[0,2].must_equal Vector[1,2]
|
86
|
+
@v[0,5].must_equal Vector[1,2,3,4,5]
|
87
|
+
end
|
88
|
+
|
89
|
+
it "works at the end of a vector" do
|
90
|
+
@v[4,1].must_equal Vector[5]
|
91
|
+
end
|
92
|
+
|
93
|
+
it "works even if goes past the vector" do
|
94
|
+
@v[4,2].must_equal Vector[5]
|
95
|
+
@v[4,10].must_equal Vector[5]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'minitest/spec'
|
2
|
+
require 'feldtruby/word_counter'
|
3
|
+
|
4
|
+
describe "WordCounter" do
|
5
|
+
it "can count words" do
|
6
|
+
wc = FeldtRuby::WordCounter.new
|
7
|
+
wc.count_words "The fox likes running. The fox likes it. It feels good for the fox."
|
8
|
+
wc.words.sort.must_equal ["feels", "fox", "good", "likes", "running"]
|
9
|
+
wc.count("feels").must_equal 1
|
10
|
+
wc.count("fox").must_equal 3
|
11
|
+
wc.count("good").must_equal 1
|
12
|
+
wc.count("likes").must_equal 2
|
13
|
+
wc.count("running").must_equal 1
|
14
|
+
|
15
|
+
wc.count("notinthere").must_equal 0
|
16
|
+
end
|
17
|
+
|
18
|
+
it "can return a top list of most common words" do
|
19
|
+
wc = FeldtRuby::WordCounter.new
|
20
|
+
wc.count_words "The fox likes running. The fox likes it. It feels good for the fox."
|
21
|
+
t = wc.top_words(1)
|
22
|
+
t.must_be_instance_of Array
|
23
|
+
t.must_equal [["fox", 3]]
|
24
|
+
wc.top_words(2).must_equal [["fox", 3], ["likes", 2]]
|
25
|
+
end
|
26
|
+
|
27
|
+
it "can merge words that are very close to each other (singularis/pluralis/-ing)" do
|
28
|
+
wc = FeldtRuby::WordCounter.new
|
29
|
+
wc.count_words "test tests testing testing program programs programs design"
|
30
|
+
wc.merge!
|
31
|
+
|
32
|
+
wc.count("test|tests|testing").must_equal 4
|
33
|
+
wc.count("program|programs").must_equal 3
|
34
|
+
wc.count("design").must_equal 1
|
35
|
+
end
|
36
|
+
|
37
|
+
it "has merged word descriptions in the top list" do
|
38
|
+
wc = FeldtRuby::WordCounter.new
|
39
|
+
wc.count_words "test tests testing testing program programs programs"
|
40
|
+
wc.merge!
|
41
|
+
|
42
|
+
wc.top_words(2).must_equal [["test|tests|testing", 4], ["program|programs", 3]]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "NgramWordCounter" do
|
47
|
+
it "counts all 2-grams" do
|
48
|
+
wc = FeldtRuby::NgramWordCounter.new(2)
|
49
|
+
wc.count_words "The fox likes running. The fox likes it. It feels good for the fox."
|
50
|
+
|
51
|
+
wc.words.sort.must_equal ["fox likes", "likes running", "feels good"].sort
|
52
|
+
|
53
|
+
wc.count("fox likes").must_equal 2
|
54
|
+
wc.count("likes running").must_equal 1
|
55
|
+
wc.count("feels good").must_equal 1
|
56
|
+
end
|
57
|
+
end
|