wallace 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. data/Gemfile +14 -0
  2. data/README.md +12 -0
  3. data/Rakefile +52 -0
  4. data/VERSION +1 -0
  5. data/bin/.gitkeep +0 -0
  6. data/lib/analysers/fitness_distribution_analyser.rb +28 -0
  7. data/lib/analysers/init.rb +1 -0
  8. data/lib/core/analyser.rb +63 -0
  9. data/lib/core/breeder.rb +68 -0
  10. data/lib/core/breeding_graph.rb +59 -0
  11. data/lib/core/breeding_graph/init.rb +3 -0
  12. data/lib/core/breeding_graph/input_node.rb +55 -0
  13. data/lib/core/breeding_graph/node.rb +68 -0
  14. data/lib/core/breeding_graph/node_input.rb +6 -0
  15. data/lib/core/evaluator.rb +52 -0
  16. data/lib/core/evolver.rb +47 -0
  17. data/lib/core/exceptions.rb +6 -0
  18. data/lib/core/experiment.rb +22 -0
  19. data/lib/core/fitness.rb +8 -0
  20. data/lib/core/fraction.rb +7 -0
  21. data/lib/core/individual.rb +65 -0
  22. data/lib/core/logger.rb +5 -0
  23. data/lib/core/migrator.rb +6 -0
  24. data/lib/core/operator.rb +54 -0
  25. data/lib/core/population.rb +56 -0
  26. data/lib/core/selector.rb +43 -0
  27. data/lib/core/species.rb +52 -0
  28. data/lib/core/state.rb +29 -0
  29. data/lib/core/subpopulation.rb +53 -0
  30. data/lib/core/termination.rb +39 -0
  31. data/lib/distributions/gaussian_distribution.rb +60 -0
  32. data/lib/distributions/init.rb +3 -0
  33. data/lib/fitness/init.rb +1 -0
  34. data/lib/fitness/raw_fitness.rb +30 -0
  35. data/lib/loggers/csv_logger.rb +20 -0
  36. data/lib/loggers/init.rb +1 -0
  37. data/lib/loggers/mongo_logger.rb +5 -0
  38. data/lib/loggers/sqlite_logger.rb +5 -0
  39. data/lib/modules/ge/backus_naur_form.rb +125 -0
  40. data/lib/modules/ge/grammar_derivation.rb +30 -0
  41. data/lib/modules/ge/grammar_species.rb +29 -0
  42. data/lib/modules/ge/init.rb +5 -0
  43. data/lib/modules/init.rb +2 -0
  44. data/lib/modules/koza/builder.rb +48 -0
  45. data/lib/modules/koza/builder/full_builder.rb +92 -0
  46. data/lib/modules/koza/builder/grow_builder.rb +103 -0
  47. data/lib/modules/koza/builder/half_builder.rb +70 -0
  48. data/lib/modules/koza/builder/init.rb +3 -0
  49. data/lib/modules/koza/ephemeral.rb +33 -0
  50. data/lib/modules/koza/init.rb +12 -0
  51. data/lib/modules/koza/koza_node.rb +108 -0
  52. data/lib/modules/koza/koza_node_value.rb +72 -0
  53. data/lib/modules/koza/koza_node_value_set.rb +51 -0
  54. data/lib/modules/koza/koza_species.rb +48 -0
  55. data/lib/modules/koza/koza_tree.rb +159 -0
  56. data/lib/modules/koza/operators/init.rb +4 -0
  57. data/lib/modules/koza/operators/subtree_crossover_operation.rb +43 -0
  58. data/lib/modules/koza/operators/subtree_mutation_operation.rb +32 -0
  59. data/lib/operators/bit_flip_mutation_operation.rb +29 -0
  60. data/lib/operators/boundary_mutation_operation.rb +28 -0
  61. data/lib/operators/cycle_crossover_operation.rb +77 -0
  62. data/lib/operators/gaussian_mutation_operation.rb +39 -0
  63. data/lib/operators/half_uniform_crossover_operation.rb +24 -0
  64. data/lib/operators/init.rb +26 -0
  65. data/lib/operators/merging_crossover_operation.rb +27 -0
  66. data/lib/operators/one_point_crossover_operation.rb +29 -0
  67. data/lib/operators/order_crossover_operation.rb +38 -0
  68. data/lib/operators/partially_mapped_crossover_operation.rb +44 -0
  69. data/lib/operators/point_mutation_operation.rb +31 -0
  70. data/lib/operators/position_crossover_operation.rb +50 -0
  71. data/lib/operators/reverse_sequence_mutation_operation.rb +13 -0
  72. data/lib/operators/shuffle_mutation_operation.rb +17 -0
  73. data/lib/operators/splice_crossover_operation.rb +42 -0
  74. data/lib/operators/subtour_exchange_crossover_operation.rb +54 -0
  75. data/lib/operators/swap_mutation_operation.rb +29 -0
  76. data/lib/operators/three_parent_crossover_operation.rb +16 -0
  77. data/lib/operators/two_point_crossover_operation.rb +31 -0
  78. data/lib/operators/twors_mutation_operation.rb +18 -0
  79. data/lib/operators/uniform_crossover_operation.rb +30 -0
  80. data/lib/operators/uniform_mutation_operation.rb +31 -0
  81. data/lib/operators/variable_one_point_crossover_operation.rb +80 -0
  82. data/lib/patches/enumerable.rb +85 -0
  83. data/lib/patches/init.rb +5 -0
  84. data/lib/patches/range.rb +13 -0
  85. data/lib/selectors/init.rb +5 -0
  86. data/lib/selectors/random_selector.rb +14 -0
  87. data/lib/selectors/roulette_selector.rb +23 -0
  88. data/lib/selectors/tournament_selector.rb +36 -0
  89. data/lib/species/array_species.rb +40 -0
  90. data/lib/species/bit_string_species.rb +18 -0
  91. data/lib/species/init.rb +4 -0
  92. data/lib/species/permutation_species.rb +22 -0
  93. data/lib/species/string_species.rb +29 -0
  94. data/lib/utility/init.rb +4 -0
  95. data/lib/utility/scaled_array.rb +88 -0
  96. data/lib/utility/sorted_array.rb +39 -0
  97. data/lib/wallace.rb +40 -0
  98. data/test/.gitkeep +0 -0
  99. metadata +248 -0
