answer-factory 0.1.2 → 0.1.3.4

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -12,7 +12,7 @@ begin
12
12
 
13
13
  gemspec.required_ruby_version = '>= 1.9.1'
14
14
 
15
- gemspec.add_dependency('nudge', '>= 0.2')
15
+ gemspec.add_dependency('nudge', '>= 0.2.9')
16
16
  gemspec.add_dependency('thor', '>= 0.13')
17
17
  gemspec.add_dependency('couchrest', '>= 0.33')
18
18
  gemspec.add_dependency('configatron', '>= 2.6.2')
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.2
1
+ 0.1.3.4
@@ -1,15 +1,15 @@
1
1
  # Generated by jeweler
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in rakefile, and run the gemspec command
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{answer-factory}
8
- s.version = "0.1.2"
8
+ s.version = "0.1.3.4"
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}
12
+ s.date = %q{2010-05-27}
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}
@@ -34,20 +34,25 @@ Gem::Specification.new do |s|
34
34
  "lib/machines/any_one.rb",
35
35
  "lib/machines/build_random.rb",
36
36
  "lib/machines/evaluate_simple_score.rb",
37
+ "lib/machines/evaluate_with_test_cases.rb",
37
38
  "lib/machines/infrastructure.rb",
38
39
  "lib/machines/mutate_footnotes.rb",
40
+ "lib/machines/select_by_summed_rank.rb",
39
41
  "lib/machines/select_nondominated.rb",
40
42
  "readme.md",
41
43
  "spec/answers/answer_spec.rb",
42
44
  "spec/answers/batch_spec.rb",
43
45
  "spec/factories/factory_spec.rb",
44
46
  "spec/factories/workstation_spec.rb",
47
+ "spec/fixtures/my_data_source.csv",
45
48
  "spec/integration_specs/couch_db_integration.rspec",
46
49
  "spec/machines/any_one_spec.rb",
47
50
  "spec/machines/build_random_spec.rb",
48
51
  "spec/machines/evaluate_simple_score_spec.rb",
52
+ "spec/machines/evaluate_with_test_cases_spec.rb",
49
53
  "spec/machines/infrastructure_spec.rb",
50
54
  "spec/machines/mutate_footnotes_spec.rb",
55
+ "spec/machines/select_by_summed_rank_spec.rb",
51
56
  "spec/machines/select_nondominated_spec.rb",
52
57
  "spec/spec_helper.rb",
53
58
  "tasks/setup_factory.thor",
@@ -58,7 +63,7 @@ Gem::Specification.new do |s|
58
63
  s.rdoc_options = ["--charset=UTF-8"]
59
64
  s.require_paths = ["lib"]
60
65
  s.required_ruby_version = Gem::Requirement.new(">= 1.9.1")
61
- s.rubygems_version = %q{1.3.6}
66
+ s.rubygems_version = %q{1.3.7}
62
67
  s.summary = %q{Genetic Programming in the Nudge language}
63
68
  s.test_files = [
64
69
  "spec/answers/answer_spec.rb",
@@ -68,8 +73,10 @@ Gem::Specification.new do |s|
68
73
  "spec/machines/any_one_spec.rb",
69
74
  "spec/machines/build_random_spec.rb",
70
75
  "spec/machines/evaluate_simple_score_spec.rb",
76
+ "spec/machines/evaluate_with_test_cases_spec.rb",
71
77
  "spec/machines/infrastructure_spec.rb",
72
78
  "spec/machines/mutate_footnotes_spec.rb",
79
+ "spec/machines/select_by_summed_rank_spec.rb",
73
80
  "spec/machines/select_nondominated_spec.rb",
74
81
  "spec/spec_helper.rb"
75
82
  ]
@@ -78,8 +85,8 @@ Gem::Specification.new do |s|
78
85
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
79
86
  s.specification_version = 3
80
87
 
81
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
82
- s.add_runtime_dependency(%q<nudge>, [">= 0.2"])
88
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
89
+ s.add_runtime_dependency(%q<nudge>, [">= 0.2.9"])
83
90
  s.add_runtime_dependency(%q<thor>, [">= 0.13"])
84
91
  s.add_runtime_dependency(%q<couchrest>, [">= 0.33"])
