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