feldtruby 0.2.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.
- 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
|