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
data/Rakefile CHANGED
@@ -15,6 +15,7 @@ begin
15
15
  gemspec.add_dependency('nudge', '>= 0.2')
16
16
  gemspec.add_dependency('thor', '>= 0.13')
17
17
  gemspec.add_dependency('couchrest', '>= 0.33')
18
+ gemspec.add_dependency('configatron', '>= 2.6.2')
18
19
  gemspec.add_dependency('fakeweb', '>= 0.33')
19
20
  gemspec.add_dependency('sinatra', '>= 0.9.4')
20
21
  gemspec.add_dependency('activesupport', '>= 2.3.5')
data/Thorfile CHANGED
@@ -33,25 +33,28 @@ class New_Nudge_Type < Thor::Group
33
33
 
34
34
  def create_lib_file
35
35
  filename = "#{camelcased_type_name}.rb"
36
- template("#{nudge_gem_path}/templates/nudge_type_class.erb", "#{New_Nudge_Type.source_root}/lib/nudge/types/#{filename}")
36
+ template("#{nudge_gem_path}/templates/nudge_type_class.erb",
37
+ "#{New_Nudge_Type.source_root}/lib/nudge/types/#{filename}")
37
38
  end
38
-
39
+
39
40
  def create_lib_spec
40
41
  filename = "#{camelcased_type_name}_spec.rb"
41
- template("#{nudge_gem_path}/templates/nudge_type_spec.erb", "#{New_Nudge_Type.source_root}/spec/#{filename}")
42
+ template("#{nudge_gem_path}/templates/nudge_type_spec.erb",
43
+ "#{New_Nudge_Type.source_root}/spec/#{filename}")
42
44
  end
43
-
45
+
44
46
  def create_instructions
45
47
  suite = ["define", "equal_q", "duplicate", "flush", "pop",
46
48
  "random", "rotate", "shove", "swap", "yank", "yankdup"]
47
-
49
+
48
50
  suite.each do |inst|
49
51
  @core = "#{typename_root}_#{inst}"
50
52
  filename = "#{@core}.rb"
51
53
  @instname = "#{@core.camelize}Instruction"
52
54
  @type = typename_root
53
55
  @camelized_type = New_Nudge_Type.type_name(typename_root)
54
- template("#{nudge_gem_path}/templates/nudge_#{inst}_instruction.erb", "#{New_Nudge_Type.source_root}/lib/nudge/instructions/#{filename}")
56
+ template("#{nudge_gem_path}/templates/nudge_#{inst}_instruction.erb",
57
+ "#{New_Nudge_Type.source_root}/lib/nudge/instructions/#{filename}")
55
58
  end
56
59
  end
57
60
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.9
1
+ 0.0.10
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{answer-factory}
8
- s.version = "0.0.9"
8
+ s.version = "0.0.10"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Bill Tozier", "Trek Glowacki", "Jesse Sielaff"]
12
- s.date = %q{2010-05-02}
12
+ s.date = %q{2010-05-06}
13
13
  s.default_executable = %q{answer-factory}
14
14
  s.description = %q{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.}
15
15
  s.email = %q{bill@vagueinnovation.com}
@@ -32,9 +32,21 @@ Gem::Specification.new do |s|
32
32
  "lib/answers/batch.rb",
33
33
  "lib/factories/factory.rb",
34
34
  "lib/factories/workstation.rb",
35
- "lib/operators/basic_operators.rb",
36
- "lib/operators/evaluators.rb",
37
- "lib/operators/samplers_and_selectors.rb",
35
+ "lib/operators/all_duplicated_genomes_sampler.rb",
36
+ "lib/operators/any_one_sampler.rb",
37
+ "lib/operators/dominated_quantile_selector.rb",
38
+ "lib/operators/infrastructure.rb",
39
+ "lib/operators/most_dominated_subset_sampler.rb",
40
+ "lib/operators/nondominated_subset_selector.rb",
41
+ "lib/operators/point_crossover_operator.rb",
42
+ "lib/operators/point_delete_operator.rb",
43
+ "lib/operators/point_mutation_operator.rb",
44
+ "lib/operators/program_point_count_evaluator.rb",
45
+ "lib/operators/random_guess_operator.rb",
46
+ "lib/operators/resample_and_clone_operator.rb",
47
+ "lib/operators/resample_values_operator.rb",
48
+ "lib/operators/test_case_evaluator.rb",
49
+ "lib/operators/uniform_backbone_crossover_operator.rb",
38
50
  "pkg/nudgegp-0.0.1.gem",
39
51
  "readme.md",
40
52
  "spec/answer_spec.rb",
@@ -102,6 +114,7 @@ Gem::Specification.new do |s|
102
114
  s.add_runtime_dependency(%q<nudge>, [">= 0.2"])
