biopsy 0.1.0.alpha

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/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