answer-factory 0.0.1

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