85
92
  s.add_runtime_dependency(%q<configatron>, [">= 2.6.2"])
@@ -87,7 +94,7 @@ Gem::Specification.new do |s|
87
94
  s.add_runtime_dependency(%q<sinatra>, [">= 0.9.4"])
88
95
  s.add_runtime_dependency(%q<activesupport>, [">= 2.3.5"])
89
96
  else
90
- s.add_dependency(%q<nudge>, [">= 0.2"])
97
+ s.add_dependency(%q<nudge>, [">= 0.2.9"])
91
98
  s.add_dependency(%q<thor>, [">= 0.13"])
92
99
  s.add_dependency(%q<couchrest>, [">= 0.33"])
93
100
  s.add_dependency(%q<configatron>, [">= 2.6.2"])
@@ -96,7 +103,7 @@ Gem::Specification.new do |s|
96
103
  s.add_dependency(%q<activesupport>, [">= 2.3.5"])
97
104
  end
98
105
  else
99
- s.add_dependency(%q<nudge>, [">= 0.2"])
106
+ s.add_dependency(%q<nudge>, [">= 0.2.9"])
100
107
  s.add_dependency(%q<thor>, [">= 0.13"])
101
108
  s.add_dependency(%q<couchrest>, [">= 0.33"])
102
109
  s.add_dependency(%q<configatron>, [">= 2.6.2"])
@@ -13,7 +13,9 @@ require 'machines/infrastructure'
13
13
  require 'machines/any_one'
14
14
  require 'machines/build_random'
15
15
  require 'machines/evaluate_simple_score'
16
+ require 'machines/evaluate_with_test_cases'
16
17
  require 'machines/select_nondominated'
18
+ require 'machines/select_by_summed_rank'
17
19
  require 'machines/mutate_footnotes'
18
20
 
19
21
 
@@ -26,6 +26,8 @@ module AnswerFactory
26
26
  @couchdb_server = options[:couchdb_server] ||
27
27
  configatron.factory.couchdb.retrieve(:server, nil) ||
28
28
  "http://127.0.0.1:5984"
29
+
30
+
29
31
 
30
32
  @couchdb_name = options[:couchdb_name] ||
31
33
  configatron.factory.couchdb.retrieve(:name, nil) ||
@@ -54,6 +56,7 @@ module AnswerFactory
54
56
  configatron.factory.workstation_names = @workstation_names
55
57
  configatron.factory.couchdb.server = @couchdb_server
56
58
  configatron.factory.couchdb.name = @couchdb_name
59
+ configatron.factory.training_datasource = self.training_datasource
57
60
  end
58
61
 
59
62
 
@@ -63,5 +66,9 @@ module AnswerFactory
63
66
  rescue StandardError
64
67
  false
65
68
  end
69
+
70
+ def training_datasource
71
+ "#{@couchdb_server}/#{@name}_training"
72
+ end
66
73
  end
67
74
  end
@@ -52,6 +52,22 @@ module AnswerFactory
52
52
  end
53
53
 
54
54
 
55
+ def couchdb_viewdoc
56
+ {'_id' => "_design/#{@name.to_s}",
57
+ views: {
58
+ current: {
59
+ map: "function(doc) { if(doc.location == '#{@name.to_s}') { emit(doc._id, doc); } }"
60
+ }
61
+ }
62
+ }
63
+ end
64
+
65
+
66
+ def couchdb_create_view(db_uri = couchdb_uri)
67
+ CouchRest.database!(db_uri).save_doc(self.couchdb_viewdoc, use_uuids=false)
68
+ end
69
+
70
+
55
71
  def ship!
56
72
  # Workstation is a superclass; the default behavior (doing nothing)
57
73
  # should be overridden in a subclass definition
