wallace 0.0.0
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/Gemfile +14 -0
- data/README.md +12 -0
- data/Rakefile +52 -0
- data/VERSION +1 -0
- data/bin/.gitkeep +0 -0
- data/lib/analysers/fitness_distribution_analyser.rb +28 -0
- data/lib/analysers/init.rb +1 -0
- data/lib/core/analyser.rb +63 -0
- data/lib/core/breeder.rb +68 -0
- data/lib/core/breeding_graph.rb +59 -0
- data/lib/core/breeding_graph/init.rb +3 -0
- data/lib/core/breeding_graph/input_node.rb +55 -0
- data/lib/core/breeding_graph/node.rb +68 -0
- data/lib/core/breeding_graph/node_input.rb +6 -0
- data/lib/core/evaluator.rb +52 -0
- data/lib/core/evolver.rb +47 -0
- data/lib/core/exceptions.rb +6 -0
- data/lib/core/experiment.rb +22 -0
- data/lib/core/fitness.rb +8 -0
- data/lib/core/fraction.rb +7 -0
- data/lib/core/individual.rb +65 -0
- data/lib/core/logger.rb +5 -0
- data/lib/core/migrator.rb +6 -0
- data/lib/core/operator.rb +54 -0
- data/lib/core/population.rb +56 -0
- data/lib/core/selector.rb +43 -0
- data/lib/core/species.rb +52 -0
- data/lib/core/state.rb +29 -0
- data/lib/core/subpopulation.rb +53 -0
- data/lib/core/termination.rb +39 -0
- data/lib/distributions/gaussian_distribution.rb +60 -0
- data/lib/distributions/init.rb +3 -0
- data/lib/fitness/init.rb +1 -0
- data/lib/fitness/raw_fitness.rb +30 -0
- data/lib/loggers/csv_logger.rb +20 -0
- data/lib/loggers/init.rb +1 -0
- data/lib/loggers/mongo_logger.rb +5 -0
- data/lib/loggers/sqlite_logger.rb +5 -0
- data/lib/modules/ge/backus_naur_form.rb +125 -0
- data/lib/modules/ge/grammar_derivation.rb +30 -0
- data/lib/modules/ge/grammar_species.rb +29 -0
- data/lib/modules/ge/init.rb +5 -0
- data/lib/modules/init.rb +2 -0
- data/lib/modules/koza/builder.rb +48 -0
- data/lib/modules/koza/builder/full_builder.rb +92 -0
- data/lib/modules/koza/builder/grow_builder.rb +103 -0
- data/lib/modules/koza/builder/half_builder.rb +70 -0
- data/lib/modules/koza/builder/init.rb +3 -0
- data/lib/modules/koza/ephemeral.rb +33 -0
- data/lib/modules/koza/init.rb +12 -0
- data/lib/modules/koza/koza_node.rb +108 -0
- data/lib/modules/koza/koza_node_value.rb +72 -0
- data/lib/modules/koza/koza_node_value_set.rb +51 -0
- data/lib/modules/koza/koza_species.rb +48 -0
- data/lib/modules/koza/koza_tree.rb +159 -0
- data/lib/modules/koza/operators/init.rb +4 -0
- data/lib/modules/koza/operators/subtree_crossover_operation.rb +43 -0
- data/lib/modules/koza/operators/subtree_mutation_operation.rb +32 -0
- data/lib/operators/bit_flip_mutation_operation.rb +29 -0
- data/lib/operators/boundary_mutation_operation.rb +28 -0
- data/lib/operators/cycle_crossover_operation.rb +77 -0
- data/lib/operators/gaussian_mutation_operation.rb +39 -0
- data/lib/operators/half_uniform_crossover_operation.rb +24 -0
- data/lib/operators/init.rb +26 -0
- data/lib/operators/merging_crossover_operation.rb +27 -0
- data/lib/operators/one_point_crossover_operation.rb +29 -0
- data/lib/operators/order_crossover_operation.rb +38 -0
- data/lib/operators/partially_mapped_crossover_operation.rb +44 -0
- data/lib/operators/point_mutation_operation.rb +31 -0
- data/lib/operators/position_crossover_operation.rb +50 -0
- data/lib/operators/reverse_sequence_mutation_operation.rb +13 -0
- data/lib/operators/shuffle_mutation_operation.rb +17 -0
- data/lib/operators/splice_crossover_operation.rb +42 -0
- data/lib/operators/subtour_exchange_crossover_operation.rb +54 -0
- data/lib/operators/swap_mutation_operation.rb +29 -0
- data/lib/operators/three_parent_crossover_operation.rb +16 -0
- data/lib/operators/two_point_crossover_operation.rb +31 -0
- data/lib/operators/twors_mutation_operation.rb +18 -0
- data/lib/operators/uniform_crossover_operation.rb +30 -0
- data/lib/operators/uniform_mutation_operation.rb +31 -0
- data/lib/operators/variable_one_point_crossover_operation.rb +80 -0
- data/lib/patches/enumerable.rb +85 -0
- data/lib/patches/init.rb +5 -0
- data/lib/patches/range.rb +13 -0
- data/lib/selectors/init.rb +5 -0
- data/lib/selectors/random_selector.rb +14 -0
- data/lib/selectors/roulette_selector.rb +23 -0
- data/lib/selectors/tournament_selector.rb +36 -0
- data/lib/species/array_species.rb +40 -0
- data/lib/species/bit_string_species.rb +18 -0
- data/lib/species/init.rb +4 -0
- data/lib/species/permutation_species.rb +22 -0
- data/lib/species/string_species.rb +29 -0
- data/lib/utility/init.rb +4 -0
- data/lib/utility/scaled_array.rb +88 -0
- data/lib/utility/sorted_array.rb +39 -0
- data/lib/wallace.rb +40 -0
- data/test/.gitkeep +0 -0
- metadata +248 -0
data/Gemfile
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
source 'http://rubygems.org'
|
|
2
|
+
|
|
3
|
+
gem 'peach'
|
|
4
|
+
|
|
5
|
+
# GraphViz - Great for Trees, Graph and Network visualisation.
|
|
6
|
+
#gem 'ruby-graphviz'
|
|
7
|
+
#gem 'win32-open3'
|
|
8
|
+
|
|
9
|
+
group :development do
|
|
10
|
+
gem 'jeweler', '~> 1.8.7'
|
|
11
|
+
gem 'rake'
|
|
12
|
+
gem 'rdoc'
|
|
13
|
+
gem 'bundler'
|
|
14
|
+
end
|
data/README.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
**Wallace.rb**
|
|
2
|
+
==========
|
|
3
|
+
|
|
4
|
+
A highly expressive and modular evolutionary toolkit for performing genetic algorithms, genetic programming, grammatical evolution and meta-evolution in Ruby.
|
|
5
|
+
|
|
6
|
+
**Tested with:**
|
|
7
|
+
* Ruby >= 1.9.1
|
|
8
|
+
* JRuby >= 1.7.8
|
|
9
|
+
|
|
10
|
+
**Tested platforms:**
|
|
11
|
+
* Windows 7 (x64)
|
|
12
|
+
* Ubuntu 13.10 (x64)
|
data/Rakefile
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Load all dependencies.
|
|
2
|
+
require 'rubygems'
|
|
3
|
+
require 'bundler'
|
|
4
|
+
require 'rake'
|
|
5
|
+
require 'rake/testtask'
|
|
6
|
+
require 'rdoc/task'
|
|
7
|
+
require 'jeweler'
|
|
8
|
+
|
|
9
|
+
begin
|
|
10
|
+
Bundler.setup(:default, :development)
|
|
11
|
+
rescue Bundler::BundlerError => e
|
|
12
|
+
$stderr.puts e.message
|
|
13
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
|
14
|
+
exit e.status_code
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Gem Specification.
|
|
18
|
+
Jeweler::Tasks.new do |gem|
|
|
19
|
+
gem.name = "wallace"
|
|
20
|
+
gem.homepage = "http://github.com/ChrisTimperley/Wallace.rb"
|
|
21
|
+
gem.license = "MIT"
|
|
22
|
+
gem.summary = "A powerful, flexible and modular toolkit for running Evolutionary Algorithms in Ruby"
|
|
23
|
+
gem.description = <<-EOF
|
|
24
|
+
Wallace is a powerful, flexible and modular toolkit for running Evolutionary Algorithms in Ruby.
|
|
25
|
+
Out of the box it provides support for steady-state EAs, genetic algorithms, genetic programming,
|
|
26
|
+
grammatical evolution and other types of EAs.
|
|
27
|
+
EOF
|
|
28
|
+
gem.email = "christimperley@gmail.com"
|
|
29
|
+
gem.author = "Chris Timperley"
|
|
30
|
+
gem.add_dependency "peach", "~> 0.5.1"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Gem Management.
|
|
34
|
+
Jeweler::RubygemsDotOrgTasks.new
|
|
35
|
+
|
|
36
|
+
# Unit Testing.
|
|
37
|
+
Rake::TestTask.new(:test) do |test|
|
|
38
|
+
test.libs << 'lib' << 'test'
|
|
39
|
+
test.pattern = 'test/**/test_*.rb'
|
|
40
|
+
test.verbose = true
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Documentation.
|
|
44
|
+
task :default => :test
|
|
45
|
+
Rake::RDocTask.new do |rdoc|
|
|
46
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
|
47
|
+
|
|
48
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
49
|
+
rdoc.title = "wallace #{version}"
|
|
50
|
+
rdoc.rdoc_files.include('README*')
|
|
51
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
52
|
+
end
|
data/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.0.0
|
data/bin/.gitkeep
ADDED
|
File without changes
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
class Wallace::Analysers::FitnessDistributionAnalyser < Wallace::Analyser
|
|
2
|
+
|
|
3
|
+
def initialize
|
|
4
|
+
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def after_generation(state)
|
|
8
|
+
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
protected
|
|
12
|
+
|
|
13
|
+
# Calculates the distribution of fitness values at the current evolution state.
|
|
14
|
+
#
|
|
15
|
+
# *Parameters:*
|
|
16
|
+
# * state, the current state of the evolution.
|
|
17
|
+
def calculate_distribution(state)
|
|
18
|
+
stats = {}
|
|
19
|
+
stats[:max] = state.population.fitness_max
|
|
20
|
+
stats[:min] = state.population.fitness_min
|
|
21
|
+
stats[:lq], stats[:median], stats[:uq] = state.population.fitness_quartiles
|
|
22
|
+
stats[:mean] = state.population.fitness_mean
|
|
23
|
+
stats[:std] =
|
|
24
|
+
stats[:var] =
|
|
25
|
+
return stats
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module Wallace::Analysers; end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Analysers are used to analyse the current state of the evolution at given stages.
|
|
2
|
+
# These objects may be used to gather data, calculate statistics and log information
|
|
3
|
+
# during the evolution.
|
|
4
|
+
#
|
|
5
|
+
# Analysers may harness the outputs of attached analysers to supplement their input data.
|
|
6
|
+
# This ability can be exploited to perform multi-stage post-processing and to efficiently
|
|
7
|
+
# record information.
|
|
8
|
+
class Wallace::Analyser
|
|
9
|
+
|
|
10
|
+
attr_reader :inputs
|
|
11
|
+
|
|
12
|
+
# Constructs a new analyser.
|
|
13
|
+
#
|
|
14
|
+
# *Parameters:*
|
|
15
|
+
# * inputs, the input analysers which feed data into this analyser.
|
|
16
|
+
def initialize(inputs)
|
|
17
|
+
@inputs = inputs
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Runs this analyser at a given stage in the evolution.
|
|
21
|
+
#
|
|
22
|
+
# Executes the input analysers to gather the input data which is then
|
|
23
|
+
# fed into the appropriate stage handler of this analyser.
|
|
24
|
+
#
|
|
25
|
+
# *Parameters:*
|
|
26
|
+
# * stage, the current stage in the evolution.
|
|
27
|
+
# * state, the current state of the evolution.
|
|
28
|
+
#
|
|
29
|
+
# *Returns:*
|
|
30
|
+
# Output data for processing by any attached analysers.
|
|
31
|
+
def run(stage, state)
|
|
32
|
+
input_data = @inputs.map { |i| i.run(stage, state) }
|
|
33
|
+
self.method(stage).invoke(state, input_data)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# All stage handlers are protected and should not be called directly!
|
|
37
|
+
# Instead the 'run' method must be used so that input data is gathered
|
|
38
|
+
# for the analyser.
|
|
39
|
+
protected
|
|
40
|
+
|
|
41
|
+
# This analysis hook is called prior to the start of each generation.
|
|
42
|
+
#
|
|
43
|
+
# *Parameters:*
|
|
44
|
+
# * state, the current state of the evolution.
|
|
45
|
+
# * input_data, the data provided by the attached input analysers.
|
|
46
|
+
def before_generation(state, input_data = nil); end
|
|
47
|
+
|
|
48
|
+
# This analysis hook is called after the end of a generation.
|
|
49
|
+
#
|
|
50
|
+
# *Parameters:*
|
|
51
|
+
# * state, the current state of the evolution.
|
|
52
|
+
# * input_data, the data provided by the attached input analysers.
|
|
53
|
+
def after_generation(state, input_data = nil); end
|
|
54
|
+
|
|
55
|
+
# This analysis hook is called upon termination of the evolution.
|
|
56
|
+
#
|
|
57
|
+
# *Parameters:*
|
|
58
|
+
# * state, the current state of the evolution.
|
|
59
|
+
# * input_data, the data provided by the attached input analysers.
|
|
60
|
+
def on_termination(state, input_data = nil); end
|
|
61
|
+
alias_method :after_run, :on_termination
|
|
62
|
+
|
|
63
|
+
end
|
data/lib/core/breeder.rb
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#
|
|
2
|
+
#
|
|
3
|
+
# The breeder is decoupled from the evolver, subpopulation and population models.
|
|
4
|
+
# This allows the breeder to be used in a number of different configurations, where
|
|
5
|
+
# one breeder is used per evolver, population or sub-population.
|
|
6
|
+
class Wallace::Breeder
|
|
7
|
+
|
|
8
|
+
attr_reader :elitism,
|
|
9
|
+
:graph,
|
|
10
|
+
:threads
|
|
11
|
+
|
|
12
|
+
# Constructs a new breeder.
|
|
13
|
+
#
|
|
14
|
+
# *Arguments*
|
|
15
|
+
# * opts, a hash of keyword options for this method.
|
|
16
|
+
# -> graph, the breeding graph for this breeder.
|
|
17
|
+
# -> elitism, the number (or fraction) of fittest individuals that should be carried over to the
|
|
18
|
+
# next generation. (default = 0).
|
|
19
|
+
# -> threads, the number of threads the breeding process is split across. (default = 1).
|
|
20
|
+
def initialize(opts = {})
|
|
21
|
+
@elitism = opts[:elitism]
|
|
22
|
+
@threads = opts[:threads] || 1
|
|
23
|
+
@graphs = Array.new(@threads) { opts[:graph].clone }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Breeds the next generation of individuals for the given subpopulation.
|
|
27
|
+
#
|
|
28
|
+
# *Parameters:*
|
|
29
|
+
# * rng, random number generation to use during breeding.
|
|
30
|
+
# * subpopulation, the target subpopulation.
|
|
31
|
+
def breed!(rng, subpopulation)
|
|
32
|
+
|
|
33
|
+
# Prepare the candidate list for each input for all graphs.
|
|
34
|
+
@graphs[0].inputs.each_index do |i|
|
|
35
|
+
candidates = subpopulation.contents
|
|
36
|
+
@graphs.each_index do |g|
|
|
37
|
+
candidates = @graphs[g].inputs[i].prepare!(candidates, random: rng, processed: g > 0)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Create a temporary array for the new sub-population.
|
|
42
|
+
size = subpopulation.contents.size
|
|
43
|
+
buffer = Array.new(size)
|
|
44
|
+
|
|
45
|
+
# Add the elite individuals to the end of the sub-population if elitism is enabled.
|
|
46
|
+
elites = @elitism.nil? ? 0 : @elitism
|
|
47
|
+
elites = elites.value * size if elites.is_a? Wallace::Fraction
|
|
48
|
+
buffer[(size - elites)...size] = subpopulation.contents.nmin(elites)
|
|
49
|
+
|
|
50
|
+
# Spread the breeding process across multiple threads.
|
|
51
|
+
# using a separate breeding graph for each thread to ensure thread-safety.
|
|
52
|
+
thread_size = (size/@threads.to_f).ceil
|
|
53
|
+
(0...@threads).peach(@threads) do |t|
|
|
54
|
+
range = t * thread_size
|
|
55
|
+
range = range...([size, range + thread_size].min)
|
|
56
|
+
|
|
57
|
+
@graphs[t].breed!(rng, buffer, range)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Clean each breeding graph.
|
|
61
|
+
@graphs.each { |g| g.clean! }
|
|
62
|
+
|
|
63
|
+
# Swap the contents of the sub-population with the buffer.
|
|
64
|
+
subpopulation.contents = buffer
|
|
65
|
+
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
class Wallace::BreedingGraph
|
|
2
|
+
|
|
3
|
+
attr_reader :outputs,
|
|
4
|
+
:inputs,
|
|
5
|
+
:nodes
|
|
6
|
+
|
|
7
|
+
# Constructs a new BreedingGraph.
|
|
8
|
+
#
|
|
9
|
+
# *Parameters:*
|
|
10
|
+
# * opts, a hash of keyword options.
|
|
11
|
+
# -> outputs, an array of terminal nodes in this breeding graph (can be weighted).
|
|
12
|
+
# -> buffered, flag indicating whether the process should be buffered.
|
|
13
|
+
def initialize(opts = {})
|
|
14
|
+
|
|
15
|
+
@outputs = opts[:outputs]
|
|
16
|
+
@buffered = opts[:buffered] || true
|
|
17
|
+
|
|
18
|
+
# Produce a list of nodes in the graph and find all inputs to the graph.
|
|
19
|
+
@nodes = []
|
|
20
|
+
@inputs = []
|
|
21
|
+
queue = @outputs.clone
|
|
22
|
+
|
|
23
|
+
until queue.empty?
|
|
24
|
+
node = queue.shift
|
|
25
|
+
@nodes << node
|
|
26
|
+
if node.is_a? Wallace::BreedingGraph::InputNode
|
|
27
|
+
@inputs << node
|
|
28
|
+
else
|
|
29
|
+
queue += node.inputs.map{ |i| i.source }
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Discards all temporary information regarding the last breeding
|
|
36
|
+
# cycle and clears all the buffers.
|
|
37
|
+
def clean!
|
|
38
|
+
@nodes.each { |n| n.clean! }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Breeds a portion of the associated given sub-population for the next generation.
|
|
42
|
+
#
|
|
43
|
+
# *Parameters:*
|
|
44
|
+
# * rng, the RNG to use when breeding individuals.
|
|
45
|
+
# * buffer, the buffer for the contents of the sub-population of the next generation.
|
|
46
|
+
# * range, the range of indices to be populated in the buffer by newly bred individuals.
|
|
47
|
+
def breed!(rng, buffer, range)
|
|
48
|
+
range.each { |i| buffer[i] = @outputs.sample(random: rng).take!(rng) }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Creates a clone of this breeding graph.
|
|
52
|
+
def clone
|
|
53
|
+
Wallace::BreedingGraph.new(
|
|
54
|
+
outputs: @outputs.map { |n| n.clone },
|
|
55
|
+
buffered: @buffered
|
|
56
|
+
)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Used to represent input nodes (i.e. sub-population selection nodes) in the
|
|
2
|
+
# breeding graph.
|
|
3
|
+
class Wallace::BreedingGraph::InputNode < Wallace::BreedingGraph::Node
|
|
4
|
+
|
|
5
|
+
# Constructs a new input node.
|
|
6
|
+
#
|
|
7
|
+
# *Parameters:*
|
|
8
|
+
# * selector, the selection method used by this input node.
|
|
9
|
+
def initialize(selector)
|
|
10
|
+
super(selector, [])
|
|
11
|
+
@candidates = []
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
alias :selector :function
|
|
15
|
+
|
|
16
|
+
# Selects a single individual from the list of candidates using the selection method
|
|
17
|
+
# associated with this node.
|
|
18
|
+
#
|
|
19
|
+
# *Parameters:*
|
|
20
|
+
# * rng, the RNG to use when selecting an individual from the list of candidates.
|
|
21
|
+
def take!(rng)
|
|
22
|
+
@function.produce(rng, @candidates).clone
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Prepares this input node for the next breeding cycle by supplying the contents
|
|
26
|
+
# of the sub-population and performing any necessary post-processing.
|
|
27
|
+
#
|
|
28
|
+
# To prevent redundant post-processing when using multiple threads, we can supply
|
|
29
|
+
# a prepared candidate list from the same input node from an other breeding graph.
|
|
30
|
+
#
|
|
31
|
+
# *Parameters:*
|
|
32
|
+
# * candidates, a list of (possibly post-processed) candidates for selection.
|
|
33
|
+
# * opts, a hash of keyword options for this method.
|
|
34
|
+
# -> random, the RNG to use when preparing the candidate list.
|
|
35
|
+
# -> processed, a flag indicating if this candidate list has already been post-processed.
|
|
36
|
+
#
|
|
37
|
+
# *Returns:*
|
|
38
|
+
# The prepared candidate list (so that it can be exploited by identical nodes in other graphs).
|
|
39
|
+
def prepare!(candidates, opts = {})
|
|
40
|
+
@candidates = opts[:processed] ? candidates.clone : @function.prepare(candidates, opts)
|
|
41
|
+
return @candidates
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Creates a clone of this node and its inputs.
|
|
45
|
+
def clone
|
|
46
|
+
Wallace::BreedingGraph::InputNode.new(@function)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Clears the contents of the buffer and candidates list for this input node.
|
|
50
|
+
def clean!
|
|
51
|
+
super
|
|
52
|
+
@candidates.clear
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Represents a node within the breeding graph.
|
|
2
|
+
#
|
|
3
|
+
# Each node corresponds to a stage within the breeding process and is associated with
|
|
4
|
+
# a given operator or selection method which is used on the inputs to that node to produce
|
|
5
|
+
# some individuals for the next generation or for further processing by successive nodes in
|
|
6
|
+
# the graph.
|
|
7
|
+
class Wallace::BreedingGraph::Node
|
|
8
|
+
|
|
9
|
+
attr_reader :function,
|
|
10
|
+
:inputs
|
|
11
|
+
|
|
12
|
+
# Constructs a node for the breeding graph.
|
|
13
|
+
#
|
|
14
|
+
# *Parameters:*
|
|
15
|
+
# * function, the operator or selection method associated to this node.
|
|
16
|
+
# * inputs, an array of inputs to this node.
|
|
17
|
+
def initialize(function, inputs)
|
|
18
|
+
@inputs = inputs
|
|
19
|
+
@function = function
|
|
20
|
+
@buffer = []
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Takes and returns a single individual produced (or selected) by this node.
|
|
24
|
+
#
|
|
25
|
+
# If there are no individuals held in the buffer then a number are produced
|
|
26
|
+
# (or selected) using the operator (or selection method) attached to this node.
|
|
27
|
+
#
|
|
28
|
+
# *Parameters:*
|
|
29
|
+
# * rng, the RNG to use if new individuals are bred.
|
|
30
|
+
#
|
|
31
|
+
# *Returns:*
|
|
32
|
+
# An array of individuals produced at this node.
|
|
33
|
+
def take!(rng)
|
|
34
|
+
@buffer += @function.produce(rng, generate_inputs!(rng)) if @buffer.empty?
|
|
35
|
+
return @buffer.pop
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Clears the buffer of this node and discards any temporary
|
|
39
|
+
# information relating to the breeding processs.
|
|
40
|
+
def clean!
|
|
41
|
+
@buffer.clear
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Creates a clone of this node and its inputs.
|
|
45
|
+
def clone
|
|
46
|
+
Wallace::BreedingGraph::Node.new(
|
|
47
|
+
@function,
|
|
48
|
+
@inputs.map { |i| Wallace::BreedingGraph::NodeInput.new(i.source.clone, i.size) }
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
protected
|
|
53
|
+
|
|
54
|
+
# Generates a set of individuals to use as input to this node using the information
|
|
55
|
+
# in the list of associated node inputs.
|
|
56
|
+
#
|
|
57
|
+
# *Parameters:*
|
|
58
|
+
# * rng, the RNG to use when producing individuals up the breeding chain.
|
|
59
|
+
#
|
|
60
|
+
# *Returns:*
|
|
61
|
+
# An array of individuals to use as input for this node.
|
|
62
|
+
def generate_inputs!(rng)
|
|
63
|
+
return @inputs.reduce([]) do |inds, input|
|
|
64
|
+
inds += Array.new(input.size) { input.source.take!(rng) }
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
end
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Used to represent inputs to a given node in the breeding graph.
|
|
2
|
+
#
|
|
3
|
+
# *Attributes:*
|
|
4
|
+
# * source, the source node to retrieve individuals from.
|
|
5
|
+
# * size, the number of individuals to retrieve from the source.
|
|
6
|
+
Wallace::BreedingGraph::NodeInput = Struct.new(:source, :size)
|