biopsy 0.1.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
data/test/helper.rb ADDED
@@ -0,0 +1,187 @@
1
+ require 'simplecov'
2
+ require 'coveralls'
3
+
4
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
5
+ SimpleCov::Formatter::HTMLFormatter,
6
+ Coveralls::SimpleCov::Formatter
7
+ ]
8
+ SimpleCov.start
9
+
10
+ require 'test/unit'
11
+ begin; require 'turn/autorun'; rescue LoadError; end
12
+ require 'shoulda-context'
13
+ require 'biopsy'
14
+
15
+ Turn.config.format = :pretty
16
+ Turn.config.trace = 10
17
+
18
+ Biopsy::Settings.instance.set_defaults
19
+
20
+ # Helper class provides methods for setting up test data.
21
+ class Helper
22
+
23
+ require 'fileutils'
24
+
25
+ attr_reader :tmp_dir
26
+ attr_reader :target_dir
27
+ attr_reader :domain_dir
28
+ attr_reader :domain_path
29
+ attr_reader :target_path
30
+ attr_reader :objective_dir
31
+ attr_reader :objective_path
32
+
33
+ def cleanup
34
+ self.instance_variables.each do |ivar|
35
+ if ivar =~ /_dir/
36
+ dir = self.instance_variable_get(ivar)
37
+ FileUtils.rm_rf dir if File.exists? dir
38
+ end
39
+ end
40
+ end
41
+
42
+ # Create a tmp directory for test data
43
+ def setup_tmp_dir
44
+ @tmp_dir = File.expand_path('.tmp')
45
+ Dir.mkdir @tmp_dir
46
+ end
47
+
48
+ # Return a hash of valid target data
49
+ def target_data
50
+ {
51
+ :input_files => {
52
+ :in => 'input.txt'
53
+ },
54
+ :output_files => {
55
+ :params => 'output.txt'
56
+ },
57
+ :parameter_ranges => {
58
+ :a => (-40..40).step(2).to_a,
59
+ :b => (0..100).step(2).to_a,
60
+ :c => (-50..50).to_a
61
+ },
62
+ :constructor_path => 'test_constructor.rb'
63
+ }
64
+ end
65
+
66
+ # Setup the directory for target
67
+ def setup_target
68
+ @target_dir = File.join(@tmp_dir, 'targets')
69
+ Dir.mkdir @target_dir
70
+ Biopsy::Settings.instance.target_dir = [@target_dir]
71
+ end
72
+
73
+ # Create a valid target definition in the target dir
74
+ def create_valid_target
75
+ data = self.target_data
76
+ name = 'test_target'
77
+ @target_path = File.join(@target_dir, name + '.yml')
78
+ self.yaml_dump data, @target_path
79
+ File.open(File.join(@target_dir, data[:constructor_path]), 'w') do |f|
80
+ f.puts %Q{
81
+ class TestConstructor
82
+
83
+ require 'yaml'
84
+
85
+ def run(params)
86
+ File.open('output.txt', 'w') do |f|
87
+ f.puts(params.to_yaml)
88
+ end
89
+ { :params => File.expand_path('output.txt') }
90
+ end
91
+
92
+ end
93
+ }
94
+ end
95
+ name
96
+ end
97
+
98
+ # Return a hash of valid domain data
99
+ def domain_data
100
+ {
101
+ :input_filetypes => [
102
+ {
103
+ :n => 1,
104
+ :allowed_extensions => [
105
+ '.txt'
106
+ ]
107
+ }
108
+ ],
109
+ :output_filetypes => [
110
+ {
111
+ :n => 1,
112
+ :allowed_extensions => [
113
+ '.txt'
114
+ ]
115
+ }
116
+ ],
117
+ :objectives => [
118
+ 'test1', 'test2'
119
+ ]
120
+ }
121
+ end
122
+
123
+ def setup_domain
124
+ @domain_dir = File.join(@tmp_dir, 'domains')
125
+ Dir.mkdir @domain_dir
126
+ Biopsy::Settings.instance.domain_dir = [@domain_dir]
127
+ end
128
+
129
+ def create_valid_domain
130
+ data = domain_data
131
+ name = 'test_domain'
132
+ @domain_path = File.join(@domain_dir, name + '.yml')
133
+ self.yaml_dump data, @domain_path
134
+ name
135
+ end
136
+
137
+ def setup_objective
138
+ @objective_dir = File.join(@tmp_dir, 'objectives')
139
+ Dir.mkdir @objective_dir
140
+ Biopsy::Settings.instance.objectives_dir = [@objective_dir]
141
+ end
142
+
143
+ def create_valid_objective
144
+ objective = %Q{
145
+ class TestObjective < Biopsy::ObjectiveFunction
146
+
147
+ require 'yaml'
148
+
149
+ def initialize
150
+ @optimum = 0
151
+ @max = 0
152
+ @weighting = 1
153
+ end
154
+
155
+ def run(input, threads)
156
+ file = input[:params]
157
+ input = YAML::load_file(file)
158
+ a = input[:a].to_i
159
+ b = input[:b].to_i
160
+ c = input[:c].to_i
161
+ value = - Math.sqrt((a-4)**2) - Math.sqrt((b-4)**2) - Math.sqrt((c-4)**2)
162
+ {
163
+ :optimum => @optimum,
164
+ :max => @max,
165
+ :weighting => @weighting,
166
+ :result => value
167
+ }
168
+ end
169
+ end
170
+ }
171
+ @objective_path = File.join(@objective_dir, 'test_objective.rb')
172
+ self.string_dump objective, @objective_path
173
+ end
174
+
175
+ # Dump +:object+ as YAML to +:file+
176
+ def yaml_dump object, file
177
+ self.string_dump object.to_yaml, file
178
+ end
179
+
180
+ # Dump +:string+ to +:file+
181
+ def string_dump string, file
182
+ File.open(file, 'w') do |f|
183
+ f.puts string
184
+ end
185
+ end
186
+
187
+ end
@@ -0,0 +1,61 @@
1
+ require 'helper'
2
+
3
+ class TestDomain < Test::Unit::TestCase
4
+
5
+ require 'fileutils'
6
+
7
+ context "Domain" do
8
+
9
+ setup do
10
+ @h = Helper.new
11
+ @h.setup_tmp_dir
12
+
13
+ # we need a domain
14
+ @h.setup_domain
15
+ domain_name = @h.create_valid_domain
16
+ @domain = Biopsy::Domain.new domain_name
17
+ end
18
+
19
+ teardown do
20
+ @h.cleanup
21
+ end
22
+
23
+ should "be able to find the current domain" do
24
+ assert_equal 'test_domain', @domain.get_current_domain
25
+ end
26
+
27
+ should "be able to find a definition" do
28
+ assert_equal @h.domain_path, @domain.locate_definition('test_domain')
29
+ end
30
+
31
+ should "fail to find a non-existent definition" do
32
+ assert_equal nil, @domain.locate_definition('fake_filename')
33
+ end
34
+
35
+ should "reject any invalid config" do
36
+ # generate all trivial invalid configs
37
+ @h.domain_data.keys.each do |key|
38
+ d = @h.domain_data.clone
39
+ d.delete key
40
+ filepath = File.join(@h.domain_dir, 'broken_thing.yml')
41
+ File.open(filepath, 'w') do |f|
42
+ f.puts d.to_yaml
43
+ end
44
+
45
+ assert_raise Biopsy::DomainLoadError do
46
+ @domain.load_by_name 'broken_thing'
47
+ end
48
+
49
+ File.delete filepath if File.exists? filepath
50
+ end
51
+ end
52
+
53
+ should "write a template that can be loaded as a valid definition" do
54
+ @domain.write_template File.join(@h.domain_dir, 'template.yml')
55
+ @domain.load_by_name 'template'
56
+ assert_equal ['objective1', 'objective2'], @domain.objectives
57
+ end
58
+
59
+ end # Domain context
60
+
61
+ end # TestDomain
@@ -0,0 +1,84 @@
1
+ require 'helper'
2
+
3
+ class TestExperiment < Test::Unit::TestCase
4
+
5
+ context "Experiment" do
6
+
7
+ setup do
8
+ @h = Helper.new
9
+ @h.setup_tmp_dir
10
+
11
+ # we need a domain
12
+ @h.setup_domain
13
+ domain_name = @h.create_valid_domain
14
+ @domain = Biopsy::Domain.new domain_name
15
+
16
+ # and a target
17
+ @h.setup_target
18
+ target_name = @h.create_valid_target
19
+ @target = Biopsy::Target.new @domain
20
+ @target.load_by_name target_name
21
+
22
+ # and an objective
23
+ @h.setup_objective
24
+ @h.create_valid_objective
25
+ end
26
+
27
+ teardown do
28
+ @h.cleanup
29
+ end
30
+
31
+ should "fail to init when passed a non existent target" do
32
+ assert_raise Biopsy::TargetLoadError do
33
+ Biopsy::Experiment.new('fake_target', 'test_domain')
34
+ end
35
+ end
36
+
37
+ should "fail to init when passed a non existent domain" do
38
+ assert_raise Biopsy::DomainLoadError do
39
+ Biopsy::Experiment.new('test_target', 'fake_domain')
40
+ end
41
+ end
42
+
43
+ should "be able to select a valid point from the parameter space" do
44
+ e = Biopsy::Experiment.new('test_target', 'test_domain')
45
+ start_point = e.random_start_point
46
+ start_point.each_pair do |param, value|
47
+ assert @h.target_data[:parameter_ranges][param].include? value
48
+ end
49
+ end
50
+
51
+ should "be able to select a starting point" do
52
+ e = Biopsy::Experiment.new('test_target', 'test_domain')
53
+ start_point = e.start
54
+ start_point.each_pair do |param, value|
55
+ assert @h.target_data[:parameter_ranges][param].include? value
56
+ end
57
+ end
58
+
59
+ should "respect user's choice of starting point" do
60
+ s = {:a => 2, :b => 4}
61
+ e = Biopsy::Experiment.new('test_target', 'test_domain', s)
62
+ assert_equal s, e.start
63
+ end
64
+
65
+ should "automatically select an optimiser if none is specified" do
66
+ e = Biopsy::Experiment.new('test_target', 'test_domain')
67
+ assert e.algorithm.kind_of? Biopsy::TabuSearch
68
+ end
69
+
70
+ should "return an optimal set of parameters and score when run" do
71
+ # Kernel.srand 123
72
+ e = Biopsy::Experiment.new('test_target', 'test_domain')
73
+ known_best = {
74
+ :a => 4,
75
+ :b => 4,
76
+ :c => 4
77
+ }
78
+ best_found = e.run[:parameters]
79
+ assert_equal known_best, best_found
80
+ end
81
+
82
+ end # Experiment context
83
+
84
+ end # TestExperiment
data/test/test_file.rb ADDED
@@ -0,0 +1,20 @@
1
+ require 'helper'
2
+
3
+ class TestFile < Test::Unit::TestCase
4
+
5
+ context "File" do
6
+
7
+ should "return full path to executable in $PATH" do
8
+ filename = 'testfile'
9
+ path = ENV['PATH'].split(File::PATH_SEPARATOR).first
10
+ filepath = File.join(path, filename)
11
+ File.open(filepath, 'w') do |f|
12
+ f.puts 'test'
13
+ end
14
+ File.chmod(0777, filepath)
15
+ assert_equal filepath, File.which(filename)
16
+ end
17
+
18
+ end # File context
19
+
20
+ end # TestFile
data/test/test_hash.rb ADDED
@@ -0,0 +1,55 @@
1
+ require 'helper'
2
+
3
+ class TestHash < Test::Unit::TestCase
4
+
5
+ require 'pp'
6
+
7
+ context "Hash" do
8
+
9
+ setup do
10
+ @a = {
11
+ 'a' => {
12
+ 'b' => 1,
13
+ 'c' => {
14
+ 'd' => [2, 3, 4]
15
+ }
16
+ }
17
+ }
18
+ end
19
+
20
+ should "recursively symbolise keys" do
21
+ b = {
22
+ :a => {
23
+ :b => 1,
24
+ :c => {
25
+ :d => [2, 3, 4]
26
+ }
27
+ }
28
+ }
29
+ assert @a != b, "hash with string keys not equal to hash with symbol keys"
30
+ assert_equal @a.deep_symbolize, b, "symbolized string key hash equals symbol key hash"
31
+ end
32
+
33
+ should "recursively merge hashes" do
34
+ b = {
35
+ 'a' => {
36
+ 'c' => {
37
+ 'e' => 'new value'
38
+ }
39
+ }
40
+ }
41
+ c = {
42
+ 'a' => {
43
+ 'b' => 1,
44
+ 'c' => {
45
+ 'd' => [2, 3, 4],
46
+ 'e' => 'new value'
47
+ }
48
+ }
49
+ }
50
+ assert_equal @a.deep_merge(b), c
51
+ end
52
+
53
+ end # Hash context
54
+
55
+ end # TestHash
@@ -0,0 +1,99 @@
1
+ require 'helper'
2
+
3
+ class TestObjectiveHandler < Test::Unit::TestCase
4
+
5
+ context "ObjectiveHandler" do
6
+
7
+ setup do
8
+ @h = Helper.new
9
+ @h.setup_tmp_dir
10
+
11
+ # we need a domain
12
+ @h.setup_domain
13
+ domain_name = @h.create_valid_domain
14
+ @domain = Biopsy::Domain.new domain_name
15
+
16
+ # and a target
17
+ @h.setup_target
18
+ target_name = @h.create_valid_target
19
+ @target = Biopsy::Target.new @domain
20
+ @target.load_by_name target_name
21
+
22
+ # and an objective
23
+ @h.setup_objective
24
+ objective_name = @h.create_valid_objective
25
+ end
26
+
27
+ teardown do
28
+ @h.cleanup
29
+ end
30
+
31
+ should "return loaded objectives on init" do
32
+ oh = Biopsy::ObjectiveHandler.new @domain, @target
33
+ refute oh.objectives.empty?
34
+ end
35
+
36
+ should "prefer local objective list to full set" do
37
+ Dir.chdir(@h.objective_dir) do
38
+ objective = %{
39
+ class AnotherObjective < Biopsy::ObjectiveFunction
40
+ def run(input, threads)
41
+ 10
42
+ end
43
+ end
44
+ }
45
+ File.open('another_objective.rb', 'w') do |f|
46
+ f.puts objective
47
+ end
48
+ File.open('objectives.txt', 'w') do |f|
49
+ f.puts 'another_objective'
50
+ end
51
+ end
52
+ oh = Biopsy::ObjectiveHandler.new @domain, @target
53
+ assert_equal 1, oh.objectives.length
54
+ assert_equal 'AnotherObjective', oh.objectives.keys.first
55
+ end
56
+
57
+ should "run an objective and return the result" do
58
+ oh = Biopsy::ObjectiveHandler.new @domain, @target
59
+ values = {
60
+ :a => 4,
61
+ :b => 4,
62
+ :c => 4
63
+ }
64
+ file = File.expand_path(File.join(@h.tmp_dir, 'output.txt'))
65
+ File.open(file, 'w') do |f|
66
+ f.puts values.to_yaml
67
+ end
68
+ result = oh.run_for_output({:params => file}, 0, 1, allresults=true)
69
+ assert_equal 0, result[:results]["TestObjective"][:result]
70
+ end
71
+
72
+ should "perform euclidean distance dimension reduction" do
73
+ oh = Biopsy::ObjectiveHandler.new @domain, @target
74
+ results = {
75
+ :a => {
76
+ :optimum => 100,
77
+ :weighting => 1,
78
+ :result => 49,
79
+ :max => 100
80
+ },
81
+ :b => {
82
+ :optimum => 1,
83
+ :weighting => 1,
84
+ :result => 62,
85
+ :max => 100
86
+ },
87
+ :c => {
88
+ :optimum => 0,
89
+ :weighting => 1,
90
+ :result => 33,
91
+ :max => 66
92
+ }
93
+ }
94
+ assert_equal 0.47140452079103173, oh.dimension_reduce(results)
95
+ end
96
+
97
+ end # Experiment context
98
+
99
+ end # TestExperiment
@@ -0,0 +1,74 @@
1
+ require 'helper'
2
+
3
+ class TestSettings < Test::Unit::TestCase
4
+
5
+ context "Settings" do
6
+
7
+ setup do
8
+ @data = {
9
+ :domain => 'test_domain',
10
+ :objectives_dir => './objectives',
11
+ }
12
+ @config_file = File.expand_path 'testconfig.yml'
13
+ @settings = Biopsy::Settings.instance
14
+ File.open(@config_file, 'w') do |f|
15
+ f.puts @data.to_yaml
16
+ end
17
+ @settings.load @config_file
18
+ end
19
+
20
+ teardown do
21
+ File.delete @config_file if File.exists? @config_file
22
+ end
23
+
24
+ should "load the specified config file" do
25
+ assert @settings.domain == @data[:domain]
26
+ assert @settings.objectives_dir == @data[:objectives_dir]
27
+ end
28
+
29
+ should "complain about malformed config file" do
30
+ # write non-YAML data to file
31
+ File.open(@config_file, 'w') do |f|
32
+ f.puts @test
33
+ end
34
+ assert_raise RuntimeError do
35
+ @settings.load @config_file
36
+ end
37
+ end
38
+
39
+ should "be able to save settings and load them identically" do
40
+ @settings.save @config_file
41
+ @settings.load @config_file
42
+ @data.each_pair do |key, value|
43
+ varname = "@#{key.to_s}".to_sym
44
+ assert_equal value, @settings.instance_variable_get(varname)
45
+ end
46
+ end
47
+
48
+ should "be a singleton" do
49
+ assert_equal @settings.object_id, Biopsy::Settings.instance.object_id
50
+ end
51
+
52
+ should "make loaded settings available as methods" do
53
+ assert @settings.domain == @data[:domain], 'domain key not loaded as method'
54
+ assert @settings.objectives_dir == @data[:objectives_dir], 'objectives_dir key not loaded as method'
55
+ end
56
+
57
+ should "produce a YAML string representation" do
58
+ s = @settings.to_s
59
+ h = YAML.load(s)
60
+ @data.each_pair do |key, value|
61
+ varname = "@#{key.to_s}".to_sym
62
+ assert_equal value, @settings.instance_variable_get(varname)
63
+ end
64
+ end
65
+
66
+ should "error when a non-existent config is requested" do
67
+ assert_raise Biopsy::SettingsError do
68
+ @settings.locate_config :fake_key, 'blah'
69
+ end
70
+ end
71
+
72
+ end # RunHandler context
73
+
74
+ end # TestRunHandler
@@ -0,0 +1,14 @@
1
+ require 'helper'
2
+
3
+ class TestString < Test::Unit::TestCase
4
+
5
+ context "String" do
6
+
7
+ should "return CamelCase version of snake_case" do
8
+ assert_equal "snake_case".camelize, "SnakeCase"
9
+ assert_equal "a_b_c_d".camelize, "ABCD"
10
+ end
11
+
12
+ end # String context
13
+
14
+ end # TestString
@@ -0,0 +1,89 @@
1
+ require 'helper'
2
+
3
+ class TestTarget < Test::Unit::TestCase
4
+
5
+ require 'fileutils'
6
+
7
+ context "Target" do
8
+
9
+ setup do
10
+ @h = Helper.new
11
+ @h.setup_tmp_dir
12
+
13
+ # we need a domain
14
+ @h.setup_domain
15
+ domain_name = @h.create_valid_domain
16
+ @domain = Biopsy::Domain.new domain_name
17
+
18
+ # and a target
19
+ @h.setup_target
20
+ target_name = @h.create_valid_target
21
+ @target = Biopsy::Target.new @domain
22
+ @target.load_by_name target_name
23
+ end
24
+
25
+ teardown do
26
+ @h.cleanup
27
+ end
28
+
29
+ should "be able to find an existing definition" do
30
+ filepath = File.join(@h.target_dir, 'fake_thing.yml')
31
+ File.open(filepath, 'w') do |f|
32
+ f.puts "this doesn't matter"
33
+ end
34
+
35
+ assert_equal filepath, @target.locate_definition('fake_thing')
36
+ end
37
+
38
+ should "fail to find a non-existent definition" do
39
+ assert_equal nil, @target.locate_definition('not_real')
40
+ end
41
+
42
+ should "reject any invalid config" do
43
+ # generate all trivial invalid configs
44
+ @h.target_data.keys.each do |key|
45
+ d = @h.target_data.clone
46
+ d.delete key
47
+ filepath = File.join(@h.target_dir, 'broken_thing.yml')
48
+ File.open(filepath, 'w') do |f|
49
+ f.puts d.to_yaml
50
+ end
51
+
52
+ assert_raise Biopsy::TargetLoadError do
53
+ @target.load_by_name 'broken_thing'
54
+ end
55
+
56
+ File.delete filepath if File.exists? filepath
57
+ end
58
+ end
59
+
60
+ should "reject a config that doesn't match the domain spec" do
61
+ d = @h.target_data
62
+ d[:input_files][:fake] = 'another.file'
63
+ assert @target.validate_config(d).length > 0
64
+ end
65
+
66
+ should "be able to store a loaded config file" do
67
+ config = YAML::load_file(@h.target_path).deep_symbolize
68
+ @target.store_config config
69
+ @h.target_data.each_pair do |key, value|
70
+ assert_equal value, @target.instance_variable_get('@' + key.to_s)
71
+ end
72
+ end
73
+
74
+ should "recognise a malformed or missing constructor" do
75
+ config = YAML::load_file(@h.target_path).deep_symbolize
76
+ @target.store_config config
77
+
78
+ assert !@target.check_constructor, "missing constructor is invalid"
79
+
80
+ File.open(@h.target_data[:constructor_path], 'w') do |f|
81
+ f.puts '[x**2 for x in range(10)]' # python :)
82
+ end
83
+ assert !@target.check_constructor, "invalid ruby is invalid"
84
+ File.delete @h.target_data[:constructor_path]
85
+ end
86
+
87
+ end # Target context
88
+
89
+ end # TestTarget context