@@ -0,0 +1,140 @@
1
+ #encoding: utf-8
2
+ module AnswerFactory
3
+ module Machines
4
+
5
+
6
+ class EvaluateWithTestCases < Machine
7
+ attr_accessor :sensors
8
+ attr_accessor :test_cases
9
+ attr_reader :name
10
+ attr_reader :csv_filename
11
+
12
+ def initialize(options = {})
13
+ super
14
+ @name = options[:name] || "evaluator"
15
+ @sensors = options[:sensors] || {}
16
+ @csv_filename = options[:training_data_csv]
17
+
18
+ self.load_training_data!
19
+ end
20
+
21
+
22
+ def score(batch, overridden_options = {})
23
+ all_options = @options.merge(overridden_options)
24
+ name = all_options[:name]
25
+
26
+
27
+ raise ArgumentError, "EvaluateWithTestCases#score cannot process a #{batch.class}" unless
28
+ batch.kind_of?(Batch)
29
+ raise ArgumentError, "EvaluateWithTestCases: Undefined #name attribute" if
30
+ name.nil?
31
+
32
+ load_training_data!
33
+
34
+ batch.each do |answer|
35
+
36
+ raw_results = Hash.new {|hash, key| hash[key] = []}
37
+
38
+ @test_cases.each do |t|
39
+
40
+ interpreter = Interpreter.new(answer.blueprint,all_options)
41
+
42
+ t.inputs.each do |variable_header, variable_value|
43
+ variable_name, variable_type = variable_header.split(":")
44
+ interpreter.bind_variable(variable_name, ValuePoint.new(variable_type, variable_value))
45
+ end
46
+
47
+ @sensors.each do |sensor_name, sensor_block|
48
+ interpreter.register_sensor(sensor_name, &sensor_block)
49
+ end
50
+
51
+ interpreter.run.each do |sensor_name, sensor_result|
52
+ raw_results[sensor_name] << (t.outputs[sensor_name].to_i - sensor_result)
53
+ end
54
+ end
55
+
56
+ @sensors.each do |sensor_name, sensor_block|
57
+ answer.scores[sensor_name] = raw_results[sensor_name].inject(0) do |sum, measurement|
58
+ sum + measurement.abs
59
+ end
60
+ end
61
+ end
62
+
63
+
64
+ return batch
65
+ end
66
+
67
+
68
+ def training_datasource
69
+ configatron.factory.training_datasource
70
+ end
71
+
72
+
73
+ def training_data_view
74
+ "#{configatron.factory.training_datasource}/_design/#{@name}/_view/test_cases"
75
+ end
76
+
77
+
78
+ def header_prep(header_string)
79
+ raise ArgumentError, "Header must match /reference_name:nudge_type/" unless
80
+ header_string.match /[\p{Alpha}][\p{Alnum}_]*:[\p{Alpha}][\p{Alnum}_]/
81
+ header_string.strip
82
+ end
83
+
84
+
85
+ def build_sensor(name, &block)
86
+ @sensors[name] = block
87
+ end
88
+
89
+
90
+ def install_training_data_from_csv(csv_filename = @csv_filename)
91
+ reader = CSV.new(File.open(csv_filename), headers: true)
92
+ reader.readline
93
+ split_point = reader.headers.find_index(nil)
94
+
95
+ input_headers = reader.headers[0...split_point].collect {|head| header_prep(head)}
96
+ output_headers = reader.headers[split_point+1..-1].collect {|head| head.strip}
97
+
98
+ reader.rewind
99
+
100
+ offset = input_headers.length+1
101
+ db = CouchRest.database!(training_datasource)
102
+
103
+ reader.each do |row|
104
+ inputs = {}
105
+ input_headers.each_with_index {|header,i| inputs[header] = row[i].strip}
106
+ outputs = {}
107
+ output_headers.each_with_index {|header,i| outputs[header] = row[i+offset].strip}
108
+ db.bulk_save_doc( {:inputs => inputs, :outputs => outputs})
109
+ end
110
+
111
+ db.bulk_save
112
+
113
+ end
114
+
115
+
116
+ def load_training_data!
117
+ db = CouchRest.database!(training_datasource)
118
+ result = db.view("#{@name}/test_cases")
119
+ @test_cases =
120
+ result["rows"].collect {|r| TestCase.new(
121
+ inputs: r["value"]["inputs"], outputs: r["value"]["outputs"])}
122
+ end
123
+
124
+
125
+ alias :generate :score
126
+ end
127
+
128
+
129
+
130
+
131
+ class TestCase
132
+ attr_accessor :inputs, :outputs
133
+
134
+ def initialize(options = {})
135
+ @inputs = options[:inputs] || {}
136
+ @outputs = options[:outputs] || {}
137
+ end
138
+ end
139
+ end
140
+ end
@@ -1,6 +1,8 @@
1
1
  module AnswerFactory
