revac 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (9) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +10 -0
  3. data/LICENSE +21 -0
  4. data/README.md +4 -0
  5. data/Rakefile +54 -0
  6. data/VERSION +1 -0
  7. data/lib/revac.rb +207 -0
  8. data/test/.gitkeep +0 -0
  9. metadata +112 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d7c4ca351b69ea97d4e61ce1a0e2a615204c22b4
4
+ data.tar.gz: fa2f20c8a5f837c9ebe29db0e95fdffb0f9733c0
5
+ SHA512:
6
+ metadata.gz: e78e574cde74e8822b3dbf17b4766e208cb63c87ad6c3cd5b6e64cbe506c5bfc87fc4d8635a60431189770246526a26552280e23e7510f156762ae2438ddf26b
7
+ data.tar.gz: a05be6bc722d77ddf0006e18f75eac50d906a5be3d468f7b84ac4b96274a2d0fa834caefe04a823a9f64ad5b820c52b856eb788690db205eefd4eab1383a1137
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ # encoding: utf-8
2
+
3
+ source 'http://rubygems.org'
4
+
5
+ group :development do
6
+ gem 'jeweler'
7
+ gem 'rake'
8
+ gem 'rdoc'
9
+ gem 'bundler'
10
+ end
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Chris Timperley
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,4 @@
1
+ RubyREVAC
2
+ =========
3
+
4
+ A Ruby-based implementation of the REVAC meta-heuristic tuner (Relevance Estimation and Value Calibration of Evolutionary Algorithm Parameters).
@@ -0,0 +1,54 @@
1
+ # encoding: utf-8
2
+
3
+ # Load all dependencies.
4
+ require 'rubygems'
5
+ require 'bundler'
6
+ require 'rake'
7
+ require 'rake/testtask'
8
+ require 'rdoc/task'
9
+ require 'jeweler'
10
+
11
+ begin
12
+ Bundler.setup(:default, :development)
13
+ rescue Bundler::BundlerError => e
14
+ $stderr.puts e.message
15
+ $stderr.puts "Run `bundle install` to install missing gems"
16
+ exit e.status_code
17
+ end
18
+
19
+ # Gem Specification.
20
+ Jeweler::Tasks.new do |gem|
21
+ gem.name = "revac"
22
+ gem.homepage = "http://github.com/ChrisTimperley/RubyREVAC"
23
+ gem.license = "MIT"
24
+ gem.summary = "Implementation of REVAC tuner for meta-heuristics (Relevance Estimation and Value Calibration
25
+ of Evolutionary Algorithm Parameters)."
26
+ gem.description = <<-EOF
27
+ An implementation of the REVAC tuner (Relevance Estimation and Value Calibration
28
+ of Evolutionary Algorithm Parameters) proposed by V. Nannen and A.E. Eiben, extended to work with arbitrary meta-heuristics
29
+ implemented on arbitrary platforms.
30
+ EOF
31
+ gem.email = "christimperley@gmail.com"
32
+ gem.author = "Chris Timperley"
33
+ end
34
+
35
+ # Gem Management.
36
+ Jeweler::RubygemsDotOrgTasks.new
37
+
38
+ # Unit Testing.
39
+ Rake::TestTask.new(:test) do |test|
40
+ test.libs << 'lib' << 'test'
41
+ test.pattern = 'test/**/test_*.rb'
42
+ test.verbose = true
43
+ end
44
+
45
+ # Documentation.
46
+ task :default => :test
47
+ Rake::RDocTask.new do |rdoc|
48
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
49
+
50
+ rdoc.rdoc_dir = 'rdoc'
51
+ rdoc.title = "revac #{version}"
52
+ rdoc.rdoc_files.include('README*')
53
+ rdoc.rdoc_files.include('lib/**/*.rb')
54
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,207 @@
1
+ # encoding: utf-8
2
+ require 'CSV' unless defined?(CSV)
3
+
4
+ # Implements the REVAC tuning method (Relevance Estimation and Value
5
+ # Calibration of Evolutionary Algorithm Parameters), proposed by Nannen and
6
+ # Eiben, which provides a fast and rational approach to parameter tuning for
7
+ # meta-heuristics.
8
+ #
9
+ # Although originally designed for finding the optimal parameter setup for
10
+ # evolutionary algorithms, this REVAC implementation may also be used to
11
+ # tune other meta-heuristics, such as Ant Colony Optimisation or Particle
12
+ # Swarm Optimisation.
13
+ module REVAC
14
+
15
+ # Used to hold the name and range of legal values for a search parameter.
16
+ Parameter = Struct.new(:name, :range)
17
+
18
+ # Logs a parameter vector and its associated utility function value using
19
+ # a given CSV file.
20
+ #
21
+ # ==== Parameters
22
+ # [+path+] The path to the output CSV file.
23
+ # [+evaluation+] The current evaluation.
24
+ # [+vector+] The parameter vector table.
25
+ # [+utility+] The utility of the given vector.
26
+ def self.log(path, evaluation, vector, utility)
27
+ CSV.open(path, 'ab') do |f|
28
+ f << [evaluation] + vector + [utility]
29
+ end
30
+ end
31
+
32
+ # Computes the utility (response) of a given parameter vector.
33
+ #
34
+ # ==== Parameters
35
+ # [+vector+] The parameter vector to evaluate.
36
+ # [+parameters+] The list of tunable parameters.
37
+ # [+algorithm+] A function which takes the a hash of parameter
38
+ # values as input, performs a given algorithm and
39
+ # returns the best fitness value found.
40
+ # [+runs+] The number of runs to use for each parameter
41
+ # vector.
42
+ def self.evaluate_vector(vector, parameters, algorithm, runs)
43
+
44
+ # Transform the vector into a hash of parameter names and
45
+ # settings and compute the mean best fitness using this
46
+ # parameter vector across a given number of runs.
47
+ vector = Hash[vector.to_a.each_with_index.map { |v, i|
48
+ [parameters[i].name, v]
49
+ }]
50
+ Array.new(runs) { algorithm[vector] }.inject(:+).to_f / runs
51
+
52
+ end
53
+
54
+ # Performs REVAC mutation on an individual at a given index within the vector
55
+ # table to produce a new mutated vector.
56
+ #
57
+ # ==== Parameters
58
+ # [+random+] The random number generator.
59
+ # [+table+] The vector table.
60
+ # [+index+] The index of the individual to mutate.
61
+ # [+h+] The radius of the partial marginal density function.
62
+ #
63
+ # ==== Returns
64
+ # The resulting vector.
65
+ def self.mutate(random, table, index, h)
66
+
67
+ # Sort the values of this parameter for all vectors into ascending order
68
+ # before calculating the upper and lower bounds of the mutation distribution
69
+ # and drawing a new parameter value at uniform random.
70
+ return Array.new(table[0].length) do |i|
71
+ window = (0...table.length).sort { |x, y| table[x][i] <=> table[y][i] }
72
+ position = window.find_index(index)
73
+ window = [
74
+ window[[position - h, 0].max],
75
+ window[[position + h, table.length - 1].min]
76
+ ]
77
+ window = table[window[0]][i] .. table[window[1]][i]
78
+ random.rand(window)
79
+ end
80
+
81
+ end
82
+
83
+ # Performs a multi-parent crossover on a list of parent chromosomes to produce
84
+ # a new chromosome.
85
+ #
86
+ # ==== Parameters
87
+ # [+random+] The random number generator.
88
+ # [+parents+] The parents of the child chromosome.
89
+ #
90
+ # ==== Returns
91
+ # The proto-child vector of the provided parent vectors.
92
+ def self.crossover(random, parents)
93
+ Array.new(parents[0].length) { |i| parents[random.rand(parents.length)][i] }
94
+ end
95
+
96
+ # Tunes a given algorithm using REVAC.
97
+ #
98
+ # ==== Parameters
99
+ # [+parameters+] A list of parameters to optimise. Each entry in the
100
+ # list contains the name of the parameter and the range
101
+ # of values that it can take.
102
+ # [+opts+] A hash of keyword options to this method.
103
+ # [+&algorithm+] A lambda function which takes a hash of named parameters
104
+ # as its input, uses them to perform a given algorithm,
105
+ # and returns the fitness of the best individual found.
106
+ #
107
+ # ==== Options
108
+ # [+evaluations+] The maximum number of evaluations to perform.
109
+ # [+parents+] The number of parents to use when creating each child.
110
+ # [+runs+] The number of runs to perform for each vector.
111
+ # [+vectors+] The number of parameter vectors in the population.
112
+ # [+h+] The radius of the partial marginal density function.
113
+ # [+log+] The path to the output CSV file.
114
+ # [+debug+] Used to enable / disable evaluation logging to the console.
115
+ #
116
+ # ==== Returns
117
+ # A hash containing the `optimal' parameter values for the given problem.
118
+ def self.tune(parameters, opts = {}, &algorithm)
119
+
120
+ # Load the default values for any omitted parameters.
121
+ opts[:vectors] ||= 80
122
+ opts[:parents] ||= 40
123
+ opts[:h] ||= 5
124
+ opts[:runs] ||= 5
125
+ opts[:evaluations] ||= 5000
126
+ opts[:debug] ||= false
127
+
128
+ # The RNG to use during optimisation.
129
+ random = Random.new
130
+
131
+ # Convert the list of parameter name/range pairs into a list of parameter
132
+ # objects.
133
+ parameters = parameters.map { |n, r| Parameter.new(n, r) }
134
+
135
+ # Initialise evolution statistics.
136
+ oldest = 0
137
+ iterations = 0
138
+ evaluations = 0
139
+
140
+ # Initialise the output CSV file (if one has been given).
141
+ CSV.open(opts[:log], 'wb') do |f|
142
+ f << ['Evaluation'] + parameters.map { |p| p.name } + ['Utility']
143
+ end if opts[:log]
144
+
145
+ # Draw an initial set of parameter vectors at uniform random from
146
+ # their initial distributions.
147
+ table = Array.new(opts[:vectors]) do
148
+ parameters.map { |p| random.rand(p.range) }
149
+ end
150
+
151
+ # Compute the utility of each parameter vector, before finding and recording
152
+ # the best parameter vector.
153
+ utility = table.map do |v|
154
+ u = evaluate_vector(v, parameters, algorithm, opts[:runs])
155
+ log(opts[:output], evaluations, v, u) if opts[:log]
156
+ puts "Evaluation #{evaluations}: #{u}" if opts[:debug]
157
+ evaluations += 1
158
+ u
159
+ end
160
+ best_utility, best_vector = utility.each_with_index.min
161
+ best_vector = table[best_vector]
162
+
163
+ # Keep optimising until the termination condition is met.
164
+ until evaluations == opts[:evaluations]
165
+
166
+ # Select the N-best vectors from the table as the parents
167
+ # of the next parameter vector.
168
+ parents = (0...opts[:vectors]).sort { |x, y|
169
+ utility[x] <=> utility[y]
170
+ }.take(opts[:parents]).map { |i|
171
+ table[i]
172
+ }
173
+
174
+ # Perform multi-parent crossover on the N parents to create
175
+ # a proto-child vector.
176
+ child = crossover(random, parents)
177
+
178
+ # Replace the oldest vector from the population with this
179
+ # proto-child vector, before mutating it and computing its
180
+ # utility.
181
+ table[oldest] = child
182
+ table[oldest] = mutate(random, table, oldest, opts[:h])
183
+ utility[oldest] = evaluate_vector(child, parameters, algorithm, opts[:runs])
184
+
185
+ # Update the best vector if the child vector is an improvement.
186
+ if utility[oldest] < best_utility
187
+ best_vector = table[oldest]
188
+ best_utility = utility[oldest]
189
+ end
190
+
191
+ # Update evolution statistics and perform logging.
192
+ iterations += 1
193
+ evaluations += 1
194
+ log(opts[:output], evaluations, child, utility[oldest]) if opts[:log]
195
+ puts "Evaluation #{evaluations}: #{utility[oldest]}" if opts[:debug]
196
+ oldest = (oldest + 1) % opts[:vectors]
197
+
198
+ end
199
+
200
+ # Return the optimal parameter vector as a hash.
201
+ return Hash[parameters.zip(0...parameters.length).map { |param, index|
202
+ [param.name, best_vector[index]]
203
+ }]
204
+
205
+ end
206
+
207
+ end
File without changes
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: revac
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Chris Timperley
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: jeweler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rdoc
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: |2
70
+ An implementation of the REVAC tuner (Relevance Estimation and Value Calibration
71
+ of Evolutionary Algorithm Parameters) proposed by V. Nannen and A.E. Eiben, extended to work with arbitrary meta-heuristics
72
+ implemented on arbitrary platforms.
73
+ email: christimperley@gmail.com
74
+ executables: []
75
+ extensions: []
76
+ extra_rdoc_files:
77
+ - LICENSE
78
+ - README.md
79
+ files:
80
+ - Gemfile
81
+ - LICENSE
82
+ - README.md
83
+ - Rakefile
84
+ - VERSION
85
+ - lib/revac.rb
86
+ - test/.gitkeep
87
+ homepage: http://github.com/ChrisTimperley/RubyREVAC
88
+ licenses:
89
+ - MIT
90
+ metadata: {}
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubyforge_project:
107
+ rubygems_version: 2.0.14
108
+ signing_key:
109
+ specification_version: 4
110
+ summary: Implementation of REVAC tuner for meta-heuristics (Relevance Estimation and
111
+ Value Calibration of Evolutionary Algorithm Parameters).
112
+ test_files: []