@@ -0,0 +1,52 @@
1
+ # The evaluator is responsible for determining the fitness of given individuals within the
2
+ # population. An evaluation function is provided to each evaluator which produces a fitness
3
+ # measure object for a given individual.
4
+ class Wallace::Evaluator
5
+
6
+ attr_reader :threads
7
+
8
+ # Constructs a new evaluator.
9
+ # Attach the given fitness calculation method to the evaluate method of this
10
+ # evaluator.
11
+ #
12
+ # *Parameters:*
13
+ # * opts, a hash of keyword options for this evaluator.
14
+ # -> threads, the number of threads that evaluation should be split across.
15
+ # * f, function to calculate and return the fitness of a given individual.
16
+ def initialize(opts = {}, &f)
17
+ @threads = opts[:threads] || 1
18
+ define_singleton_method(:evaluate, &f)
19
+ end
20
+
21
+ # Evaluates all given individuals within a population.
22
+ # If multi-threading is enabled then individuals are split into equal (as possible)
23
+ # chunks. For now concurrency has not been implemented, and will be thought about
24
+ # in more detail in a future version.
25
+ #
26
+ # *Parameters:*
27
+ # * rng, random number generator to use during evaluation.
28
+ # * population, the population of individuals to evaluate.
29
+ def process!(rng, population)
30
+ population.subpopulations.each do |s|
31
+ if @threads > 1
32
+ s.contents.peach(@threads) { |i| i.fitness = evaluate(rng, i) }
33
+ else
34
+ s.contents.each { |i| i.fitness = evaluate(rng, i) }
35
+ end
36
+ end
37
+
38
+ # Should we find the best and worst individuals in each sub-population?
39
+
40
+ end
41
+
42
+ # Evaluates a given individual and returns their fitness score.
43
+ # Should not modify the providied individual.
44
+ #
45
+ # *Parameters:*
46
+ # * rng, random number generator to use during evaluation.
47
+ # * individual, the individual whose fitness should be calculated.
48
+ def evaluate(rng, individual)
49
+ raise NotImplementedError, 'No evaluation function was provided to this evaluator.'
50
+ end
51
+
52
+ end
@@ -0,0 +1,47 @@
1
+ # Steady-state vs. Generational
2
+ class Wallace::Evolver
3
+
4
+ # Constructs a new evolver.
5
+ #
6
+ # *Parameters:*
7
+ # * rng, the random number generator to use for stochastic processes.
8
+ # * population, the population to evolve.
9
+ # * evaluator, the evaluator to use to determine the fitness of individuals.
10
+ # * breeder, the breeder to use to create individuals for successive generations.
11
+ # * migrator, the migrator to use to exchange individuals between sub-populations.
12
+ # * termination, the termination criteria for the evolution.
13
+ def initialize(rng, population, evaluator, termination)
14
+ @rng = rng
15
+ @population = population
16
+ @evaluator = evaluator
17
+ @termination = termination
18
+ @evaluator = evaluator
19
+ @state = nil
20
+ end
21
+
22
+ # Conducts the described evolutionary algorithm until the termination criteria is met.
23
+ # Upon meeting the termination criteria, the best individual found from the entire run
24
+ # is returned.
25
+ def evolve
26
+
27
+ # Initialise and evaluate the population.
28
+ @population.fresh!(@rng)
29
+ @evaluator.process!(@rng, @population)
30
+ @state = Wallace::State.new(@population)
31
+
32
+ # Continue the evolution unless the termination condition has been met.
33
+ until @termination.finished?(@state)
34
+ @migrator.migrate!(@rng, @population) unless @migrator.nil?
35
+ @population.breed!(@rng)
36
+ @evaluator.process!(@rng, @population)
37
+ @state.next!
38
+ #puts "#{@state.best.to_s} = #{@state.best.fitness.value}"
39
+ puts "#{@state.generations}: #{@state.best.fitness.value} (#{@state.best.data.length})"
40
+ end
41
+
42
+ # Return the best individual from the run.
43
+ return @state.best
44
+
45
+ end
46
+
47
+ end
@@ -0,0 +1,6 @@
1
+ module Wallace::Errors
2
+
3
+ # Thrown when all genetic material has been used.
4
+ class GenotypeExhaustedError < StandardError; end
5
+
6
+ end
@@ -0,0 +1,22 @@
1
+ # Experiment instances are used to
2
+ class Wallace::Experiment
3
+
4
+ # Constructs a new experiment.
5
+ #
6
+ # *Parameters:*
7
+ # * opts, a hash of keyword options for this constructor.
8
+ # -> parameters, the set of parameters to feed to the configurator.
9
+ # -> repeats, the number of times to repeat the experiment.
10
+ # * configurator, a lambda function used to setup the experiment run according to
11
+ # some given parameters.
12
+ def initialize(opts, configurator)
13
+ @parameters = opts[:parameters]
14
+ @repeats = opts[:repeats] || 1
15
+ end
16
+
17
+ # Performs the experiment described by this objects attributes.
18
+ def experiment
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,8 @@
1
+ # The base fitness measure class. Fitness objects are responsible for storing
2
+ # fitness details for a given individual and should implement ordering
3
+ # functionality such that they can be compared and sorted.
4
+ class Wallace::Fitness
5
+
6
+ include Comparable
7
+
8
+ end
@@ -0,0 +1,7 @@
1
+ # Instances of the fraction class are used to disambiguate if a given float
2
+ # should be treated as an absolute value or rather a fraction of some other
3
+ # (implicit) value.
4
+ #
5
+ # If the Fraction class is not used, it is assumed that a given number is an
6
+ # absolute number (in a context-sensitive environment).
7
+ Wallace::Fraction = Struct.new(:value)
@@ -0,0 +1,65 @@
1
+ # This class is the base class used to represent individuals within the population.
2
+ # The data (e.g. list, tree, string) for an individual is held within its data attribute.
3
+ class Wallace::Individual
4
+
5
+ include Comparable
6
+
7
+ # Neither the species nor the data of the individual may be modified
8
+ # once the individual has been created.
9
+ attr_accessor :species,
10
+ :data
11
+
12
+ # Allow the fitness of the individual to be both read and written to.
13
+ attr_accessor :fitness
14
+
15
+ # Constructs a new individual.
16
+ #
17
+ # *Parameters:*
18
+ # * species, the species of the individual.
19
+ # * data, the data for this individual.
20
+ # * opts, keyword arguments for this constructor.
21
+ # * -> fitness, the fitness object for this individual (keyword).
22
+ def initialize(species, data, opts = {})
23
+ @species = species
24
+ @fitness = nil
25
+ @data = data
26
+ @fitness = opts[:fitness]
27
+ end
28
+
29
+ # Checks whether this individual has been evaluated.
30
+ # Returns true if it has, false if otherwise.
31
+ def evaluated?
32
+ return (not @fitness.nil?)
33
+ end
34
+
35
+ # Post-processes this individual.
36
+ # Returns the post-processed individual.
37
+ def finish!
38
+ @species.finish!(self)
39
+ end
40
+
41
+ # Compares the fitness of this individual against that of another.
42
+ #
43
+ # *Parameters:*
44
+ # * other, the individual to compare against.
45
+ def <=>(other)
46
+ @fitness <=> other.fitness
47
+ end
48
+
49
+ # Creates a clone of this individual.
50
+ #
51
+ # *Parameters:*
52
+ # * opts, keyword arguments for this method.
53
+ # * -> copy_fitness, flag indicating whether fitness information should be copied across
54
+ # to the cloned individual.
55
+ def clone(opts = {})
56
+ copy_fitness = opts[:copy_fitness]
57
+ return Wallace::Individual.new(species, data.clone, fitness: copy_fitness ? fitness : nil)
58
+ end
59
+
60
+ # Produces a string representation of this individual.
61
+ def to_s
62
+ return @data.to_s
63
+ end
64
+
65
+ end
@@ -0,0 +1,5 @@
1
+ #
2
+ class Wallace::Logger < Wallace::Analyser
3
+
4
+
5
+ end
@@ -0,0 +1,6 @@
1
+ # The migrator is responsible for migrating (exchanging) individuals between
2
+ # connected sub-populations.
3
+ class Wallace::Migrator
4
+
5
+
6
+ end
@@ -0,0 +1,54 @@
1
+ # Operators are used to produce individuals for the next generation using
2
+ # members of the current population as their inputs.
3
+ #
4
+ # TODO: Feed state data into the operator (size of population, fitness, generations, etc).
5
+ class Wallace::Operator
6
+
7
+ # Using meta-methods to give the operator a name/tag that
8
+ # can be used to refer to it within the DSL or command line.
9
+ class << self
10
+ attr_accessor :name
11
+ end
12
+
13
+ # Constructs an instance of an Operator.
14
+ #
15
+ # *Parameters:*
16
+ # * opts, a hash of keyword options for this method.
17
+ # -> id, the unique identifier for this operator.
18
+ def initialize(opts = {})
19
+ @id = opts[:id]
20
+ end
21
+
22
+ # Produces an arbitrary number of individuals according to the rules of this operator
23
+ # using
24
+ #
25
+ # *Parameters:*
26
+ # * rng, random number generator to use within the operation.
27
+ # * inputs, input individuals to the operation.
28
+ #
29
+ # *Returns:*
30
+ # An array of the offspring individuals produced by this operation.
31
+ def produce(rng, inputs)
32
+ operate(rng, inputs.map { |i| i.data }).each_with_index do |data, i|
33
+ inputs[i].data = data
34
+ end
35
+ return inputs
36
+ end
37
+
38
+ # Performs this genetic operation on the genetic data of a given number of individuals
39
+ # to return the genetic data for their offspring.
40
+ #
41
+ # Unlike 'produce', this method operates only on the genetic data (we refrain from the
42
+ # term chromosome since an individual may have multiple chromosomes).
43
+ #
44
+ # *Parameters:*
45
+ # * rng, the RNG to use within this operation.
46
+ # * inputs, input individuals to the operation.
47
+ #
48
+ # *Returns:*
49
+ # An array containing the genetic data for each produced offspring.
50
+ def operate(rng, inputs)
51
+ raise NotImplementedError, "No 'operate' function was implemented by this operator."
52
+ end
53
+
54
+ end
@@ -0,0 +1,56 @@
1
+ # In this current version of Wallace each population may only hold individuals of a single
2
+ # species. For future versions supporting co-operative and competitive evolution it should
3
+ # be able to adapt the population model to fit.
4
+ class Wallace::Population
5
+
6
+ attr_reader :subpopulations
7
+
8
+ # Constructs a new population.
9
+ #
10
+ # *Parameters:*
11
+ # * breeder, the breeder used to generate individuals for successive generations.
12
+ # * subpopulations, a list of subpopulations contained within this population.
13
+ def initialize(breeder, subpopulations)
14
+ @breeder = breeder
15
+ @subpopulations = subpopulations
16
+ end
17
+
18
+ # Clears the contents of each sub-population within this population and resets all
19
+ # attached components.
20
+ def clear!
21
+ @subpopulations.each { |s| s.clear }
22
+ end
23
+
24
+ # Initialises a fresh population of individuals.
25
+ #
26
+ # *Parameters:*
27
+ # * rng, the random number generator to use.
28
+ def fresh!(rng)
29
+ @subpopulations.each { |s| s.fresh!(rng) }
30
+ end
31
+
32
+ # Breeds the next generation of individuals for this population.
33
+ #
34
+ # *Parameters:*
35
+ # * rng, random number generator to use during breeding.
36
+ def breed!(rng)
37
+ @subpopulations.each { |s| @breeder.breed!(rng, s) }
38
+ end
39
+
40
+ # Selects the best individual from the current population.
41
+ def best
42
+ @subpopulations.map { |s| s.best }.min
43
+ end
44
+
45
+ # Selects the worst individual from the current population.
46
+ def worst
47
+ @subpopulations.map { |s| s.worst }.max
48
+ end
49
+
50
+ # Calculates the combined size (number of individuals) of this population.
51
+ def size
52
+ @subpopulations.reduce(0) { |sum, sp| sum += sp.length }
53
+ end
54
+ alias_method :length, :size
55
+
56
+ end
@@ -0,0 +1,43 @@
1
+ # Selectors are used to select individuals from a set of candidates (usually, but not exclusively
2
+ # the sub-population) as participants in the breeding process.
3
+ #
4
+ # Different selector classes are used to implement different types of selection rules.
5
+ #
6
+ # A "prepare" method is provided to allow the list of candidates to be pre-processed, saving time
7
+ # for selection methods such as roulette selection.
8
+ class Wallace::Selector
9
+
10
+ class << self
11
+ attr_accessor :name
12
+ end
13
+
14
+ # Prepares a list of candidates for use with this selector.
15
+ # Pre-processing can be performed on this list to improve performance.
16
+ # The list of candidates is not stored by the selector, instead it is stored
17
+ # by an associated input node.
18
+ #
19
+ # *Parameters:*
20
+ # * candidates, the list of candidates to select from.
21
+ # * opts, a hash of keyword options for this method.
22
+ # -> random, random number generator to use in pre-processing of candidates.
23
+ #
24
+ # *Returns:*
25
+ # A prepared list of candidates for use with this selector.
26
+ def prepare(candidates, opts = {})
27
+ candidates
28
+ end
29
+
30
+ # Selects a single individual from the list of prepared candidates according
31
+ # to the rules of this selection method.
32
+ #
33
+ # *Parameters:*
34
+ # * rng, the RNG to use to inform the choice of candidate.
35
+ # * candidates, the candidate individuals for selection.
36
+ #
37
+ # *Returns:*
38
+ # A selected individual from the list of candidates.
39
+ def produce(rng, candidates)
40
+ raise NotImplementedError, 'No produce function was implemented by this selector.'
41
+ end
42
+
43
+ end
@@ -0,0 +1,52 @@
1
+ # Each individual in the population must belong to a single species.
2
+ class Wallace::Species
3
+
4
+ attr_reader :id
5
+
6
+ class << self
7
+ attr_accessor :name
8
+ end
9
+
10
+ # Creates a new Species.
11
+ #
12
+ # *Parameters:*
13
+ # * opts, hash of keyword options used by this method.
14
+ # -> id, the unique identifier for this species.
15
+ def initialize(opts)
16
+ @id = opts[:id]
17
+ end
18
+
19
+ # Determines whether a given individual is a valid member of this species.
20
+ # Validity may be determined by more than just membership. For example,
21
+ # there may be certain size constraints on individuals.
22
+ #
23
+ # *Parameters:*
24
+ # * individual, the individual to check for validity.
25
+ #
26
+ # *Returns:*
27
+ # true if a valid member of this species, false if invalid or not a member.
28
+ def valid?(individual)
29
+ individual.species === self
30
+ end
31
+
32
+ # Spawns a new member of this species at random.
33
+ #
34
+ # *Parameters:*
35
+ # * rng, the random number generator to use.
36
+ def spawn(rng)
37
+ raise NotImplementedError, 'Spawn method not implemented by this species.'
38
+ end
39
+
40
+ # Used to post-process individuals belonging to this species after they have
41
+ # been created. By default this method is a stub.
42
+ #
43
+ # *Parameters:*
44
+ # * individual, the individual to post-process.
45
+ #
46
+ # *Returns:*
47
+ # * the post-processed individual.
48
+ def finish!(individual)
49
+ individual
50
+ end
51
+
52
+ end
@@ -0,0 +1,29 @@
1
+ # This class is used to record information about the state of the evolution.
2
+ class Wallace::State
3
+
4
+ # Allow all information to be read, but not changed.
5
+ attr_reader :population,
6
+ :generations,
7
+ :stagnation,
8
+ :best
9
+
10
+ # Constructs a new state object.
11
+ #
12
+ # *Parameters*
13
+ # * population, the population of the state.
14
+ def initialize(population)
15
+ @population = population
16
+ @generations = 0
17
+ @stagnation = 0
18
+ @best = population.best
19
+ end
20
+
21
+ # Updates the state object with the current state of the evolver.
22
+ def next!
23
+ @generations += 1
24
+ @stagnation += 1
25
+ pbest = population.best
26
+ @best = pbest if pbest < @best
27
+ end
28
+
29
+ end