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
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# The swap mutation operator swaps pairs of consecutive genes according to
|
|
2
|
+
# an associated probability.
|
|
3
|
+
class Wallace::Operators::SwapMutationOperator < Wallace::Operator
|
|
4
|
+
|
|
5
|
+
name = :swap_mutation
|
|
6
|
+
|
|
7
|
+
# Allow the swap probability to be dynamically adjusted.
|
|
8
|
+
attr_accessor :probability
|
|
9
|
+
|
|
10
|
+
# Constructs a new swap mutation operator.
|
|
11
|
+
#
|
|
12
|
+
# *Parameters:*
|
|
13
|
+
# * opts, a hash of keyword options for this method.
|
|
14
|
+
# -> id, the unique identifier for this operator.
|
|
15
|
+
# -> inputs, an array of inputs (OperatorInput) to this operator.
|
|
16
|
+
# -> probability, the probability that a given pair (of consecutive genes) should be swapped. (default = 0.05).
|
|
17
|
+
def initialize(opts = {})
|
|
18
|
+
super(opts)
|
|
19
|
+
@probability = opts[:probability] || 0.05
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def operate(rng, inputs)
|
|
23
|
+
(0...inputs[0].length).each_cons(2) do |g1, g2|
|
|
24
|
+
inputs[0][g1], inputs[0][g2] = inputs[0][g2], inputs[0][g1] if rng.rand <= @probability
|
|
25
|
+
end
|
|
26
|
+
return inputs
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Three parent crossover takes three individuals as parents, A, B and C, and
|
|
2
|
+
# combines them to form a single child D. At each point in the chromosome, the
|
|
3
|
+
# values of A and B are compared; if they are the same then that value is used,
|
|
4
|
+
# otherwise the value at C is used.
|
|
5
|
+
class Wallace::Operators::ThreeParentCrossoverOperator < Wallace::Operator
|
|
6
|
+
|
|
7
|
+
name = :three_parent_crossover
|
|
8
|
+
|
|
9
|
+
def operate(rng, inputs)
|
|
10
|
+
(0...inputs[0].length).each do |i|
|
|
11
|
+
inputs[0][i] = inputs[2][i] if inputs[0][i] != inputs[1][i]
|
|
12
|
+
end
|
|
13
|
+
return [inputs[0]]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Two-point crossover takes two individuals, A and B, as parents, and produces two children by
|
|
2
|
+
# swapping the substrings between loci, X and Y.
|
|
3
|
+
#
|
|
4
|
+
# Two crossover points, X and Y, are chosen such that X and Y both lie within the bounds of A and B
|
|
5
|
+
# and that X < Y. The substrings A[X...Y] and B[X...Y] are swapped to produce two (potentially) new
|
|
6
|
+
# children strings.
|
|
7
|
+
class Wallace::Operators::TwoPointCrossoverOperator < Wallace::Operator
|
|
8
|
+
|
|
9
|
+
name = :two_point_crossover
|
|
10
|
+
|
|
11
|
+
def operate(rng, inputs)
|
|
12
|
+
|
|
13
|
+
# Ensure that the individuals are longer than length 2!
|
|
14
|
+
return inputs if inputs[0].length <= 2 or inputs[1].length <= 2
|
|
15
|
+
|
|
16
|
+
# Calculate the crossover points X and Y then swap the substrings
|
|
17
|
+
# between A and B at those loci.
|
|
18
|
+
x = rng.rand((1...([inputs[0].length, inputs[1].length].min-1)))
|
|
19
|
+
y = rng.rand((x...([inputs[0].length, inputs[1].length].min)))
|
|
20
|
+
|
|
21
|
+
# Unfortunately []= can not be performed in parallel so we must
|
|
22
|
+
# find and store a substring before swapping them.
|
|
23
|
+
t = inputs[0][x...y]
|
|
24
|
+
inputs[0][x...y] = inputs[1][x...y]
|
|
25
|
+
inputs[1][x...y] = t
|
|
26
|
+
|
|
27
|
+
return inputs
|
|
28
|
+
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# The Twors Mutation operator takes an individual and swaps the values
|
|
2
|
+
# of two genes selected at random.
|
|
3
|
+
#
|
|
4
|
+
# Found in:
|
|
5
|
+
# => Analyzing the Performance of Mutation Operators to Solve the Travelling Salesman Problem
|
|
6
|
+
# => Otman ABDOUN, Jaafar ABOUCHABAKA, Chakir TAJANI
|
|
7
|
+
|
|
8
|
+
class Wallace::Operators::TworsMutationOperator < Wallace::Operator
|
|
9
|
+
|
|
10
|
+
name = :twors_mutation
|
|
11
|
+
|
|
12
|
+
def operate(rng, inputs)
|
|
13
|
+
i, j = (0...inputs[0].length).to_a.sample(2)
|
|
14
|
+
inputs[0][i], inputs[0][j] = inputs[0][j], inputs[0][i]
|
|
15
|
+
return inputs
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Uniform crossover takes two individuals as parents, A and B, and produces two children, C and D,
|
|
2
|
+
# by swapping genes according to the mixing ratio probability. Using a mixing ratio of 0.5 should
|
|
3
|
+
# result in individuals which share half of their genes with a given parent.
|
|
4
|
+
class Wallace::Operators::UniformCrossoverOperator < Wallace::Operator
|
|
5
|
+
|
|
6
|
+
name = :uniform_crossover
|
|
7
|
+
|
|
8
|
+
# Allow the mixing ratio to be dynamically adjusted.
|
|
9
|
+
attr_accessor :ratio
|
|
10
|
+
|
|
11
|
+
# Constructs a new uniform crossover operator.
|
|
12
|
+
#
|
|
13
|
+
# *Parameters:*
|
|
14
|
+
# * opts, a hash of keyword options for this method.
|
|
15
|
+
# -> id, the unique identifier for this operator.
|
|
16
|
+
# -> inputs, an array of inputs (OperatorInput) to this operator.
|
|
17
|
+
# -> ratio, the mixing ratio to use during crossover. (default = 0.5).
|
|
18
|
+
def initialize(opts = {})
|
|
19
|
+
super(opts)
|
|
20
|
+
@ratio = opts[:ratio] || 0.5
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def operate(rng, inputs)
|
|
24
|
+
(0...inputs[0].length).each do |i|
|
|
25
|
+
inputs[0][i], inputs[1][i] = inputs[1][i], inputs[0][i] if rng.rand <= @ratio
|
|
26
|
+
end
|
|
27
|
+
return inputs
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
class Wallace::Operators::UniformMutationOperator < Wallace::Operator
|
|
2
|
+
|
|
3
|
+
name = :uniform_mutation
|
|
4
|
+
|
|
5
|
+
# Allow the mutation probability to be dynamically adjusted.
|
|
6
|
+
attr_accessor :probability
|
|
7
|
+
|
|
8
|
+
# Constructs a new uniform mutation operator.
|
|
9
|
+
#
|
|
10
|
+
# *Parameters:*
|
|
11
|
+
# * opts, a hash of keyword options for this method.
|
|
12
|
+
# -> id, the unique identifier for this operator.
|
|
13
|
+
# -> inputs, an array of inputs (OperatorInput) to this operator.
|
|
14
|
+
# -> values, the range of values that this mutator should sample from (can be biased by using a weighted array).
|
|
15
|
+
# -> probability, the probability that a given bit should be mutated. (default = 0.01).
|
|
16
|
+
def initialize(opts = {})
|
|
17
|
+
super(opts)
|
|
18
|
+
@values = opts[:values]
|
|
19
|
+
@probability = opts[:probability] || 0.01
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def operate(rng, inputs)
|
|
23
|
+
(0...inputs[0].length).each do |i|
|
|
24
|
+
if rng.rand <= @probability
|
|
25
|
+
inputs[0][i] = @values.sample(random: rng)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
return inputs
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# The variable one point crossover operator was designed to address the problem of bloat
|
|
2
|
+
# resulting from the crossover on variable length chromosomes using the splice crossover
|
|
3
|
+
# operator.
|
|
4
|
+
#
|
|
5
|
+
# This operator takes two chromosomes as its input, X and Y, and slices each into two
|
|
6
|
+
# subsequences, A and B, and C and D, respectively. The first subsequence of X (A) is
|
|
7
|
+
# combined with the last subsequence of Y (D) to form one of the child chromosomes.
|
|
8
|
+
# The second child is formed by combining the first subsequence of Y (C) with the last
|
|
9
|
+
# subsequence of X (B).
|
|
10
|
+
#
|
|
11
|
+
# The subsequences are chosen at random from the set of legal sub-sequences. More
|
|
12
|
+
# details on the process are given in "operate".
|
|
13
|
+
#
|
|
14
|
+
# The key feature of this operator is that it guarantees that the length of the child
|
|
15
|
+
# chromosomes will fall within some given bounds. This feature helps to control the
|
|
16
|
+
# problem of bloat in problems using variable length chromosomes and can significantly
|
|
17
|
+
# improve performance in certain cases.
|
|
18
|
+
#
|
|
19
|
+
# *WARNING:* Operates on the assumption that the input individuals obey the length constraints.
|
|
20
|
+
# *WARNING:* The length of each input chromosome must be at least 2, failing this the original
|
|
21
|
+
# inputs are simply returned.
|
|
22
|
+
#
|
|
23
|
+
# *Author:* Chris Timperley
|
|
24
|
+
class Wallace::Operators::VariableOnePointCrossoverOperator < Wallace::Operator
|
|
25
|
+
|
|
26
|
+
attr_accessor :bounds
|
|
27
|
+
|
|
28
|
+
# Constructs a new variable one point crossover operator.
|
|
29
|
+
#
|
|
30
|
+
# *Parameters:*
|
|
31
|
+
# * opts, a hash of keyword options for this constructor.
|
|
32
|
+
# -> id, the unique identifier for this operator.
|
|
33
|
+
# -> bounds, the bounds upon the length of child chromosomes produced by this operator.
|
|
34
|
+
def initialize(opts = {})
|
|
35
|
+
super(opts)
|
|
36
|
+
@bounds = opts[:bounds]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Produces two child chromosomes using two parent chromosomes according to the
|
|
40
|
+
# rules of this genetic operator.
|
|
41
|
+
#
|
|
42
|
+
# *Parameters:*
|
|
43
|
+
# * rng, the RNG to use to select the crossover point.
|
|
44
|
+
# * parents, the parent chromosomes used in the operation.
|
|
45
|
+
#
|
|
46
|
+
# *Returns:*
|
|
47
|
+
# An array containing two child chromosomes formed using data from the parent chromosomes.
|
|
48
|
+
def operate(rng, parents)
|
|
49
|
+
|
|
50
|
+
# Calculate the length of each parent.
|
|
51
|
+
x, y = parents[0].length, parents[1].length
|
|
52
|
+
|
|
53
|
+
# If either parent chromosome is shorter than length 2 then return them as the child
|
|
54
|
+
# chromosomes (else the operation will fail).
|
|
55
|
+
return parents if x < 2 or y < 2
|
|
56
|
+
|
|
57
|
+
# Pseudo-randomly select the first sub-sequence of X (A) such that
|
|
58
|
+
# 1 <= |A| < |X|. Let B be the remaining part of X.
|
|
59
|
+
a = rng.rand(1...x)
|
|
60
|
+
b = x - a
|
|
61
|
+
|
|
62
|
+
# Using the chosen A, find all the possible values of |D| such that
|
|
63
|
+
# MIN <= |A| + |D| <= MAX. Restrict the set of possible values for |D|
|
|
64
|
+
# to those which produce a legal |C| (i.e. MIN <= |B| + |C| <= MAX)
|
|
65
|
+
# before sampling a value uniformly at random.
|
|
66
|
+
d = ([1, @bounds.min - a].max..[y, @bounds.max - a].min).to_a.select{ |v|
|
|
67
|
+
@bounds.cover?(b + y - v)
|
|
68
|
+
}.sample(random: rng)
|
|
69
|
+
c = y - d
|
|
70
|
+
|
|
71
|
+
# Gather and combine the subsequences into the child chromosomes using
|
|
72
|
+
# their calculated lengths.
|
|
73
|
+
return [
|
|
74
|
+
parents[0].first(a) + parents[1].last(d),
|
|
75
|
+
parents[1].first(c) + parents[0].last(b)
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
module Enumerable
|
|
2
|
+
|
|
3
|
+
# Returns an array of all consecutive k-tuples in the collection.
|
|
4
|
+
#
|
|
5
|
+
# *Parameters:*
|
|
6
|
+
# * k, the size of the tuples.
|
|
7
|
+
def cons(k)
|
|
8
|
+
k_tuples = []
|
|
9
|
+
each_cons(k) { |tuple| k_tuples << tuple }
|
|
10
|
+
return k_tuples
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Checks whether this collection does not contain a given value V.
|
|
14
|
+
#
|
|
15
|
+
# *Parameters:*
|
|
16
|
+
# * v, the value to check for.
|
|
17
|
+
#
|
|
18
|
+
# *Returns:*
|
|
19
|
+
# true if not contained, false if contained.
|
|
20
|
+
def exclude?(v)
|
|
21
|
+
not include?(v)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Calculates and returns the N highest values from this collection.
|
|
25
|
+
#
|
|
26
|
+
# *Parameters:*
|
|
27
|
+
# * n, number of values to return.
|
|
28
|
+
def nmax(n)
|
|
29
|
+
result = [] # lowest to highest
|
|
30
|
+
each do |v|
|
|
31
|
+
result = result.sorted_insert!(v).last(n) if result.length < n or v > result.first
|
|
32
|
+
end
|
|
33
|
+
return result
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Calculates and returns the N lowest values from this collection.
|
|
37
|
+
#
|
|
38
|
+
# *Parameters:*
|
|
39
|
+
# * n, number of values to return.
|
|
40
|
+
def nmin(n)
|
|
41
|
+
result = [] # lowest to highest
|
|
42
|
+
each do |v|
|
|
43
|
+
result = result.sorted_insert!(v).last(n) if result.length < n or v < result.first
|
|
44
|
+
end
|
|
45
|
+
return result
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Inserts a given value into this collection, assuming that the
|
|
49
|
+
# collection is sorted.
|
|
50
|
+
#
|
|
51
|
+
# *Parameters:*
|
|
52
|
+
# * value, the value to insert into the collection.
|
|
53
|
+
#
|
|
54
|
+
# *Returns:*
|
|
55
|
+
# The resulting collection.
|
|
56
|
+
def sorted_insert!(value)
|
|
57
|
+
index = calculate_sorted_index(value)
|
|
58
|
+
insert(index, value)
|
|
59
|
+
return self
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
# Calculates the index of a given value within this sorted collection.
|
|
65
|
+
#
|
|
66
|
+
# *Parameter:*
|
|
67
|
+
# * value, the value to calculate the index of.
|
|
68
|
+
#
|
|
69
|
+
# *Returns:*
|
|
70
|
+
# * the index of that value within this collection.
|
|
71
|
+
#
|
|
72
|
+
# *TODO:*
|
|
73
|
+
# * This is a rubbish O(n) implementation!
|
|
74
|
+
# * A RB-binary tree or a better insertion sort method should
|
|
75
|
+
# be implemented.
|
|
76
|
+
def calculate_sorted_index(value)
|
|
77
|
+
i = 0
|
|
78
|
+
each do |v|
|
|
79
|
+
break if v > value
|
|
80
|
+
i += 1
|
|
81
|
+
end
|
|
82
|
+
return i
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
end
|
data/lib/patches/init.rb
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class Range
|
|
2
|
+
|
|
3
|
+
# Samples a value from within this range.
|
|
4
|
+
#
|
|
5
|
+
# *Parameters:*
|
|
6
|
+
# * opts, keyword options for this method.
|
|
7
|
+
# * => random, the RNG to use to sample from this range.
|
|
8
|
+
def sample(opts = {})
|
|
9
|
+
random = opts[:random] || Random.new
|
|
10
|
+
random.rand(self)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
class Wallace::Selectors::RandomSelector < Wallace::Selector
|
|
2
|
+
|
|
3
|
+
name = :random
|
|
4
|
+
|
|
5
|
+
# Selects an individual at random from the list of candidates.
|
|
6
|
+
#
|
|
7
|
+
# *Arguments*
|
|
8
|
+
# * rng, random number generator to use to select index of individual.
|
|
9
|
+
# * candidates, the list of candidates to select from.
|
|
10
|
+
def produce(rng, candidates)
|
|
11
|
+
candidates.sample(random: rng)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# NOT READY!
|
|
2
|
+
class Wallace::Selectors::RouletteSelector < Wallace::Selector
|
|
3
|
+
|
|
4
|
+
name = :roulette
|
|
5
|
+
|
|
6
|
+
# Precomputes the probability distribution prior to selection.
|
|
7
|
+
def prepare(rng, candidates)
|
|
8
|
+
sum_fitness = candidates.reduce(0) {|sum, ind| sum += 1.0 / ind.fitness.value.to_f }
|
|
9
|
+
distribution = Array.new(candidates.length) do |i|
|
|
10
|
+
(1.0 / candidates[i].fitness.value.to_f) / sum_fitness
|
|
11
|
+
end
|
|
12
|
+
@candidates = Wallace::Utility::ScaledArray.new(candidates, distribution)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Selects an individual at random from the list of candidates.
|
|
16
|
+
#
|
|
17
|
+
# *Arguments*
|
|
18
|
+
# * rng, random number generator to use to select index of individual.
|
|
19
|
+
def select(rng)
|
|
20
|
+
return @candidates.sample(random: rng)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
class Wallace::Selectors::TournamentSelector < Wallace::Selector
|
|
2
|
+
|
|
3
|
+
name = :tournament
|
|
4
|
+
|
|
5
|
+
# Constructs a new tournament selector.
|
|
6
|
+
#
|
|
7
|
+
# *Arguments*
|
|
8
|
+
# * opts, a hash of keyword options for this method.
|
|
9
|
+
# -> size, the size of the tournament. (default = 3).
|
|
10
|
+
# -> pick_best, flag indicating if the best individual in the tournament should win.
|
|
11
|
+
def initialize(opts = {})
|
|
12
|
+
@size = opts[:size] || 3
|
|
13
|
+
pick_best = opts[:pick_best] || true
|
|
14
|
+
@winning_condition = pick_best ? -1 : 1
|
|
15
|
+
raise ArgumentError, "Tournament size must be greater than 1." if @size < 2
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Random samples a number (equal to the tournament size) of individuals
|
|
19
|
+
# from the list of candidates, and returns the best or worst (depending on
|
|
20
|
+
# the selector parameters).
|
|
21
|
+
#
|
|
22
|
+
# *Arguments*
|
|
23
|
+
# * rng, random number generator to use to select index of individual.
|
|
24
|
+
#
|
|
25
|
+
# *Parameters:*
|
|
26
|
+
# * rng, random number generator to use to select index of individual.
|
|
27
|
+
# * candidates, the list of candidates for inclusion within the tournament.
|
|
28
|
+
#
|
|
29
|
+
# *Returns:*
|
|
30
|
+
# The winning individual of the tournament (whether best or worst).
|
|
31
|
+
def produce(rng, candidates)
|
|
32
|
+
tournament = candidates.sample(@size, random: rng)
|
|
33
|
+
return @winning_condition ? tournament.min : tournament.max
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Array-based species model their members as arrays of set values.
|
|
2
|
+
class Wallace::Species::ArraySpecies < Wallace::Species
|
|
3
|
+
|
|
4
|
+
name = :array
|
|
5
|
+
|
|
6
|
+
# Constructs a new array-based species.
|
|
7
|
+
#
|
|
8
|
+
# *Parameters:*
|
|
9
|
+
# * opts, hash of keyword options used by this method.
|
|
10
|
+
# -> id, the unique identifier for this species.
|
|
11
|
+
# -> length, the length constraints imposed on individuals of this species.
|
|
12
|
+
# -> values, the possible values of array elements (can be either a range or an array).
|
|
13
|
+
def initialize(opts = {})
|
|
14
|
+
super(opts)
|
|
15
|
+
@length = opts[:length]
|
|
16
|
+
@values = opts[:values]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Check that a given individual meets the length constraints of the species.
|
|
20
|
+
def valid?(individual)
|
|
21
|
+
return false unless super(individual)
|
|
22
|
+
return true if @length.nil?
|
|
23
|
+
return false if @length.is_a? Integer and individual.data.length != @length
|
|
24
|
+
return false if @length.is_a? Range and (not @length.include? individual.data.length)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Creates a new array-based individual using the set of values and length
|
|
28
|
+
# constraints of the species.
|
|
29
|
+
#
|
|
30
|
+
# *Parameters:*
|
|
31
|
+
# * opts, hash of keyword options used by this method.
|
|
32
|
+
# -> random, the RNG to use when spawning individuals.
|
|
33
|
+
def spawn(opts = {})
|
|
34
|
+
opts[:random] ||= Random.new
|
|
35
|
+
size = @length.is_a?(Range) ? opts[:random].rand(@length) : @length
|
|
36
|
+
data = Array.new(size) { @values.sample(opts) }
|
|
37
|
+
return Wallace::Individual.new(self, data)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
end
|