2
2
  module Machines
3
3
 
4
+ require 'csv'
5
+
4
6
  class Machine
5
7
 
6
8
  attr_reader :options
@@ -0,0 +1,54 @@
1
+ module AnswerFactory
2
+ module Machines
3
+
4
+
5
+
6
+
7
+ class SelectBySummedRank < Machine
8
+
9
+ def screen(batch, overriden_options={})
10
+ raise ArgumentError, "SelectBySummedRank#screen cannot process class #{batch.class}" unless
11
+ batch.kind_of?(Batch)
12
+ all_options = @options.merge(overriden_options)
13
+
14
+ criteria = all_options[:comparison_criteria] || shared_goals(batch)
15
+ return batch if criteria.empty?
16
+
17
+ scored = Hash.new(0)
18
+ incomparable = Set.new
19
+
20
+ criteria.each do |criterion|
21
+ scorable, unscorable = batch.partition {|a| a.scores.include?(criterion)}
22
+ incomparable += unscorable
23
+ ranked = scorable.sort_by {|a| a.scores[criterion]}
24
+ ranked.each_with_index do |a, index|
25
+ scored[a] += index
26
+ end
27
+ end
28
+
29
+ incomparable.each {|a| scored.delete(a)}
30
+
31
+ lowest_sum = scored.values.min
32
+ winners = batch.find_all {|a| scored[a] == lowest_sum }
33
+
34
+ result = Batch.new
35
+ incomparable.each {|a| result << a}
36
+
37
+ winners.each {|a| result << a unless result.include?(a)}
38
+ return result
39
+ end
40
+
41
+
42
+ def all_goals(batch)
43
+ (batch.collect {|a| a.scores.keys}).flatten.to_set.to_a
44
+ end
45
+
46
+
47
+ def shared_goals(batch)
48
+ batch.inject(all_goals(batch)) {|intersection, answer| intersection &= answer.scores.keys}
49
+ end
50
+
51
+ alias :generate :screen
52
+ end
53
+ end
54
+ end
@@ -159,7 +159,7 @@ describe "Factory" do
159
159
  :body => "Go away!", :status => [404, "Not Found"])
160
160
  Factory.couch_available?.should == false
161
161
 
162
- f1 = Factory.new(name:"boo", couchdb_server:"http://127.0.0.1:9991/place")
162
+ f1 = Factory.new(name:"boo", couchdb_server:"http://127.0.0.1:5984/place")
163
163
  Factory.couch_available?.should == false
164
164
  end
165
165
  end
@@ -174,6 +174,18 @@ describe "Factory" do
174
174
  it "should require authorization"
175
175
  end
176
176
 
177
+ describe "training data" do
178
+ before(:each) do
179
+ FakeWeb.allow_net_connect = false
180
+ end
181
+
182
+ it "should get the name of the :training_datasource from configatron" do
183
+ Factory.new(name:"baz", couchdb_server:"http://127.0.0.1:5984").
184
+ training_datasource.should == "http://127.0.0.1:5984/baz_training"
185
+ end
186
+
187
+ end
188
+
177
189
  describe "authorization" do
178
190
  it "should be necessary to set up an admin account in couch"
179
191
 
@@ -24,6 +24,25 @@ describe "Workstation" do
24
24
  end
25
25
  end
26
26
 
27
+ describe "couchdb views documents" do
28
+ it "should respond to :couchdb_viewdoc" do
29
+ Workstation.new(:foo).should respond_to(:couchdb_viewdoc)
30
+ end
31
+
32
+ it "should return a Hash" do
33
+ Workstation.new(:foo).couchdb_viewdoc.should be_a_kind_of(Hash)
34
+ end
35
+
36
+ it "should have the right _id value" do
37
+ Workstation.new(:foo).couchdb_viewdoc["_id"].should == "_design/foo"
38
+ end
39
+
40
+ it "should include the correct map code for this Workstation" do
41
+ Workstation.new(:foo).couchdb_viewdoc[:views][:current][:map].should ==
42
+ "function(doc) { if(doc.location == 'foo') { emit(doc._id, doc); } }"
43
+ end
44
+ end
45
+
27
46
 
28
47
  describe "capacity" do
29
48
  it "should accept a #capacity attribute in initialization" do