103
115
  s.add_runtime_dependency(%q<thor>, [">= 0.13"])
104
116
  s.add_runtime_dependency(%q<couchrest>, [">= 0.33"])
117
+ s.add_runtime_dependency(%q<configatron>, [">= 2.6.2"])
105
118
  s.add_runtime_dependency(%q<fakeweb>, [">= 0.33"])
106
119
  s.add_runtime_dependency(%q<sinatra>, [">= 0.9.4"])
107
120
  s.add_runtime_dependency(%q<activesupport>, [">= 2.3.5"])
@@ -109,6 +122,7 @@ Gem::Specification.new do |s|
109
122
  s.add_dependency(%q<nudge>, [">= 0.2"])
110
123
  s.add_dependency(%q<thor>, [">= 0.13"])
111
124
  s.add_dependency(%q<couchrest>, [">= 0.33"])
125
+ s.add_dependency(%q<configatron>, [">= 2.6.2"])
112
126
  s.add_dependency(%q<fakeweb>, [">= 0.33"])
113
127
  s.add_dependency(%q<sinatra>, [">= 0.9.4"])
114
128
  s.add_dependency(%q<activesupport>, [">= 2.3.5"])
@@ -117,6 +131,7 @@ Gem::Specification.new do |s|
117
131
  s.add_dependency(%q<nudge>, [">= 0.2"])
118
132
  s.add_dependency(%q<thor>, [">= 0.13"])
119
133
  s.add_dependency(%q<couchrest>, [">= 0.33"])
134
+ s.add_dependency(%q<configatron>, [">= 2.6.2"])
120
135
  s.add_dependency(%q<fakeweb>, [">= 0.33"])
121
136
  s.add_dependency(%q<sinatra>, [">= 0.9.4"])
122
137
  s.add_dependency(%q<activesupport>, [">= 2.3.5"])
@@ -8,9 +8,21 @@ require 'couchrest'
8
8
  require 'answers/answer'
9
9
  require 'answers/batch'
10
10
 
11
- require 'operators/basic_operators'
11
+ require 'operators/infrastructure'
12
+ require 'operators/random_guess_operator'
13
+ require 'operators/resample_and_clone_operator'
14
+ require 'operators/resample_values_operator'
15
+ require 'operators/uniform_backbone_crossover_operator'
16
+ require 'operators/point_crossover_operator'
17
+ require 'operators/point_delete_operator'
18
+ require 'operators/point_mutation_operator'
19
+
20
+ require 'operators/test_case_evaluator'
21
+ require 'operators/program_point_count_evaluator'
22
+
23
+
24
+
12
25
  require 'operators/samplers_and_selectors'
13
- require 'operators/evaluators'
14
26
 
15
27
  require 'factories/factory'
16
28
  require 'factories/workstation'
@@ -4,7 +4,7 @@ module AnswerFactory
4
4
  class Answer
5
5
  attr_accessor :scores, :tags
6
6
  attr_reader :draft_blueprint, :program, :timestamp, :ancestors
7
- attr_reader :initialization_options, :progress
7
+ attr_reader :initialization_options, :progress, :couch_id
8
8
 
9
9
 
10
10
  def initialize(blueprint, options = {})
@@ -12,11 +12,12 @@ module AnswerFactory
12
12
  blueprint.kind_of?(String) || blueprint.kind_of?(NudgeProgram)
13
13
  build_from_blueprint!(blueprint)
14
14
 
15
- @scores = Hash.new do |hash, key|
15
+ @scores = options[:scores] || Hash.new do |hash, key|
16
16
  raise ArgumentError, "scores must use symbols as keys" unless key.kind_of?(Symbol)
17
17
  nil
18
18
  end
19
19
  @timestamp = Time.now
20
+ @couch_id = options[:couch_id] || ""
20
21
  @initialization_options = options
21
22
  @progress = options[:progress] || 0
22
23
  @ancestors = options[:ancestors] || []
@@ -121,6 +122,19 @@ module AnswerFactory
121
122
 
122
123
 
123
124
  def data
124
- {'blueprint' => self.blueprint, 'tags' => self.tags, 'scores' => self.scores, 'timestamp' => @timestamp}
125
+ {'blueprint' => self.blueprint, 'tags' => self.tags, 'scores' => self.scores, 'progress' => @progress, 'timestamp' => @timestamp}
126
+ end
127
+
128
+
129
+ def from_serial_hash(hash)
130
+ value_hash = hash["value"]
131
+ tag_set = Set.new(value_hash["tags"].collect {|t| t.to_sym})
132
+ symbolized_scores = value_hash["scores"].inject({}) {|memo,(k,v)| memo[k.to_sym] = v; memo }
133
+
134
+ Answer.new(value_hash["blueprint"],
135
+ couch_id:value_hash["_id"],
136
+ tags:tag_set,
137
+ scores:symbolized_scores,
138
+ progress:value_hash["progress"])
125
139
  end
