answer-factory 0.0.9 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -0
- data/Thorfile +9 -6
- data/VERSION +1 -1
- data/answer-factory.gemspec +20 -5
- data/lib/answer-factory.rb +14 -2
- data/lib/answers/answer.rb +17 -3
- data/lib/answers/batch.rb +15 -3
- data/lib/factories/workstation.rb +50 -1
- data/lib/operators/all_duplicated_genomes_sampler.rb +14 -0
- data/lib/operators/any_one_sampler.rb +7 -0
- data/lib/operators/dominated_quantile_selector.rb +16 -0
- data/lib/operators/infrastructure.rb +74 -0
- data/lib/operators/most_dominated_subset_sampler.rb +13 -0
- data/lib/operators/nondominated_subset_selector.rb +17 -0
- data/lib/operators/point_crossover_operator.rb +24 -0
- data/lib/operators/point_delete_operator.rb +19 -0
- data/lib/operators/point_mutation_operator.rb +22 -0
- data/lib/operators/program_point_count_evaluator.rb +14 -0
- data/lib/operators/random_guess_operator.rb +30 -0
- data/lib/operators/resample_and_clone_operator.rb +28 -0
- data/lib/operators/resample_values_operator.rb +40 -0
- data/lib/operators/{evaluators.rb → test_case_evaluator.rb} +3 -34
- data/lib/operators/uniform_backbone_crossover_operator.rb +53 -0
- data/readme.md +28 -3
- data/spec/answer_spec.rb +33 -1
- data/spec/batch_spec.rb +25 -12
- data/spec/factories/factory_spec.rb +53 -36
- data/spec/factories/workstation_spec.rb +194 -20
- data/spec/operators/evaluators/program_point_evaluator_spec.rb +1 -1
- data/spec/operators/evaluators/test_case_evaluator_spec.rb +2 -2
- data/spec/operators/nondominated_subset_spec.rb +8 -8
- data/spec/operators/random_guess_spec.rb +16 -11
- data/spec/operators/resample_and_clone_spec.rb +8 -8
- data/spec/operators/uniformBackboneCrossover_spec.rb +7 -7
- data/spec/spec_helper.rb +1 -0
- metadata +38 -12
- data/lib/operators/basic_operators.rb +0 -240
- 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",
|
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",
|
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",
|
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.
|
1
|
+
0.0.10
|
data/answer-factory.gemspec
CHANGED
@@ -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.
|
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-
|
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/
|
36
|
-
"lib/operators/
|
37
|
-
"lib/operators/
|
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"])
|
data/lib/answer-factory.rb
CHANGED
@@ -8,9 +8,21 @@ require 'couchrest'
|
|
8
8
|
require 'answers/answer'
|
9
9
|
require 'answers/batch'
|
10
10
|
|
11
|
-
require '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'
|
data/lib/answers/answer.rb
CHANGED
@@ -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.
|
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, "#{
|
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
|
-
|
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 =
|
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,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
|