answer-factory 0.0.9 → 0.0.10

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/Rakefile +1 -0
  2. data/Thorfile +9 -6
  3. data/VERSION +1 -1
  4. data/answer-factory.gemspec +20 -5
  5. data/lib/answer-factory.rb +14 -2
  6. data/lib/answers/answer.rb +17 -3
  7. data/lib/answers/batch.rb +15 -3
  8. data/lib/factories/workstation.rb +50 -1
  9. data/lib/operators/all_duplicated_genomes_sampler.rb +14 -0
  10. data/lib/operators/any_one_sampler.rb +7 -0
  11. data/lib/operators/dominated_quantile_selector.rb +16 -0
  12. data/lib/operators/infrastructure.rb +74 -0
  13. data/lib/operators/most_dominated_subset_sampler.rb +13 -0
  14. data/lib/operators/nondominated_subset_selector.rb +17 -0
  15. data/lib/operators/point_crossover_operator.rb +24 -0
  16. data/lib/operators/point_delete_operator.rb +19 -0
  17. data/lib/operators/point_mutation_operator.rb +22 -0
  18. data/lib/operators/program_point_count_evaluator.rb +14 -0
  19. data/lib/operators/random_guess_operator.rb +30 -0
  20. data/lib/operators/resample_and_clone_operator.rb +28 -0
  21. data/lib/operators/resample_values_operator.rb +40 -0
  22. data/lib/operators/{evaluators.rb → test_case_evaluator.rb} +3 -34
  23. data/lib/operators/uniform_backbone_crossover_operator.rb +53 -0
  24. data/readme.md +28 -3
  25. data/spec/answer_spec.rb +33 -1
  26. data/spec/batch_spec.rb +25 -12
  27. data/spec/factories/factory_spec.rb +53 -36
  28. data/spec/factories/workstation_spec.rb +194 -20
  29. data/spec/operators/evaluators/program_point_evaluator_spec.rb +1 -1
  30. data/spec/operators/evaluators/test_case_evaluator_spec.rb +2 -2
  31. data/spec/operators/nondominated_subset_spec.rb +8 -8
  32. data/spec/operators/random_guess_spec.rb +16 -11
  33. data/spec/operators/resample_and_clone_spec.rb +8 -8
  34. data/spec/operators/uniformBackboneCrossover_spec.rb +7 -7
  35. data/spec/spec_helper.rb +1 -0
  36. metadata +38 -12
  37. data/lib/operators/basic_operators.rb +0 -240
  38. data/lib/operators/samplers_and_selectors.rb +0 -131
@@ -14,23 +14,23 @@ describe "UniformBackboneCrossoverOperator" do
14
14
  end
15
15
 
16
16
  it "should produce a Batch of Answers when it receives #generate" do
17
- @newDudes = @myXover.generate(@myGuesser.generate(2))
17
+ @newDudes = @myXover.generate(@myGuesser.generate(Batch.new,how_many:2))
18
18
  @newDudes.should be_a_kind_of(Batch)
19
19
  @newDudes.each {|dude| dude.should be_a_kind_of(Answer)}
20
20
  end
21
21
 
22
22
  it "should produce the same number of Answers it gets as a default" do
23
- @newDudes = @myXover.generate(@myGuesser.generate(6))
23
+ @newDudes = @myXover.generate(@myGuesser.generate(Batch.new,how_many:6))
24
24
  @newDudes.length.should == 6
25
25
  end
26
26
 
27
27
  it "should have an optional parameter that specifies the number of offspring to produce" do
28
- @newDudes = @myXover.generate(@myGuesser.generate(2),5)
28
+ @newDudes = @myXover.generate(@myGuesser.generate(Batch.new,how_many:2),5)
29
29
  @newDudes.length.should == 5
30
30
  end
31
31
 
32
32
  it "should only include backbone points from one of the parents in the offsprings' genomes" do
33
- rents = @myGuesser.generate(2)
33
+ rents = @myGuesser.generate(Batch.new,how_many:2)
34
34
  @newDudes = @myXover.generate(rents,1)
35
35
  @newDudes.length.should == 1
36
36
  allParentalPoints = rents[0].program[1].contents + rents[1].program[1].contents
@@ -38,21 +38,21 @@ describe "UniformBackboneCrossoverOperator" do
38
38
  end
39
39
 
40
40
  it "should return an identical individual if given only one parent" do
41
- rent = @myGuesser.generate(1)
41
+ rent = @myGuesser.generate(Batch.new,how_many:1)
42
42
  @newDudes = @myXover.generate(rent,3)
43
43
  @newDudes.each {|kid| kid.program.tidy.should == rent[0].program.tidy}
