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,62 @@
1
+ require File.join(File.dirname(__FILE__), "./../spec_helper")
2
+
3
+
4
+ describe "PointDeleteOperator search operator" do
5
+ describe "generate" do
6
+ before(:each) do
7
+ @zapper = PointDeleteOperator.new()
8
+ @dude1 = Answer.new("block { do thing1 \n do thing2 \n do thing3}")
9
+ end
10
+
11
+ it "should accept a Batch as a param" do
12
+ lambda{@zapper.generate()}.should raise_error(ArgumentError)
13
+ lambda{@zapper.generate(812)}.should raise_error(ArgumentError)
14
+ lambda{@zapper.generate(Batch.new)}.should_not raise_error(ArgumentError)
15
+ lambda{@zapper.generate(Batch[@dude1])}.should_not raise_error(ArgumentError)
16
+ end
17
+
18
+ it "should raise an Argument error if all contents of the crowd aren't Answers" do
19
+ lambda{@zapper.generate([])}.should_not raise_error(ArgumentError)
20
+ lambda{@zapper.generate([ 77 ])}.should raise_error(ArgumentError)
21
+ lambda{@zapper.generate([ @dude1, 77 ])}.should raise_error(ArgumentError)
22
+ end
23
+
24
+ it "should return a Batch as a result" do
25
+ @zapper.generate([@dude1]).should be_a_kind_of(Batch)
26
+ end
27
+
28
+ it "should use Answer#delete_point to produce the variants" do
29
+ @dude1.should_receive(:delete_point_or_clone).and_return("do parseable")
30
+ @zapper.generate([@dude1])
31
+ end
32
+
33
+ it "should produce one result per individual in the wildtype crowd as a default" do
34
+ @zapper.generate([@dude1]).length.should == 1
35
+ @zapper.generate([@dude1, @dude1]).length.should == 2
36
+ end
37
+
38
+ it "should produce more if passed the optional howManyCopies parameter > 1" do
39
+ @zapper.generate([@dude1],2).length.should == 2
40
+ @zapper.generate([@dude1, @dude1],3).length.should == 6
41
+ end
42
+
43
+ it "should produce individuals from which a random point (and its subpoints) is deleted" do
44
+ @zapper.generate([@dude1],5).each {|baby| baby.points.should < @dude1.points}
45
+ end
46
+
47
+ it "should produce 'block {}' whenever a root is deleted" do
48
+ @zapper.should_receive(:rand).with(4).and_return(0)
49
+ @zapper.generate([@dude1])[0].blueprint.should == "block {}"
50
+ end
51
+
52
+ it "should increment the progress of the offspring" do
53
+ @dude1.stub(:progress).and_return(195)
54
+ @zapper.generate([@dude1])[0].progress.should == 196
55
+ end
56
+
57
+ it "should handle moving the footnotes correctly"
58
+
59
+ it "should maintain unused footnotes correctly"
60
+
61
+ end
62
+ end
@@ -0,0 +1,77 @@
1
+ require File.join(File.dirname(__FILE__), "./../spec_helper")
2
+
3
+
4
+ describe "PointMutationOperator" do
5
+ describe "initialization" do
6
+ it "should have a params attribute when created that sets basic values for code generation" do
7
+ PointMutationOperator.new.incoming_options.should == {}
8
+ mutator = PointMutationOperator.new(:points => 3, :blocks => 1)
9
+ mutator.incoming_options.should_not == {}
10
+ mutator.incoming_options[:points].should == 3
11
+ end
12
+ end
13
+
14
+ describe "generate" do
15
+ before(:each) do
16
+ @gammaray = PointMutationOperator.new(target_size_in_points: 3, types_name: ["int"])
17
+ @dude1 = Answer.new("block { do x1 \n do x2 \n do x3}")
18
+ end
19
+
20
+ it "should accept an Array of one or more Answers as a param" do
21
+ lambda{@gammaray.generate()}.should raise_error(ArgumentError)
22
+ lambda{@gammaray.generate(99)}.should raise_error(ArgumentError)
23
+ lambda{@gammaray.generate([])}.should raise_error(ArgumentError)
24
+ lambda{@gammaray.generate([99])}.should raise_error(ArgumentError)
25
+
26
+ lambda{@gammaray.generate([@dude1])}.should_not raise_error(ArgumentError)
27
+ end
28
+
29
+ it "should return a Batch as a result" do
30
+ @gammaray.generate([@dude1]).should be_a_kind_of(Batch)
31
+ end
32
+
33
+ it "should use Answer#replace_point to produce the variants" do
34
+ @dude1.should_receive(:replace_point_or_clone).and_return("do anything")
35
+ @gammaray.generate([@dude1])
36
+ end
37
+
38
+ it "should produce one result per individual in the wildtype crowd as a default" do
39
+ @gammaray.generate([@dude1]).length.should == 1
40
+ @gammaray.generate([@dude1,@dude1]).length.should == 2
41
+ end
42
+
43
+ it "should produce more if passed the optional howManyCopies parameter > 1" do
44
+ @gammaray.generate([@dude1],3).length.should == 3
45
+ @gammaray.generate([@dude1,@dude1],2).length.should == 4
46
+ end
47
+
48
+ it "should produce individuals from which a random point (and all subpoints) is replaced" do
49
+ @gammaray.should_receive(:rand).and_return(0)
50
+ @gammaray.generate([@dude1])[0].points.should == 3 # totally replaced with 3-pt code
51
+ @gammaray.should_receive(:rand).and_return(1)
52
+ @gammaray.generate([@dude1])[0].points.should == 6 #replace point 2 with 3-pt code
53
+ @gammaray.should_receive(:rand).and_return(2)
54
+ @gammaray.generate([@dude1])[0].points.should == 6 #replace point 3 with 3-pt code
55
+ @gammaray.should_receive(:rand).and_return(3)
56
+ @gammaray.generate([@dude1])[0].points.should == 6 #replace point 4 with 3-pt code
57
+ end
58
+
59
+ it "should accept temporarily overriding params to pass into CodeType.random_value" do
60
+ @gammaray.should_receive(:rand).and_return(0)
61
+ @gammaray.generate([@dude1])[0].points.should == 3 # totally replaced with 3-pt code
62
+ @gammaray.should_receive(:rand).and_return(0)
63
+ @gammaray.generate([@dude1],1,target_size_in_points: 10)[0].points.should == 10
64
+ end
65
+
66
+ it "should increment the #progress of the offspring" do
67
+ @dude1.stub(:progress).and_return(888)
68
+ @gammaray.generate([@dude1],13).each {|baby| baby.progress.should == 889}
69
+ end
70
+
71
+ it "should handle moving the footnotes correctly"
72
+
73
+ it "should maintain unused footnotes correctly"
74
+
75
+ it "should introduce new footnotes smoothly, if a ValuePoint is added"
76
+ end
77
+ end
@@ -0,0 +1,77 @@
1
+ #encoding:utf-8
2
+ require File.join(File.dirname(__FILE__), "./../spec_helper")
3
+
4
+
5
+ describe "random_guess operator" do
6
+ before(:each) do
7
+ @myGuesser = RandomGuessOperator.new
8
+ end
9
+
10
+ it "should be a kind of SearchOperator" do
11
+ @myGuesser.should be_a_kind_of(SearchOperator)
12
+ end
13
+
14
+ it "should have a params attribute when created that sets basic values for code generation" do
15
+ RandomGuessOperator.new.incoming_options.should == {}
16
+ thisGuesser = RandomGuessOperator.new(foo:99, bar:101)
17
+ thisGuesser.incoming_options.should_not == {}
18
+ thisGuesser.incoming_options[:foo].should == 99
19
+ end
20
+
21
+
22
+ it "should produce a Batch of Answers when it receives #generate" do
23
+ newDudes = @myGuesser.generate
24
+ newDudes.should be_a_kind_of(Batch)
25
+ newDudes[0].should be_a_kind_of(Answer)
26
+ newDudes[0].blueprint.should_not == nil
27
+ newDudes[0].program.should_not == nil
28
+ end
29
+
30
+ it "should produce one as a default, more if a higher number is passed in" do
31
+ @myGuesser.generate.length.should == 1
32
+ @myGuesser.generate(4).length.should == 4
33
+ end
34
+
35
+ it "should have a parsed blueprint as its #program attribute" do
36
+ newDudes = @myGuesser.generate
37
+ newDudes[0].program.should be_a_kind_of(NudgeProgram)
38
+ end
39
+
40
+ it "should accept temporarily overriding options to pass into CodeType.any_value" do
41
+ @myNewGuesser = RandomGuessOperator.new(
42
+ target_size_in_points: 7,
43
+ instruction_names: ["int_add", "int_subtract"],
44
+ reference_names: ["x1", "x2", "x3"])
45
+
46
+ lambda{@myNewGuesser.generate(
47
+ 3,
48
+ target_size_in_points: 12,
49
+ reference_names: ["y1"])}.should_not raise_error
50
+
51
+ @myNewGuesser.generate(
52
+ 3,
53
+ target_size_in_points: 12)[0].program.points.should_not == 7
54
+
55
+ @myNewGuesser.generate(
56
+ 3,
57
+ target_size_in_points: 12)[0].program.points.should == 12
58
+
59
+ @myNewGuesser.generate(
60
+ 1,
61
+ target_size_in_points: 16,
62
+ probabilities:{b:0,r:1,v:0,i:0},
63
+ reference_names: ["y1"])[0].blueprint.should include("y1")
64
+ end
65
+
66
+ it "should produce a Batch that contains Answers with progress=0 only" do
67
+ @myGuesser.generate(12).each {|dude| dude.progress.should == 0}
68
+ end
69
+
70
+ it "should handle generated footnotes correctly" do
71
+ @myGuesser.generate(1,
72
+ target_size_in_points: 52,
73
+ type_names:["int"],
74
+ probabilities: {b:0,v:1,i:0,r:0})[0].program.footnote_section.scan(/«int»/).length.should == 51
75
+ end
76
+
77
+ end
@@ -0,0 +1,60 @@
1
+ require File.join(File.dirname(__FILE__), "./../spec_helper")
2
+
3
+
4
+ describe "resample_and_clone operator" do
5
+ before(:each) do
6
+ @myGuesser = RandomGuessOperator.new(type_names: ["int"], instruction_names: ["int_add"])
7
+ @mySampler = ResampleAndCloneOperator.new
8
+ end
9
+
10
+ it "should be a kind of SearchOperator" do
11
+ @mySampler.should be_a_kind_of(SearchOperator)
12
+ end
13
+
14
+ it "should produce a list of Answers when it receives #generate" do
15
+ newDudes = @mySampler.generate(@myGuesser.generate(3))
16
+ newDudes.should be_a_kind_of(Batch)
17
+ newDudes[0].should be_a_kind_of(Answer)
18
+ newDudes[0].blueprint.should_not == nil
19
+ newDudes[0].program.should_not == nil
20
+ end
21
+
22
+ it "should produce one Answer with a blueprint identical to one of the passed in crowd's" do
23
+ pop = @myGuesser.generate(1)
24
+ newDudes = @mySampler.generate(pop)
25
+ newDudes[0].blueprint.should == pop[0].blueprint
26
+ end
27
+
28
+ it "should return more than one individual when asked to, resampling as needed" do
29
+ newDudes = @mySampler.generate(@myGuesser.generate(10))
30
+ newDudes.length.should == 1
31
+ newDudes = @mySampler.generate(@myGuesser.generate(3),2)
32
+ newDudes.length.should == 2
33
+ newDudes = @mySampler.generate(@myGuesser.generate(1),2)
34
+ newDudes.length.should == 2
35
+ newDudes[0].blueprint.should == newDudes[1].blueprint
36
+ end
37
+
38
+ it "should not return links to the original program copies in the new clones" do
39
+ pop = @myGuesser.generate(3)
40
+ newDudes = @mySampler.generate(pop)
41
+ pop.collect {|old_dude| old_dude.program.object_id}.should_not include(newDudes[0].program.object_id)
42
+ end
43
+
44
+ it "should have a parsed blueprint as its #program attribute" do
45
+ pop = @myGuesser.generate(3)
46
+ newDudes = @mySampler.generate(pop)
47
+ newDudes[0].program.should be_a_kind_of(NudgeProgram)
48
+ end
49
+
50
+ it "should increment the #progress of each clone" do
51
+ pop = @myGuesser.generate(3)
52
+ pop.each {|donor| donor.stub(:progress).and_return(12)}
53
+ newDudes = @mySampler.generate(pop)
54
+ newDudes.each {|kid| kid.progress.should == 13}
55
+ end
56
+
57
+ it "should handle footnotes correctly"
58
+
59
+ it "should use a deep copy to clone new guys, not just clone"
60
+ end
@@ -0,0 +1,135 @@
1
+ #encoding: utf-8
2
+ require File.join(File.dirname(__FILE__), "./../spec_helper")
3
+
4
+
5
+ describe "ResampleValuesOperator search operator" do
6
+
7
+ it "should not need any initial parameters" do
8
+ lambda{ResampleValuesOperator.new()}.should_not raise_error
9
+ end
10
+
11
+ it "should be possible to pass in stored parameters" do
12
+ lambda{ResampleValuesOperator.new(randomIntegerLowerBound: 12)}.should_not raise_error
13
+ rs = ResampleValuesOperator.new({boolTrueProbability: 0.2})
14
+ rs.incoming_options.should include(:boolTrueProbability)
15
+ end
16
+
17
+ describe "generate" do
18
+ before(:each) do
19
+ @rs = ResampleValuesOperator.new({boolTrueProbability: 0.2})
20
+ @intDude = Answer.new("block {value «int»}\n«int» 3")
21
+ @boolDude = Answer.new("block {value «bool»}\n«bool» false")
22
+ @floatDude = Answer.new("block {value «float»}\n«float» -991.2213")
23
+ @complicatedDude = Answer.new(
24
+ "block {value «int» value «bool» block {value «float»}}\n«int» 3\n«bool» false\n«float» 0.0")
25
+ end
26
+
27
+ it "should require a Batch as a first parameter" do
28
+ lambda{@rs.generate()}.should raise_error(ArgumentError)
29
+ lambda{@rs.generate(Batch.new)}.should_not raise_error(ArgumentError)
30
+ end
31
+
32
+ it "should raise an ArgumentError if the array parameter isn't all Answers" do
33
+ lambda{@rs.generate([88])}.should raise_error(ArgumentError)
34
+ lambda{@rs.generate([@intDude])}.should_not raise_error(ArgumentError)
35
+ end
36
+
37
+ it "should by default produce one (1) resampled mutant for each input" do
38
+ @rs.generate([@intDude]).length.should == 1
39
+ @rs.generate([@intDude, @intDude]).length.should == 2
40
+ end
41
+
42
+ it "should call #any_value for each line in each individual that is a 'sample'" do
43
+ IntType.should_receive(:any_value).and_return(777)
44
+ newGuys = @rs.generate([@intDude])
45
+
46
+ BoolType.should_receive(:any_value).and_return(false)
47
+ newGuys = @rs.generate([@boolDude])
48
+
49
+ FloatType.should_receive(:any_value).and_return(9.999)
50
+ newGuys = @rs.generate([@floatDude])
51
+
52
+ IntType.should_receive(:any_value).and_return(777)
53
+ BoolType.should_receive(:any_value).and_return(false)
54
+ FloatType.should_receive(:any_value).and_return(9.999)
55
+ newGuys = @rs.generate([@complicatedDude])
56
+
57
+ IntType.should_receive(:any_value).and_return(1,2)
58
+ BoolType.should_receive(:any_value).and_return(false,true)
59
+ FloatType.should_receive(:any_value).and_return(1.0,2.0)
60
+ oldGuys = [@intDude,@boolDude,@floatDude,@complicatedDude]
61
+ newGuys = @rs.generate(oldGuys,1)
62
+ end
63
+
64
+ it "should be possible to pass in a higher integer, and get that many variants for each input" do
65
+ IntType.should_receive(:any_value).and_return(11,22,33)
66
+ newGuys = @rs.generate([@intDude],3)
67
+ newGuys[0].blueprint.should include("11")
68
+ newGuys[1].blueprint.should include("22")
69
+ newGuys[2].blueprint.should include("33")
70
+
71
+ IntType.should_receive(:any_value).and_return(4,5,6,7)
72
+ newGuys = @rs.generate([@intDude, @intDude],2)
73
+ end
74
+
75
+ it "should be using the Operator's saved parameters as a default behavior" do
76
+ wholeLottaParams = {
77
+ :randomIntegerLowerBound => 1000,
78
+ :randomIntegerUpperBound => 1005,
79
+ :randomBooleanTruthProb => 0.2,
80
+ :randomFloatLowerBound => 112.0,
81
+ :randomFloatUpperBound => 112.5}
82
+
83
+ resampleLimited = ResampleValuesOperator.new(wholeLottaParams)
84
+
85
+ IntType.should_receive(:any_value).with(hash_including(wholeLottaParams))
86
+ newGuys = resampleLimited.generate([@intDude])
87
+
88
+ BoolType.should_receive(:any_value).with(hash_including(wholeLottaParams))
89
+ newGuys = resampleLimited.generate([@boolDude])
90
+
91
+ FloatType.should_receive(:any_value).with(hash_including(wholeLottaParams))
92
+ newGuys = resampleLimited.generate([@floatDude])
93
+ end
94
+
95
+ it "should return Answers who (probably) differ from the originals passed in" do
96
+ outOfRangeParams = {
97
+ :randomIntegerLowerBound => 1000,
98
+ :randomIntegerUpperBound => 1005,
99
+ :randomBooleanTruthProb => 1.0,
100
+ :randomFloatLowerBound => 112.0,
101
+ :randomFloatUpperBound => 112.5}
102
+ resampleFarAway = ResampleValuesOperator.new(outOfRangeParams)
103
+ newGuys = resampleFarAway.generate([@intDude])
104
+ newGuys[0].blueprint.should =~ /100[0-5]/
105
+ newGuys = resampleFarAway.generate([@boolDude])
106
+ newGuys[0].blueprint.should =~ /true/
107
+ newGuys = resampleFarAway.generate([@floatDude])
108
+ newGuys[0].blueprint.should =~ /112\.[0-5]/
109
+ end
110
+
111
+ it "should be possible to temporarily override some or all of the preset @params" do
112
+ bigInt = {:randomIntegerLowerBound => 90000,:randomIntegerUpperBound => 91000}
113
+ toBeOverridden = ResampleValuesOperator.new(bigInt)
114
+ defaults = toBeOverridden.generate([@intDude],5)
115
+ defaults.each {|dude| dude.blueprint.should =~ /9\d\d\d\d/}
116
+
117
+ littler = toBeOverridden.generate([@intDude],5,
118
+ :randomIntegerLowerBound => -19, :randomIntegerUpperBound => -10)
119
+ littler.each {|dude| dude.blueprint.should =~ /-1\d/}
120
+ end
121
+
122
+ it "should increment the #progress of every clone" do
123
+ @complicatedDude.stub!(:progress).and_return(192)
124
+ @rs.generate([@complicatedDude],5).each {|clone| clone.progress.should == 193}
125
+ end
126
+
127
+ it "should leave unrecognized types with footnote values in place" do
128
+ hunh = Answer.new("value «foo»\n«foo» bar")
129
+ ResampleValuesOperator.new.generate([hunh])[0].blueprint.should include("bar")
130
+ end
131
+
132
+ it "should work with CodeType footnotes, and reduce the number of points in each successive call"
133
+ end
134
+
135
+ end
@@ -0,0 +1,67 @@
1
+ require File.join(File.dirname(__FILE__), "./../spec_helper")
2
+
3
+
4
+ describe "UniformBackboneCrossoverOperator" do
5
+ before(:each) do
6
+ @newDudes = []
7
+ @options = {target_size_in_points: 6, instruction_names: ["a", "b", "c"], type_names: ["int"]}
8
+ @myXover = UniformBackboneCrossoverOperator.new
9
+ @myGuesser = RandomGuessOperator.new(@options)
10
+ end
11
+
12
+ it "should be a kind of SearchOperator" do
13
+ @myXover.should be_a_kind_of(SearchOperator)
14
+ end
15
+
16
+ it "should produce a Batch of Answers when it receives #generate" do
17
+ @newDudes = @myXover.generate(@myGuesser.generate(2))
18
+ @newDudes.should be_a_kind_of(Batch)
19
+ @newDudes.each {|dude| dude.should be_a_kind_of(Answer)}
20
+ end
21
+
22
+ it "should produce the same number of Answers it gets as a default" do
23
+ @newDudes = @myXover.generate(@myGuesser.generate(6))
24
+ @newDudes.length.should == 6
25
+ end
26
+
27
+ it "should have an optional parameter that specifies the number of offspring to produce" do
28
+ @newDudes = @myXover.generate(@myGuesser.generate(2),5)
29
+ @newDudes.length.should == 5
30
+ end
31
+
32
+ it "should only include backbone points from one of the parents in the offsprings' genomes" do
33
+ rents = @myGuesser.generate(2)
34
+ @newDudes = @myXover.generate(rents,1)
35
+ @newDudes.length.should == 1
36
+ allParentalPoints = rents[0].program[1].contents + rents[1].program[1].contents
37
+ allTidied = allParentalPoints.collect {|pt| "#{pt.tidy}<br />"}
38
+ end
39
+
40
+ it "should return an identical individual if given only one parent" do
41
+ rent = @myGuesser.generate(1)
42
+ @newDudes = @myXover.generate(rent,3)
43
+ @newDudes.each {|kid| kid.program.tidy.should == rent[0].program.tidy}
44
+ end
45
+
46
+
47
+ it "should not affect the original parents set in any way" do
48
+ rents = @myGuesser.generate(2)
49
+ originalMom = rents[0].object_id
50
+ @newDudes = @myXover.generate(rents,1)
51
+ rents[0].object_id.should == originalMom
52
+ end
53
+
54
+ it "should return offspring with #progress values incremented from the largest parent value" do
55
+ rents = @myGuesser.generate(2)
56
+ rents[0].should_receive(:progress).at_least(1).times.and_return(12)
57
+ rents[1].should_receive(:progress).at_least(1).times.and_return(33)
58
+ @newDudes = @myXover.generate(rents,20)
59
+ @newDudes.each {|baby| [13, 34].should include(baby.progress)}
60
+ end
61
+
62
+ it "should handle moving the footnotes correctly"
63
+
64
+ it "should maintain unused footnotes correctly"
65
+
66
+
67
+ end