answer-factory 0.1.2 → 0.1.3.4

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.
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)