answer-factory 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/LICENSE.txt +21 -0
  2. data/Rakefile +29 -0
  3. data/Thorfile +79 -0
  4. data/VERSION +1 -0
  5. data/_spikes/old_vs_new_dominated_by?.rb +45 -0
  6. data/config/database.yml +9 -0
  7. data/lib/answer-factory.rb +14 -0
  8. data/lib/answers/answer.rb +126 -0
  9. data/lib/answers/batch.rb +49 -0
  10. data/lib/factories/factory.rb +53 -0
  11. data/lib/factories/workstation.rb +33 -0
  12. data/lib/operators/basic_operators.rb +240 -0
  13. data/lib/operators/evaluators.rb +113 -0
  14. data/lib/operators/samplers_and_selectors.rb +131 -0
  15. data/pkg/nudgegp-0.0.1.gem +0 -0
  16. data/readme.md +29 -0
  17. data/spec/answer_spec.rb +412 -0
  18. data/spec/batch_spec.rb +98 -0
  19. data/spec/config_spec.rb +94 -0
  20. data/spec/factories/factory_spec.rb +86 -0
  21. data/spec/factories/workstation_spec.rb +139 -0
  22. data/spec/operators/any_one_sampler_spec.rb +39 -0
  23. data/spec/operators/dominated_quantile_spec.rb +111 -0
  24. data/spec/operators/duplicate_genomes_spec.rb +35 -0
  25. data/spec/operators/evaluators/program_point_evaluator_spec.rb +43 -0
  26. data/spec/operators/evaluators/test_case_evaluator_spec.rb +129 -0
  27. data/spec/operators/infrastructure_spec.rb +45 -0
  28. data/spec/operators/most_dominated_subset_spec.rb +47 -0
  29. data/spec/operators/nondominated_subset_spec.rb +103 -0
  30. data/spec/operators/pointCrossover_spec.rb +60 -0
  31. data/spec/operators/pointDeletion_spec.rb +62 -0
  32. data/spec/operators/pointMutation_spec.rb +77 -0
  33. data/spec/operators/random_guess_spec.rb +77 -0
  34. data/spec/operators/resample_and_clone_spec.rb +60 -0
  35. data/spec/operators/resample_values_spec.rb +135 -0
  36. data/spec/operators/uniformBackboneCrossover_spec.rb +67 -0
  37. data/spec/spec_helper.rb +14 -0
  38. metadata +201 -0