44
44
  end
45
45
 
46
46
 
47
47
  it "should not affect the original parents set in any way" do
48
- rents = @myGuesser.generate(2)
48
+ rents = @myGuesser.generate(Batch.new,how_many:2)
49
49
  originalMom = rents[0].object_id
50
50
  @newDudes = @myXover.generate(rents,1)
51
51
  rents[0].object_id.should == originalMom
52
52
  end
53
53
 
54
54
  it "should return offspring with #progress values incremented from the largest parent value" do
55
- rents = @myGuesser.generate(2)
55
+ rents = @myGuesser.generate(Batch.new,how_many:2)
56
56
  rents[0].should_receive(:progress).at_least(1).times.and_return(12)
57
57
  rents[1].should_receive(:progress).at_least(1).times.and_return(33)
58
58
  @newDudes = @myXover.generate(rents,20)
data/spec/spec_helper.rb CHANGED
@@ -3,6 +3,7 @@ $: << File.join(File.dirname(__FILE__), "/../lib")
3
3
 
4
4
  require 'spec'
5
5
  require 'fakeweb'
6
+ require 'couchrest'
6
7
  require './lib/answer-factory'
7
8
 
8
9
  include AnswerFactory
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 9
9
- version: 0.0.9
8
+ - 10
9
+ version: 0.0.10
10
10
  platform: ruby
11
11
  authors:
12
12
  - Bill Tozier
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2010-05-02 00:00:00 -04:00
19
+ date: 2010-05-06 00:00:00 -04:00
20
20
  default_executable: answer-factory
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency
@@ -59,9 +59,23 @@ dependencies:
59
59
  type: :runtime
60
60
  version_requirements: *id003
61
61
  - !ruby/object:Gem::Dependency
62
- name: fakeweb
62
+ name: configatron
63
63
  prerelease: false
64
64
  requirement: &id004 !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ segments:
69
+ - 2
70
+ - 6
71
+ - 2
72
+ version: 2.6.2
73
+ type: :runtime
74
+ version_requirements: *id004
75
+ - !ruby/object:Gem::Dependency
76
+ name: fakeweb
77
+ prerelease: false
78
+ requirement: &id005 !ruby/object:Gem::Requirement
65
79
  requirements:
66
80
  - - ">="
67
81
  - !ruby/object:Gem::Version
@@ -70,11 +84,11 @@ dependencies:
70
84
  - 33
71
85
  version: "0.33"
72
86
  type: :runtime
73
- version_requirements: *id004
87
+ version_requirements: *id005
74
88
  - !ruby/object:Gem::Dependency
75
89
  name: sinatra
76
90
  prerelease: false
77
- requirement: &id005 !ruby/object:Gem::Requirement
91
+ requirement: &id006 !ruby/object:Gem::Requirement
78
92
  requirements:
79
93
  - - ">="
80
94
  - !ruby/object:Gem::Version
@@ -84,11 +98,11 @@ dependencies:
84
98
  - 4
85
99
  version: 0.9.4
86
100
  type: :runtime
87
- version_requirements: *id005
101
+ version_requirements: *id006
88
102
  - !ruby/object:Gem::Dependency
89
103
  name: activesupport
90
104
  prerelease: false
91
- requirement: &id006 !ruby/object:Gem::Requirement
105
+ requirement: &id007 !ruby/object:Gem::Requirement
92
106
  requirements:
93
107
  - - ">="
94
108
  - !ruby/object:Gem::Version
@@ -98,7 +112,7 @@ dependencies:
98
112
  - 5
99
113
  version: 2.3.5
100
114
  type: :runtime
101
- version_requirements: *id006
115
+ version_requirements: *id007
102
116
  description: The pragmaticgp gem provides a simple framework for building, running and managing genetic programming experiments which automatically discover algorithms and equations to solve user-defined problems.
103
117
  email: bill@vagueinnovation.com
104
118
  executables:
@@ -122,9 +136,21 @@ files:
122
136
  - lib/answers/batch.rb
123
137
  - lib/factories/factory.rb
124
138
  - lib/factories/workstation.rb