126
140
  end
data/lib/answers/batch.rb CHANGED
@@ -34,11 +34,23 @@ module AnswerFactory
34
34
  end
35
35
 
36
36
 
37
- def self.load_tagged_answers(couchdb_uri, tag)
37
+ def self.load_from_couch(couchdb_uri, design_doc)
38
38
  raise ArgumentError, "#{couchdb_uri} is not a String" unless couchdb_uri.kind_of?(String)
39
- raise ArgumentError, "#{tag} is not a String" unless tag.kind_of?(String)
39
+ raise ArgumentError, "#{design_doc} is not a String" unless design_doc.kind_of?(String)
40
+
41
+ batch = Batch.new
42
+
40
43
  db = CouchRest.database(couchdb_uri) # add the view document and key here
41
- return Batch.new
44
+ begin
45
+ response = db.view(design_doc)
46
+ response["rows"].each do |hash|
47
+ puts hash["values"]
48
+ batch << Answer.from_serial_hash(hash)
49
+ end
50
+ rescue JSON::ParserError => e
51
+ puts "Batch not read due to JSON ParserError: '#{e.message}'"
52
+ end
53
+ return batch
42
54
  end
43
55
 
44
56
 
@@ -1,8 +1,10 @@
1
1
  module AnswerFactory
2
+
2
3
  class Workstation
3
4
  attr_reader :name, :capacity, :couchdb_uri, :factory_name
4
5
  attr_accessor :downstream_stations
5
6
  attr_accessor :answers
7
+ attr_accessor :build_sequence
6
8
 
7
9
 
8
10
  def initialize(name, options = {})
@@ -11,8 +13,9 @@ module AnswerFactory
11
13
  @factory_name = options[:factory_name] || 'factory_name'
12
14
  @couchdb_uri = options[:couchdb_uri] || "http://127.0.0.1:5984/#{@factory_name}"
13
15
  @capacity = options[:capacity] || 100
16
+ @build_sequence = options[:build_sequence] || Array.new
14
17
  @downstream_stations = Array.new
15
- @answers = Array.new
18
+ @answers = Batch.new
16
19
  end
17
20
 
18
21
 
@@ -23,6 +26,52 @@ module AnswerFactory
23
26
  end
24
27
 
25
28
 
29
+ def gather_mine
30
+ @answers += Batch.load_from_couch(@couchdb_uri, "#{name}/current")
31
+ end
32
+
33
+
34
+ def build!
35
+ # Workstation is a superclass; the default behavior (doing nothing)
36
+ # should be overridden in a subclass definition
37
+ end
38
+
39
+
40
+ def process_with(operator)
41
+ raise ArgumentError, "Workstation#process_with cannot process with a #{operator.class}" unless
42
+ operator.kind_of?(SearchOperator)
43
+ operator.generate(@answers)
44
+ end
45
+
46
+
47
+ def ship!
48
+ # Workstation is a superclass; the default behavior (doing nothing)
49
+ # should be overridden in a subclass definition
50
+ end
51
+
52
+
53
+ def ship_to(where, &filter)
54
+ raise ArgumentError, "Workstation#ship_to cannot ship to a #{where.class}" unless
55
+ where.kind_of?(Symbol)
56
+ (@answers.find_all &filter).each {|a| a.add_tag where; a.remove_tag @name}
57
+ end
58
+
59
+
60
+ def scrap!
61
+ # Workstation is a superclass; the default behavior (doing nothing)
62
+ # should be overridden in a subclass definition
63
+ end
64
+
65
+
66
+ def scrap_if(why, &filter)
67
+ (@answers.find_all &filter).each {|a| a.add_tag :SCRAP; a.remove_tag @name}
68
+ end
69
+
70
+ def scrap_everything
71
+ scrap_if("everything dies") {|x| true}
72
+ end
73
+
74
+
26
75
  def cycle
27
76
  self.receive!
28
77
  self.build!