@@ -0,0 +1,4 @@
1
+ x1:int, x2:int, x3:int,,y1, y2
2
+ 1,2,3,, 9991, 8812
3
+ 2,3,4,, 88, -12
4
+ 3,4,5,, boo, far
@@ -15,7 +15,6 @@ end
15
15
 
16
16
  describe "CouchDB stuff" do
17
17
  before(:each) do
18
-
19
18
  configatron.factory.couchdb.server = "http://127.0.0.1:5984"
20
19
  configatron.factory.couchdb.name = "integration_test_db"
21
20
  break("CouchDB is offline") unless Factory.couch_available?
@@ -24,6 +23,7 @@ describe "CouchDB stuff" do
24
23
 
25
24
  after(:all) do
26
25
  CouchRest.database!(@db_uri).delete!
26
+ CouchRest.database!("#{@db_uri}_training").delete!
27
27
  end
28
28
 
29
29
 
@@ -37,7 +37,52 @@ describe "CouchDB stuff" do
37
37
  Factory.couch_available?.should be false
38
38
  FakeWeb.allow_net_connect = true
39
39
  end
40
+ end
41
+
42
+
43
+ describe "Machine CouchDB interaction" do
44
+ before(:each) do
45
+ the_database_name = "#{@db_uri}_training"
46
+ @db = CouchRest.database!(the_database_name)
47
+ end
40
48
 
49
+ it "should accept :install_training_data_from_csv to populate a db from csv" do
50
+ configatron.temp do
51
+ configatron.factory.training_datasource = "http://127.0.0.1:5984/integration_test_db_training"
52
+ @db.save_doc(
53
+ {'_id' => "_design/tester",
54
+ views: { test_cases: { map:
55
+ "function(doc) { emit(doc._id, doc); }"}}})
56
+
57
+ @my_machine = Machines::EvaluateWithTestCases.new(name: :tester)
58
+ @my_machine.install_training_data_from_csv('./spec/fixtures/my_data_source.csv')
59
+ saved = @db.documents
60
+ saved["rows"].length.should == 4
61
+ end
62
+ end
63
+
64
+ it "should strip whitespace from all the training value strings" do
65
+ configatron.temp do
66
+ configatron.factory.training_datasource = "http://127.0.0.1:5984/integration_test_db_training"
67
+ @my_machine = Machines::EvaluateWithTestCases.new(name: :tester)
68
+ @my_machine.install_training_data_from_csv('./spec/fixtures/my_data_source.csv')
69
+ first_doc = @db.get(@db.documents["rows"][0]["id"])
70
+ first_doc["outputs"].each {|k,v| k.strip.should == k}
71
+ first_doc["outputs"].each {|k,v| v.strip.should == v}
72
+
73
+ first_doc["inputs"].each {|k,v| k.strip.should == k}
74
+ first_doc["inputs"].each {|k,v| v.strip.should == v}
75
+ end
76
+ end
77
+
78
+ it "should use load_training_data! to access the right view" do
79
+ configatron.temp do
80
+ configatron.factory.training_datasource = "http://127.0.0.1:5984/integration_test_db_training"
81
+ @my_machine = Machines::EvaluateWithTestCases.new(name: :tester)
82
+ @my_machine.load_training_data!
83
+ @my_machine.test_cases[0].inputs["x1:int"].should == "3"
84
+ end
85
+ end
41
86
  end
42
87
 
43
88
  describe "Batch reading and writing" do
@@ -87,6 +132,19 @@ describe "CouchDB stuff" do
87
132
 
88
133
  describe "Workstations passing Batches around" do
89
134
 
135
+ describe "couchdb_create_view" do
136
+ before(:each) do
137
+ @ws_viewer = Workstation.new(:view_maker)
138
+ end
139
+
140
+ it "should save a view doc" do
141
+ response = @ws_viewer.couchdb_create_view
142
+ # "ok"=>true, "id"=>"_design/view_maker", "rev"=>"1-a813357133c1538c86168966b36f97bd"}
143
+ response["ok"].should == true
144
+ response["id"].should == "_design/view_maker"
145
+ end
146
+ end
147
+
90
148
  describe "gathering" do
91
149
  before(:each) do
92
150
  @ws1 = ShipperStation.new(:this_place)