@@ -0,0 +1,35 @@
1
+ require File.join(File.dirname(__FILE__), "./../spec_helper")
2
+
3
+
4
+ describe AllDuplicatedGenomesSampler do
5
+ before(:each) do
6
+ @dg = AllDuplicatedGenomesSampler.new
7
+ end
8
+
9
+
10
+ describe "#generate" do
11
+ before(:each) do
12
+ @dudes = Batch[Answer.new("block {}"), Answer.new("block {}"), Answer.new("do int_add")]
13
+ end
14
+
15
+ it "should take a Batch as a first parameter" do
16
+ lambda{@dg.generate()}.should raise_error(ArgumentError)
17
+ lambda{@dg.generate(@dudes)}.should_not raise_error(ArgumentError)
18
+ end
19
+
20
+ it "should return a Batch" do
21
+ @dg.generate(@dudes).should be_a_kind_of(Batch)
22
+ end
23
+
24
+ it "should return a Batch containing references to individuals in the original Batch" do
25
+ repeats = @dg.generate(@dudes)
26
+ repeats.length.should == 2
27
+ repeats.each {|dude| @dudes.should include(dude)}
28
+ end
29
+
30
+ it "should return an empty Batch if there are no duplicates" do
31
+ @dudes.delete_at(0)
32
+ @dg.generate(@dudes).should be_empty
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,43 @@
1
+ require File.join(File.dirname(__FILE__), "./../../spec_helper")
2
+
3
+
4
+ describe ProgramPointEvaluator do
5
+
6
+ describe "initialization" do
7
+ it "should have an objective name set upon initialization" do
8
+ lambda{ProgramPointEvaluator.new}.should raise_error(ArgumentError)
9
+ end
10
+ end
11
+
12
+ describe "#evaluate" do
13
+ before(:each) do
14
+ @ppe = ProgramPointEvaluator.new(:name => :point_count)
15
+ @dudes = Batch[
16
+ Answer.new("block {}"),
17
+ Answer.new("block { block {}}"),
18
+ Answer.new("block { block { block {}}}")
19
+ ]
20
+ end
21
+
22
+ it "should take a Batch of individuals as an argument" do
23
+ lambda{@ppe.evaluate(Answer.new("block {}"))}.should raise_error(ArgumentError)
24
+ lambda{@ppe.evaluate(Batch[Answer.new("block {}")])}.should_not raise_error(ArgumentError)
25
+ end
26
+
27
+ it "should raise an exception if it can't evaluate an Answer" do
28
+ lambda{@ppe.evaluate(Batch[Answer.new("blah_de_blah")])}.should raise_error(ArgumentError)
29
+ end
30
+
31
+ it "should write the number of program points into each individual" do
32
+ @dudes.each {|dude| dude.scores[:point_count].should == nil}
33
+ @ppe.evaluate(@dudes)
34
+ @dudes.collect {|dude| dude.scores[:point_count]}.should == [1,2,3]
35
+ end
36
+
37
+ it "should return the same Batch it got, with updated values" do
38
+ origIDs = @dudes.collect {|dude| dude.object_id}
39
+ @ppe.evaluate(@dudes)
40
+ @dudes.each {|dude| origIDs.should include(dude.object_id)}
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,129 @@
1
+ #encoding: utf-8
2
+ require File.join(File.dirname(__FILE__), "./../../spec_helper")
3
+
4
+
5
+ describe TestCase do
6
+ describe "bindings" do
7
+ it "should have a Hash of independent variable names and assigned values" do
8
+ TestCase.new.should respond_to(:bindings)
9
+ end
10
+ end
11
+
12
+ describe "expectations" do
13
+ it "should have a Hash of dependent variable names and expected values" do
14
+ TestCase.new.should respond_to(:expectations)
15
+ end
16
+ end
17
+
18
+ describe "gauges" do
19
+ it "should have a Hash of Proc objects that measure the observed values" do
20
+ TestCase.new.should respond_to(:gauges)
21
+ end
22
+
23
+ it "should have one gauge for every expectation" do
24
+ lambda{TestCase.new(
25
+ :expectations => {"y1"=>ValuePoint.new("int", 31)},
26
+ :gauges => {"h"=>Proc.new {} } )}.should raise_error
27
+ lambda{TestCase.new(
28
+ :expectations => {"y1"=>ValuePoint.new("int", 31)},
29
+ :gauges => {"y1"=>Proc.new {} } )}.should_not raise_error
30
+ end
31
+ end
32
+ end
33
+
34
+
35
+
36
+
37
+
38
+ describe TestCaseEvaluator do
39
+ describe "initialize" do
40
+ it "should have a name" do
41
+ lambda{TestCaseEvaluator.new()}.should raise_error(ArgumentError)
42
+ lambda{TestCaseEvaluator.new(name:"boolean_multiplexer_SSE")}.should_not raise_error
43
+ end
44
+
45
+ it "should have all the info it needs to set up an Interpreter"
46
+ end
47
+
48
+ describe "evaluate" do
49
+ before(:each) do
50
+ @tce = TestCaseEvaluator.new(:name => :error)
51
+ @dudes = Batch[
52
+ Answer.new("value «int»\n«int» 12"),
53
+ Answer.new("value «int»\n«int» -9912"),
54
+ Answer.new("value «bool»\n«bool» false"),
55
+ Answer.new("block { value «int» value «int»}\n«int» 99\n«int» 12")
56
+ ]
57
+ @cases = [
58
+ TestCase.new(:bindings => {"x" => ValuePoint.new("int",77)},
59
+ :expectations => {"y" => 12},
60
+ :gauges => {"y" => Proc.new {|interp| interp.stacks[:int].peek}}
61
+ ),
62
+ TestCase.new(:bindings => {"x" => ValuePoint.new("int",78)},
63
+ :expectations => {"y" => 13},
64
+ :gauges => {"y" => Proc.new {|interp| interp.stacks[:int].peek}}
65
+ )
66
+ ]
67
+ end
68
+
69
+ it "should raise an exception if the batch argument isn't a Batch" do
70
+ lambda{@tce.evaluate()}.should raise_error(ArgumentError)
71
+ lambda{@tce.evaluate(@dudes)}.should_not raise_error(ArgumentError)
72
+ end
73
+
74
+ it "should assign a numeric score to all the Answers in the Batch" do
75
+ @dudes.each {|dude| dude.scores[:error].should == nil}
76
+ @tce.evaluate(@dudes, @cases)
77
+ @dudes.each {|dude| dude.scores[:error].kind_of?(Numeric).should == true}
78
+ end
79
+
80
+ it "should create an Interpreter for each run" do
81
+ myI = Interpreter.new
82
+ Interpreter.should_receive(:new).exactly(8).times.and_return(myI)
83
+ @tce.evaluate(@dudes, @cases)
84
+ end
85
+
86
+ it "should be reset each time before running them" do
87
+ myI = Interpreter.new
88
+ Interpreter.stub!(:new).and_return(myI)
89
+ myI.should_receive(:reset).exactly(8).times
90
+ @tce.evaluate(@dudes, @cases)
91
+ end
92
+
93
+ it "should set up bindings before running them" do
94
+ myI = Interpreter.new
95
+ Interpreter.stub!(:new).and_return(myI)
96
+ myI.should_receive(:bind_variable).exactly(8).times
97
+ @tce.evaluate(@dudes, @cases)
98
+ end
99
+
100
+ it "should run each TestCase for each individual in the Batch" do
101
+ myI = Interpreter.new
102
+ Interpreter.stub!(:new).and_return(myI)
103
+ myI.should_receive(:run).exactly(8).times
104
+ @tce.evaluate(@dudes, @cases)
105
+ end
106
+
107
+ it "after running a TestCase using an Answer, all the gauges should be called" do
108
+ myI = Interpreter.new
109
+ Interpreter.stub!(:new).and_return(myI)
110
+ myI.should_receive(:run).exactly(8).times
111
+ @tce.evaluate(@dudes, @cases)
112
+
113
+ myI.should_receive(:run).exactly(4).times
114
+ @tce.evaluate(@dudes, [TestCase.new(:gauges => {"a" => Proc.new {|interp|}})])
115
+ end
116
+
117
+ it "should synthesize the gauge readings into a single numerical score"
118
+
119
+ it "should loop over the test cases to collect the error" do
120
+ @dudes.each {|dude| dude.scores[:error].should == nil}
121
+
122
+ @tce.evaluate(@dudes, @cases)
123
+ equal12or13error = @dudes.collect {|dude| dude.scores[:error]}
124
+ expectedScores = [1, 19849, 200000, 1]
125
+ (0..3).each {|i| equal12or13error[i].should be_close(expectedScores[i].to_f / 2, 0.0001)}
126
+ end
127
+
128
+ end
129
+ end
@@ -0,0 +1,45 @@
1
+ require File.join(File.dirname(__FILE__), "./../spec_helper")
2
+
3
+
4
+ describe "domination_classes" do
5
+ before(:each) do
6
+ @dq = Sampler.new
7
+
8
+ @dudes = Batch[Answer.new("block {}"), Answer.new("do int_add")]
9
+ @dudes[0].scores["first"] = 120
10
+ @dudes[1].scores["first"] = 2
11
+ @dudes[0].scores["second"] = 2
12
+ @dudes[1].scores["second"] = 120
13
+ @dudes[0].scores["third"] = 20
14
+ @dudes[1].scores["third"] = 1200
15
+ end
16
+
17
+ it "should take a Batch as an argument" do
18
+ lambda{@dq.domination_classes()}.should raise_error(ArgumentError)
19
+ lambda{@dq.domination_classes(@dudes)}.should_not raise_error(ArgumentError)
20
+
21
+ end
22
+
23
+ it "can take a template of objective names to use" do
24
+ lambda{@dq.domination_classes(@dudes, ["first"])}.should_not raise_error(ArgumentError)
25
+ end
26
+
27
+ it "should return a Hash" do
28
+ @dq.domination_classes(@dudes, ["first"]).should be_a_kind_of(Hash)
29
+ end
30
+
31
+ it "should use the count of dominating Answers as the key" do
32
+ @dq.domination_classes(@dudes, ["second"]).keys.should == [0,1]
33
+ @dq.domination_classes(@dudes).keys.should == [0]
34
+
35
+ end
36
+
37
+ it "should have an Array of the Answers in that class as the value" do
38
+ @dq.domination_classes(@dudes, ["first"])[0].length.should == 1
39
+ @dq.domination_classes(@dudes, ["first"])[0][0].should == @dudes[1]
40
+ @dq.domination_classes(@dudes, ["second"])[0].length.should == 1
41
+ @dq.domination_classes(@dudes, ["second"])[0][0].should == @dudes[0]
42
+ @dq.domination_classes(@dudes, ["second", "third"])[0].length.should == 1
43
+ @dq.domination_classes(@dudes, ["second", "third"])[0][0].should == @dudes[0]
44
+ end
45
+ end
@@ -0,0 +1,47 @@
1
+ require File.join(File.dirname(__FILE__), "./../spec_helper")
2
+
3
+
4
+ describe MostDominatedSubsetSampler do
5
+ before(:each) do
6
+ @md = MostDominatedSubsetSampler.new
7
+ end
8
+
9
+
10
+ describe "#generate" do
11
+ before(:each) do
12
+ @dudes = Batch[Answer.new("block {}"), Answer.new("block {}"), Answer.new("block {}")]
13
+ @dudes[0].scores = Hash[:first, 2, :second, 20, :third, 200]
14
+ @dudes[1].scores = Hash[:first, 20, :second, 200, :third, 2]
15
+ @dudes[2].scores = Hash[:first, 200, :second, 2, :third, 20]
16
+ end
17
+
18
+ it "should take a Batch as a first parameter" do
19
+ lambda{@md.generate()}.should raise_error(ArgumentError)
20
+ lambda{@md.generate(@dudes)}.should_not raise_error(ArgumentError)
21
+ end
22
+
23
+ it "should return a Batch" do
24
+ @md.generate(@dudes).should be_a_kind_of(Batch)
25
+ end
26
+
27
+ it "should return a Batch containing references to individuals in the original Batch" do
28
+ half = @md.generate(@dudes)
29
+ half.length.should be > 0
30
+ half.each {|dude| @dudes.should include(dude)}
31
+ end
32
+
33
+ it "should always return at least one, even when everybody is nondominated" do
34
+ some = @md.generate(@dudes)
35
+ some.length.should == 3
36
+ some.each {|dude| @dudes.should include(dude)}
37
+ end
38
+
39
+
40
+ it "should take a template of objectives to use in sorting" do
41
+ @md.generate(@dudes,[:first]).should include(@dudes[2])
42
+ @md.generate(@dudes,[:second]).should include(@dudes[1])
43
+ @md.generate(@dudes,[:second, :third]).should include(@dudes[0])
44
+ end
45
+ end
46
+
47
+ end
@@ -0,0 +1,103 @@
1
+ require File.join(File.dirname(__FILE__), "./../spec_helper")
2
+
3
+
4
+ describe "nondominated_subset operator" do
5
+ before(:each) do
6
+ @myNondominatedScreener = NondominatedSubsetSelector.new
7
+ @params = {points:3, instruction_names:[:int_add, :int_multiply], type_names:["int"]}
8
+ @myGuesser = RandomGuessOperator.new(@params)
9
+ @twoGuys = @myGuesser.generate(2)
10
+ end
11
+
12
+ it "should be a kind of SearchOperator" do
13
+ @myNondominatedScreener.should be_a_kind_of(SearchOperator)
14
+ end
15
+
16
+ it "the #all_known_criteria method should return an Array with the union of all scores keys in the crowd" do
17
+ @twoGuys[0].scores = {x2:612, y2:77, x3:712}
18
+ @twoGuys[1].scores = {y1:2, x1:3, x2:4}
19
+ @myNondominatedScreener.all_known_criteria(@twoGuys).sort.should == [:x1,:x2, :x3, :y1, :y2].sort
20
+ end
21
+
22
+ it "the #all_shared_scores method should return an Array of the keys of the #scores hashes in the crowd" do
23
+ twoGuys = @myGuesser.generate(2)
24
+ twoGuys[0].scores = {x2:612, x1:77, x3:712}
25
+ twoGuys[1].scores = {x2:2, x1:3, x3:4}
26
+ @myNondominatedScreener.all_shared_scores(twoGuys).sort.should == [:x1,:x2, :x3].sort
27
+
28
+ twoGuys[0].scores = {x3:712, :something_else => 88}
29
+ twoGuys[1].scores = {x2:2, :x1 => 3, :x3 => 4}
30
+ @myNondominatedScreener.all_shared_scores(twoGuys).sort.should == [:x3].sort
31
+ end
32
+
33
+ it "should produce a Batch of Answer objects when it receives #generate; at least one" do
34
+ twoGuys = @myGuesser.generate(2)
35
+ twoGuys[0].scores = {:x2 => 612, :x1 => 77, :x3 => 712}
36
+ twoGuys[1].scores = {:x2 => 2, :x1 => 3, :x3 => 4}
37
+ @myNondominatedScreener.generate(twoGuys).should be_a_kind_of(Batch)
38
+ @myNondominatedScreener.generate(twoGuys)[0].should be_a_kind_of(Answer)
39
+ end
40
+
41
+ it "should work, passing along references not clones to the original guys" do
42
+ twoGuys = @myGuesser.generate(2)
43
+ twoGuys[0].scores = {x2:612, x1:77, x3:712}
44
+ twoGuys[1].scores = {x2:2, x1:3, x3:4}
45
+ @myNondominatedScreener.generate(twoGuys).should include(twoGuys[1])
46
+ @myNondominatedScreener.generate(twoGuys).should_not include(twoGuys[0])
47
+
48
+
49
+ twoGuys[0].scores = {:x2 => 1, :x1 => 2, :x3 => 3}
50
+ twoGuys[1].scores = {:x2 => 2, :x1 => 1, :x3 => 3}
51
+ @myNondominatedScreener.generate(twoGuys).length.should == 2
52
+ @myNondominatedScreener.generate(twoGuys).should include(twoGuys[0])
53
+ @myNondominatedScreener.generate(twoGuys).should include(twoGuys[1])
54
+ end
55
+
56
+ it "should work the same no matter what the scores hash order" do
57
+ twoGuys = @myGuesser.generate(2)
58
+ twoGuys[0].scores = {:x1 => 77, :x3 => 712,:x2 => 612}
59
+ twoGuys[1].scores = {:x2 => 2, :x1 => 3, :x3 => 4}
60
+ @myNondominatedScreener.generate(twoGuys).should include(twoGuys[1])
61
+
62
+ end
63
+
64
+ it "can use the 'template' parameter to compare individuals by a specified vector of scores" do
65
+ threeGuys = @myGuesser.generate(3)
66
+ threeGuys[0].scores = {:x1 => 1, :x2 => 2, :x3 => 3}
67
+ threeGuys[1].scores = {:x1 => 2, :x2 => 1, :x3 => 1}
68
+ threeGuys[2].scores = {:x1 => 4, :x2 => 0, :x3 => 2}
69
+ @myNondominatedScreener.generate(threeGuys).length.should == 3
70
+ @myNondominatedScreener.generate(threeGuys,[:x1]).length.should == 1
71
+ @myNondominatedScreener.generate(threeGuys,[:x1]).should include(threeGuys[0])
72
+ @myNondominatedScreener.generate(threeGuys,[:x2]).length.should == 1
73
+ @myNondominatedScreener.generate(threeGuys,[:x2]).should include(threeGuys[2])
74
+ @myNondominatedScreener.generate(threeGuys,[:x3]).length.should == 1
75
+ @myNondominatedScreener.generate(threeGuys,[:x3]).should include(threeGuys[1])
76
+ @myNondominatedScreener.generate(threeGuys,[:x1,:x3]).length.should == 2
77
+ @myNondominatedScreener.generate(threeGuys,[:x1,:x2]).length.should == 3
78
+ @myNondominatedScreener.generate(threeGuys,[:x2,:x3]).length.should == 2
79
+ end
80
+
81
+
82
+ it "should not include Answers in the resulting Batch that have not got the specified scores"
83
+
84
+
85
+ it "should use a default 'template' parameter that's the intersection of scores keys in the crowd" do
86
+ twoGuys = @myGuesser.generate(2)
87
+ twoGuys[0].scores = {x1:1, x2:2, x3:3}
88
+ twoGuys[1].scores = { x2:0, x4:12}
89
+
90
+ @myNondominatedScreener.generate(twoGuys).length.should == 1
91
+ @myNondominatedScreener.generate(twoGuys).should include twoGuys[1]
92
+ @myNondominatedScreener.generate(twoGuys,[:x2]).length.should == 1
93
+ end
94
+
95
+ it "should return Answers that belonged to crowd passed in" do
96
+ twoGuys = @myGuesser.generate(2)
97
+ orig_IDs = twoGuys.collect {|dude| dude.object_id}
98
+ twoGuys[0].scores = {:x1 => 1, :x2 => 2, :x3 => 3}
99
+ twoGuys[1].scores = { :x2 => 1, :x4 => 3}
100
+ @myNondominatedScreener.generate(twoGuys).length.should == 1
101
+ @myNondominatedScreener.generate(twoGuys).each {|newDude| orig_IDs.should include(newDude.object_id)}
102
+ end
103
+ end
@@ -0,0 +1,60 @@
1
+ require File.join(File.dirname(__FILE__), "./../spec_helper")
2
+
3
+
4
+ describe "PointCrossoverOperator" do
5
+ before(:each) do
6
+ @myXover = PointCrossoverOperator.new
7
+ @dude1 = Answer.new("block{do a \n do b\n do c}")
8
+ @dude2 = Answer.new("block{ref x \n ref y\n ref z}")
9
+ end
10
+
11
+ it "should be a kind of SearchOperator" do
12
+ @myXover.should be_a_kind_of(SearchOperator)
13
+ end
14
+
15
+
16
+ describe "generate" do
17
+
18
+ it "should take as a param a set of one or more Answers" do
19
+ lambda{@myXover.generate()}.should raise_error(ArgumentError)
20
+ lambda{@myXover.generate(331)}.should raise_error(ArgumentError)
21
+ lambda{@myXover.generate([])}.should raise_error(ArgumentError)
22
+ lambda{@myXover.generate([11])}.should raise_error(ArgumentError)
23
+
24
+ lambda{@myXover.generate([@dude1])}.should_not raise_error(ArgumentError)
25
+ end
26
+
27
+ it "should produce the same number of Answers it gets as a default" do
28
+ babies = @myXover.generate([@dude1])
29
+ babies.length.should == 1
30
+ end
31
+
32
+ it "should have an optional parameter that specifies the number of offspring to produce per parent" do
33
+ babies = @myXover.generate(Batch.[](@dude1, @dude2))
34
+ babies.length.should == 2
35
+ babies = @myXover.generate([@dude1, @dude2],4)
36
+ babies.length.should == 8
37
+ end
38
+
39
+ it "should only include points from one of the parents in the offspring blueprints" do
40
+ babies = @myXover.generate([@dude1, @dude2])
41
+ bothGenomes = @dude1.program.blueprint + @dude2.program.blueprint
42
+ babies.each do |baby|
43
+ baby.program.blueprint.each_line do |line|
44
+ bothGenomes.match(line.strip.delete("}")).should_not == nil
45
+ end
46
+ end
47
+ end
48
+
49
+ it "should handle moving the footnotes correctly"
50
+
51
+ it "should maintain unused footnotes correctly"
52
+
53
+ it "should increment the offspring's progress from the max parents' progress" do
54
+ @dude1.stub(:progress).and_return(7)
55
+ @dude2.stub(:progress).and_return(11)
56
+ babies = @myXover.generate([@dude1, @dude2],10)
57
+ babies.each {|baby| [8,12].should include(baby.progress)}
58
+ end
59
+ end
60
+ end