@@ -0,0 +1,14 @@
1
+ module AnswerFactory
2
+ class AllDuplicatedGenomesSampler < Sampler
3
+ def generate(crowd)
4
+ result = Batch.new
5
+ clustered = diversity_classes(crowd)
6
+ clustered.each do |blueprint, array|
7
+ if array.length > 1
8
+ result.concat array
9
+ end
10
+ end
11
+ return result
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,7 @@
1
+ module AnswerFactory
2
+ class AnyOneSampler < Sampler
3
+ def generate(crowd)
4
+ result = Batch[crowd.sample]
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,16 @@
1
+ module AnswerFactory
2
+ class DominatedQuantileSampler < Sampler
3
+ def generate(crowd, proportion = 0.5, template = all_shared_scores(crowd))
4
+ classified = domination_classes(crowd, template)
5
+ increasing_grades = classified.keys.sort {|a,b| b <=> a}
6
+ partial_ordering = []
7
+ increasing_grades.each {|grade| partial_ordering += classified[grade]}
8
+ how_many = (crowd.length * proportion).ceil
9
+
10
+ result = Batch.new
11
+ partial_ordering[0...how_many].each {|dude| result << dude} unless how_many == 0
12
+ return result
13
+ end
14
+ end
15
+
16
+ end
@@ -0,0 +1,74 @@
1
+ module AnswerFactory
2
+
3
+ # Abstract class that from which specific SearchOperator subclasses inherit initialization
4
+
5
+ class SearchOperator
6
+ attr_accessor :incoming_options
7
+
8
+ def initialize(options={})
9
+ @incoming_options = options
10
+ end
11
+ end
12
+
13
+
14
+ class Evaluator < SearchOperator
15
+ attr_accessor :score_label
16
+
17
+ def initialize(params = {})
18
+ raise(ArgumentError, "Evaluators must have a score_label") if params[:score_label] == nil
19
+ @score_label = params[:score_label]
20
+ end
21
+ end
22
+
23
+
24
+ class Sampler < SearchOperator
25
+ def initialize (params = {})
26
+ super
27
+ end
28
+
29
+ def all_known_criteria(crowd)
30
+ union = []
31
+ crowd.each do |dude|
32
+ union |= dude.known_criteria
33
+ end
34
+ return union
35
+ end
36
+
37
+
38
+ def all_shared_scores(crowd)
39
+ intersection = self.all_known_criteria(crowd)
40
+ crowd.each do |dude|
41
+ intersection = intersection & dude.known_criteria
42
+ end
43
+ return intersection
44
+ end
45
+
46
+
47
+ def domination_classes(crowd, template = all_shared_scores(crowd))
48
+ result = Hash.new()
49
+
50
+ crowd.each_index do |i|
51
+ dominated_by = 0
52
+
53
+ crowd.each_index do |j|
54
+ dominated_by += 1 if crowd[i].dominated_by?(crowd[j], template)
55
+ end
56
+
57
+ result[dominated_by] ||= []
58
+ result[dominated_by].push crowd[i]
59
+ end
60
+
61
+ return result
62
+ end
63
+
64
+
65
+ def diversity_classes(crowd)
66
+ result = Hash.new()
67
+ crowd.each do |dude|
68
+ result[dude.program.tidy] ||= []
69
+ result[dude.program.tidy] << dude
70
+ end
71
+ return result
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,13 @@
1
+ module AnswerFactory
2
+
3
+ class MostDominatedSubsetSampler < Sampler
4
+ def generate(crowd, template = all_shared_scores(crowd))
5
+ result = Batch.new
6
+ classified = domination_classes(crowd, template)
7
+ worst_key = classified.keys.sort[-1]
8
+ classified[worst_key].each {|bad_dude| result.push bad_dude}
9
+ return result
10
+ end
11
+ end
12
+
13
+ end
@@ -0,0 +1,17 @@
1
+ module AnswerFactory
2
+ class NondominatedSubsetSelector < Sampler
3
+
4
+ def generate(crowd, template = all_shared_scores(crowd))
5
+ result = Batch.new
6
+ crowd.each do |answer|
7
+ dominated = false
8
+ crowd.each do |other_answer|
9
+ dominated ||= answer.dominated_by?(other_answer, template)
10
+ end
11
+ result << answer unless dominated
12
+ end
13
+ return result
14
+ end
15
+ end
16
+
17
+ end
@@ -0,0 +1,24 @@
1
+ module AnswerFactory
2
+ class PointCrossoverOperator < SearchOperator
3
+ def generate(crowd, howManyBabies = 1)
4
+ raise(ArgumentError) if !crowd.kind_of?(Array)
5
+ raise(ArgumentError) if crowd.empty?
6
+ crowd.each {|dude| raise(ArgumentError) if !dude.kind_of?(Answer) }
7
+
8
+ result = Batch.new
9
+ production = crowd.length*howManyBabies
10
+ production.times do
11
+ mom = crowd.sample
12
+ dad = crowd.sample
13
+ mom_receives = rand(mom.points) + 1
14
+ dad_donates = rand(dad.points) + 1
15
+
16
+ baby_blueprint = mom.replace_point_or_clone(mom_receives,dad.program[dad_donates])
17
+ baby = Answer.new(baby_blueprint,
18
+ progress:[mom.progress,dad.progress].max + 1)
19
+ result << baby
20
+ end
21
+ return result
22
+ end
23
+ end
24
+ end