125
- - lib/operators/basic_operators.rb
126
- - lib/operators/evaluators.rb
127
- - lib/operators/samplers_and_selectors.rb
139
+ - lib/operators/all_duplicated_genomes_sampler.rb
140
+ - lib/operators/any_one_sampler.rb
141
+ - lib/operators/dominated_quantile_selector.rb
142
+ - lib/operators/infrastructure.rb
143
+ - lib/operators/most_dominated_subset_sampler.rb
144
+ - lib/operators/nondominated_subset_selector.rb
145
+ - lib/operators/point_crossover_operator.rb
146
+ - lib/operators/point_delete_operator.rb
147
+ - lib/operators/point_mutation_operator.rb
148
+ - lib/operators/program_point_count_evaluator.rb
149
+ - lib/operators/random_guess_operator.rb
150
+ - lib/operators/resample_and_clone_operator.rb
151
+ - lib/operators/resample_values_operator.rb
152
+ - lib/operators/test_case_evaluator.rb
153
+ - lib/operators/uniform_backbone_crossover_operator.rb
128
154
  - pkg/nudgegp-0.0.1.gem
129
155
  - readme.md
130
156
  - spec/answer_spec.rb
@@ -1,240 +0,0 @@
1
- #encoding: utf-8
2
- module AnswerFactory
3
-
4
- # Abstract class that from which specific SearchOperator subclasses inherit initialization
5
-
6
- class SearchOperator
7
- attr_accessor :incoming_options
8
-
9
- def initialize(options={})
10
- @incoming_options = options
11
- end
12
- end
13
-
14
-
15
-
16
-
17
- class RandomGuessOperator < SearchOperator
18
-
19
- # returns an Array of random Answers
20
- #
21
- # the first (optional) parameter specifies how many to make, and defaults to 1
22
- # the second (also optional) parameter is a hash that
23
- # can temporarily override those set in the initialization
24
- #
25
- # For example, if
26
- # <tt>myRandomGuesser = RandomGuessOperator.new(:randomIntegerLowerBound => -90000)</tt>
27
- #
28
- # [<tt>myRandomGuesser.generate()</tt>]
29
- # produces a list of 1 Answer, and if it has any IntType samples they will be in [-90000,100]
30
- # (since the default +:randomIntegerLowerBound+ is 100)
31
- # [<tt>myRandomGuesser.generate(1,:randomIntegerLowerBound => 0)</tt>]
32
- # makes one Answer whose IntType samples (if any) will be between [0,100]
33
-
34
- def generate(howMany = 1, overridden_options ={})
35
- result = Batch.new
36
- howMany.times do
37
- newGenome = CodeType.any_value(@incoming_options.merge(overridden_options))
38
- newDude = Answer.new(newGenome, progress:0)
39
- result << newDude
40
- end
41
- result
42
- end
43
- end
44
-
45
-
46
-
47
-
48
- class ResampleAndCloneOperator < SearchOperator
49
-
50
- # returns an Array of clones of Answers randomly selected from the crowd passed in
51
- #
52
- # the first (required) parameter is an Array of Answers
53
- # the second (optional) parameter is how many samples to take, and defaults to 1
54
- #
55
- # For example, if
56
- # <tt>@currentPopulation = [a list of 300 Answers]</tt> and
57
- # <tt>myRandomSampler = ResampleAndCloneOperator.new(@currentPopulation)</tt>
58
- # [<tt>myRandomSampler.generate()</tt>]
59
- # produces a list of 1 Answer, which is a clone of somebody from <tt>@currentPopulation</tt>
60
- # [<tt>myRandomGuesser.generate(11)</tt>]
61
- # returns a list of 11 Answers cloned from <tt>@currentPopulation</tt>,
62
- # possibly including repeats
63
-
64
- def generate(crowd, howMany = 1)
65
- result = Batch.new
66
- howMany.times do
67
- donor = crowd.sample
68
- clone = Answer.new(donor.blueprint, progress:donor.progress + 1)
69
- result << clone
70
- end
71
- return result
72
- end
73
- end
74
-
75
-
76
-
77
-
78
- class ResampleValuesOperator < SearchOperator
79
-
80
- def generate(crowd, howManyCopies = 1, overridden_options = {})
81
- crowd.each {|dude| raise(ArgumentError) if !dude.kind_of?(Answer) }
82
-
83
- result = Batch.new
84
- regenerating_options = @incoming_options.merge(overridden_options)
85
- crowd.each do |dude|
86
- howManyCopies.times do
87
- wildtype_program = dude.program
88
- starting_footnotes = wildtype_program.footnote_section.split( /^(?=«)/ )
89
- breaker = /^«([a-zA-Z][a-zA-Z0-9_]*)»\s*(.*)\s*/m
90
- type_value_pairs = starting_footnotes.collect {|fn| fn.match(breaker)[1..2]}
91
-
92
- mutant_blueprint = wildtype_program.code_section
93
-
94
- type_value_pairs.each do |pair|
95
-
96
- begin
97
- type_name = pair[0]
98
- type_class = "#{type_name}_type".camelize.constantize
99
- reduced_size = regenerating_options[:target_size_in_points] || rand(dude.points/2)
100
- reduced_option = {target_size_in_points:reduced_size}
101
- resampled_value = type_class.any_value(regenerating_options.merge(reduced_option)).to_s
102
- rescue NameError
103
- resampled_value = pair[1]
104
- end
105
- mutant_blueprint << "\n«#{pair[0].strip}» #{resampled_value.strip}"
106
- end
107
- mutant = Answer.new(mutant_blueprint, progress:dude.progress + 1)
108
- result << mutant
109
- end
110
- end
111
- return result
112
- end
113
- end
114
-
115
-
116
-
117
-
118
- class UniformBackboneCrossoverOperator < SearchOperator
119
-
120
- # Returns a Batch of new Answers whose programs are made by stitching together
121
- # the programs of pairs of 'parents'. The incoming Batch is divided into pairs based on
122
- # adjacency (modulo the Batch.length), one pair for each 'offspring' to be made. To make
123
- # an offspring, the number of backbone program points is determined in each parent; 'backbone'
124
- # refers to the number of branches directly within the root of the program, not the entire tree.
125
- #
126
- # To construct an offspring's program, program points are copied from the first parent with
127
- # probability p, or the second parent with probability (1-p), for each point in the first
128
- # parent's backbone. So if there are 13 and 6 points, respectively, the first six points are
129
- # selected randomly, but the last 7 are copied from the first parent. If there are 8 and 11
130
- # respectively, then the last 3 will be ignored from the second parent in any case.
131
- #
132
- # the first (required) parameter is an Array of Answers
133
- # the second (optional) parameter is how many crossovers to make,
134
- # which defaults to the number of Answers in the incoming Batch
135
-
136
- def generate(crowd, howMany = crowd.length, prob = 0.5)
137
- result = Batch.new
138
- howMany.times do
139
- where = rand(crowd.length)
140
- mom = crowd[where]
141
- dad = crowd[ (where+1) % crowd.length ]
142
- mom_backbone_length = mom.program[1].contents.length
143
- dad_backbone_length = dad.program[1].contents.length
144
-
145
- baby_blueprint_parts = ["",""]
146
- (0..mom_backbone_length-1).each do |backbone_point|
147
- if rand() < prob
148
- next_chunks = mom.program[1].contents[backbone_point].blueprint_parts || ["",""]
149
- else
150
- if backbone_point < dad_backbone_length
151
- next_chunks = (dad.program[1].contents[backbone_point].blueprint_parts || ["", ""])
152
- else
153
- next_chunks = ["",""]
154
- end
155
- end
156
- baby_blueprint_parts[0] << " #{next_chunks[0]}"
157
- baby_blueprint_parts[1] << " \n#{next_chunks[1]}"
158
- end
159
- mom.program.unused_footnotes.each {|fn| baby_blueprint_parts[1] += "\n#{fn}"}
160
-
161
- baby_blueprint = "block {#{baby_blueprint_parts[0]}} #{baby_blueprint_parts[1]}"
162
- baby = Answer.new(baby_blueprint, progress:[mom.progress,dad.progress].max + 1)
163
-
164
- result << baby
165
- end
166
- return result
167
- end
168
- end
169
-
170
-
171
-
172
-
173
- class PointCrossoverOperator < SearchOperator
174
- def generate(crowd, howManyBabies = 1)
175
- raise(ArgumentError) if !crowd.kind_of?(Array)
176
- raise(ArgumentError) if crowd.empty?
177
- crowd.each {|dude| raise(ArgumentError) if !dude.kind_of?(Answer) }
178
-
179
- result = Batch.new
180
- production = crowd.length*howManyBabies
181
- production.times do
182
- mom = crowd.sample
183
- dad = crowd.sample
184
- mom_receives = rand(mom.points) + 1
185
- dad_donates = rand(dad.points) + 1
186
-
187
- baby_blueprint = mom.replace_point_or_clone(mom_receives,dad.program[dad_donates])
188
- baby = Answer.new(baby_blueprint,
189
- progress:[mom.progress,dad.progress].max + 1)
190
- result << baby
191
- end
192
- return result
193
- end
194
- end
195
-
196
-
197
-
198
-
199
- class PointDeleteOperator < SearchOperator
200
- def generate(crowd, howManyCopies = 1)
201
- raise(ArgumentError) if !crowd.kind_of?(Array)
202
- crowd.each {|dude| raise(ArgumentError) if !dude.kind_of?(Answer) }
203
-
204
- result = Batch.new
205
- crowd.each do |dude|
206
- howManyCopies.times do
207
- where = rand(dude.points)+1
208
- variant = dude.delete_point_or_clone(where)
209
- baby = Answer.new(variant, progress:dude.progress + 1)
210
- result << baby
211
- end
212
- end
213
- return result
214
- end
215
- end
216
-
217
-
218
-
219
-
220
- class PointMutationOperator < SearchOperator
221
-
222
- def generate(crowd, howManyCopies = 1, overridden_options ={})
223
- raise(ArgumentError) if !crowd.kind_of?(Array)
224
- raise(ArgumentError) if crowd.empty?
225
- crowd.each {|dude| raise(ArgumentError) if !dude.kind_of?(Answer) }
226
-
227
- result = Batch.new
228
- crowd.each do |dude|
229
- howManyCopies.times do
230
- where = rand(dude.points)+1
231
- newCode = CodeType.any_value(@incoming_options.merge(overridden_options))
232
- variant = dude.replace_point_or_clone(where,newCode)
233
- baby = Answer.new(variant, progress:dude.progress + 1)
234
- result << baby
235
- end
236
- end
237
- return result
238
- end
239
- end
240
- end
@@ -1,131 +0,0 @@
1
- module AnswerFactory
2
-
3
- class Sampler < SearchOperator
4
- def initialize (params = {})
5
- super
6
- end
7
-
8
- def all_known_criteria(crowd)
9
- union = []
10
- crowd.each do |dude|
11
- union |= dude.known_criteria
12
- end
13
- return union
14
- end
15
-
16
-
17
- def all_shared_scores(crowd)
18
- intersection = self.all_known_criteria(crowd)
19
- crowd.each do |dude|
20
- intersection = intersection & dude.known_criteria
21
- end
22
- return intersection
23
- end
24
-
25
-
26
- def domination_classes(crowd, template = all_shared_scores(crowd))
27
- result = Hash.new()
28
-
29
- crowd.each_index do |i|
30
- dominated_by = 0
31
-
32
- crowd.each_index do |j|
33
- dominated_by += 1 if crowd[i].dominated_by?(crowd[j], template)
34
- end
35
-
36
- result[dominated_by] ||= []
37
- result[dominated_by].push crowd[i]
38
- end
39
-
40
- return result
41
- end
42
-
43
-
44
- def diversity_classes(crowd)
45
- result = Hash.new()
46
- crowd.each do |dude|
47
- result[dude.program.tidy] ||= []
48
- result[dude.program.tidy] << dude
49
- end
50
- return result
51
- end
52
- end
53
-
54
-
55
-
56
-
57
-
58
- class NondominatedSubsetSelector < Sampler
59
-
60
- def generate(crowd, template = all_shared_scores(crowd))
61
- result = Batch.new
62
- crowd.each do |answer|
63
- dominated = false
64
- crowd.each do |other_answer|
65
- dominated ||= answer.dominated_by?(other_answer, template)
66
- end
67
- result << answer unless dominated
68
- end
69
- return result
70
- end
71
- end
72
-
73
-
74
-
75
-
76
-
77
- class DominatedQuantileSampler < Sampler
78
- def generate(crowd, proportion = 0.5, template = all_shared_scores(crowd))
79
- classified = domination_classes(crowd, template)
80
- increasing_grades = classified.keys.sort {|a,b| b <=> a}
81
- partial_ordering = []
82
- increasing_grades.each {|grade| partial_ordering += classified[grade]}
83
- how_many = (crowd.length * proportion).ceil
84
-
85
- result = Batch.new
86
- partial_ordering[0...how_many].each {|dude| result << dude} unless how_many == 0
87
- return result
88
- end
89
- end
90
-
91
-
92
-
93
-
94
-
95
- class MostDominatedSubsetSampler < Sampler
96
- def generate(crowd, template = all_shared_scores(crowd))
97
- result = Batch.new
98
- classified = domination_classes(crowd, template)
99
- worst_key = classified.keys.sort[-1]
100
- classified[worst_key].each {|bad_dude| result.push bad_dude}
101
- return result
102
- end
103
- end
104
-
105
-
106
-
107
-
108
-
109
- class AnyOneSampler < Sampler
110
- def generate(crowd)
111
- result = Batch[crowd.sample]
112
- end
113
- end
114
-
115
-
116
-
117
-
118
-
119
- class AllDuplicatedGenomesSampler < Sampler
120
- def generate(crowd)
121
- result = Batch.new
122
- clustered = diversity_classes(crowd)
123
- clustered.each do |blueprint, array|
124
- if array.length > 1
125
- result.concat array
126
- end
127
- end
128
- return result
129
- end
130
- end
131
- end