gene_genie 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +79 -12
- data/lib/gene_genie/combiner/one_point_combiner.rb +34 -0
- data/lib/gene_genie/combiner/uniform_combiner.rb +21 -0
- data/lib/gene_genie/combiner/weighted_average_combiner.rb +26 -0
- data/lib/gene_genie/gene.rb +16 -10
- data/lib/gene_genie/gene_factory.rb +6 -2
- data/lib/gene_genie/gene_pool.rb +30 -20
- data/lib/gene_genie/genie.rb +10 -2
- data/lib/gene_genie/mutator/nudge_mutator.rb +29 -0
- data/lib/gene_genie/selector/proportional_selector.rb +2 -11
- data/lib/gene_genie/template_evaluator.rb +27 -7
- data/lib/gene_genie/version.rb +1 -1
- metadata +12 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ea71d46d33da67b5dff6976fbcf4aa6dfd73eecd0fd1c2a0fce2fe70ff02de52
|
4
|
+
data.tar.gz: 18034001ed08324f708e0395d1970aae79880f24949c1e954b453737333b04b5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 19139fef4bb51b4ca40d6f949f716bba442b5fc3490ccc26c0cd555fe0eb0db09ad4bdb8366a1562b63bdc9072a4425bd71989a3af0c4cb7fd22f9ddc247eb78
|
7
|
+
data.tar.gz: 1c7686c788280f0cf40f8abfdb2fee8f267e1b5cbfc087b913042553cde7245fc2f24ce9778c481bee6b19b03e99483add0f1f9d5484be4542f80ab5d318cd47
|
data/README.md
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
[![Build Status](https://travis-ci.org/MEHColeman/gene_genie.svg?branch=master)](https://travis-ci.org/MEHColeman/gene_genie)
|
2
1
|
[![Gem Version](https://badge.fury.io/rb/gene_genie.svg)](http://badge.fury.io/rb/gene_genie)
|
3
2
|
[![Code Climate](https://codeclimate.com/github/MEHColeman/gene_genie.png)](https://codeclimate.com/github/MEHColeman/gene_genie)
|
4
3
|
|
@@ -24,34 +23,102 @@ Or install it yourself as:
|
|
24
23
|
$ gem install gene_genie
|
25
24
|
|
26
25
|
## Usage
|
27
|
-
Basic usage is designed to be as simple as possible. You provide two things: a
|
28
|
-
|
26
|
+
Basic usage is designed to be as simple as possible. You provide two things: a
|
27
|
+
template and an evaluator.
|
28
|
+
|
29
|
+
A template is an array of hashes, representing a list of variables along with
|
30
|
+
their possible range of values that you wish to optimise.
|
31
|
+
|
29
32
|
An evaluator implements a fitness method that returns a numeric value.
|
30
|
-
The genetic algorithm will then search for the set of values that maximises the fitness.
|
31
33
|
|
32
|
-
|
34
|
+
The genetic algorithm will then search for the set of values that maximises the
|
35
|
+
fitness.
|
36
|
+
|
37
|
+
~~~ruby
|
33
38
|
require 'gene_genie'
|
34
39
|
|
35
|
-
template = {
|
40
|
+
template = [{
|
36
41
|
range_of_ints: 1..10,
|
42
|
+
more_ints: 3..100
|
43
|
+
}]
|
44
|
+
~~~
|
45
|
+
|
37
46
|
<!---
|
38
47
|
range_of_floats: 1.0..4.5,
|
39
|
-
set_of_items: [:apple, :banana, :orange],
|
48
|
+
set_of_items: [:apple, :banana, :orange],
|
40
49
|
ordered_set_of_items: [:one, :two, :three],
|
41
50
|
circular_ordered_set: [:early_morning, :morning, :noon, :afternoon,
|
42
51
|
:evening, :midnight]
|
43
52
|
-->
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
53
|
+
Typically, your fitness function will create a model represented by the values
|
54
|
+
specified in the template, evaluate the performance of that model, and return a
|
55
|
+
fitness score. But, it can be as complicated or simple as you need.
|
56
|
+
|
57
|
+
A fitness function should return a float or integer
|
58
|
+
~~~ruby
|
59
|
+
class Summer
|
60
|
+
def fitness(params)
|
61
|
+
params.inject(0) {|acc, values| acc + values.each_value.inject(&:+)}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
~~~
|
65
|
+
Then, simply create a Genie, and optimise:
|
66
|
+
~~~ruby
|
67
|
+
genie = GeneGenie::Genie.new(template, Summer.new)
|
68
|
+
genie.optimise
|
69
|
+
|
70
|
+
puts genie.best.inspect
|
71
|
+
~~~
|
72
|
+
If you want to monitor progress of the optimisation algorithm, you can register
|
73
|
+
a listener:
|
74
|
+
~~~ruby
|
75
|
+
genie.register_listener(Proc.new do |g|
|
76
|
+
puts "Best score: '#{g.best.to_hashes.to_s}', Score: #{g.best_fitness}"
|
77
|
+
end)
|
78
|
+
~~~
|
79
|
+
See the examples directory for more details.
|
80
|
+
|
81
|
+
If you use the simple `genie` interface, the genetic algorithm will come up with
|
82
|
+
reasonable best-guesses for various algorithm parameters, but you can dive under
|
83
|
+
the covers to give yourself more flexibility.
|
48
84
|
* Population size
|
49
85
|
* Gene pools
|
50
86
|
* Initialization
|
51
87
|
* Optimisation Criteria
|
52
88
|
|
53
|
-
Custom objects for crossover, gene selection, etc.
|
54
89
|
|
90
|
+
## Advanced Use
|
91
|
+
|
92
|
+
If you want more control over your algorithm, you can skip the `Genie`, and use
|
93
|
+
`GenePool` directly.
|
94
|
+
|
95
|
+
This allows you to create objects that are used to control the methods used for
|
96
|
+
mutation, crossover, gene selection, gene pool parameters like population size
|
97
|
+
and convergence criteria.
|
98
|
+
~~~ruby
|
99
|
+
gene_mutator = CustomMutator.new(gm_args)
|
100
|
+
gene_factory = CustomGeneFactory.new(gf_args)
|
101
|
+
|
102
|
+
template_evaluator = CustomTemplateEvaluator.new(template)
|
103
|
+
size = template_evaluator.recommended_size
|
104
|
+
|
105
|
+
GenePool.new(template: template,
|
106
|
+
fitness_evaluator: fitness_evaluator,
|
107
|
+
gene_factory: gene_factory,
|
108
|
+
size: size,
|
109
|
+
mutator: gene_mutator)
|
110
|
+
~~~
|
111
|
+
The `mutator` operates on a `Gene` to alter it slightly, mimicking natural gene
|
112
|
+
mutations.
|
113
|
+
|
114
|
+
The `gene_factory` creates a population of genes of a given size. Genes are
|
115
|
+
typically generated randomly across the parameter space, but this allows you to
|
116
|
+
have more control over how genes are distributed across this space.
|
117
|
+
|
118
|
+
The `template_evaluator` is used to provide other configuration options to the
|
119
|
+
GenePool, such as recommended size.
|
120
|
+
|
121
|
+
---
|
55
122
|
Note:
|
56
123
|
Due to the non-deterministic nature of the algorithm, some of the tests don't
|
57
124
|
pass every time at the moment! This is a known issue.
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module GeneGenie
|
2
|
+
module Combiner
|
3
|
+
# Picks alleles from each Gene randomly
|
4
|
+
class OnePointCombiner
|
5
|
+
def call(gene_a, gene_b)
|
6
|
+
if rand >= 0.5
|
7
|
+
first_gene = gene_a
|
8
|
+
second_gene = gene_b
|
9
|
+
else
|
10
|
+
first_gene = gene_b
|
11
|
+
second_gene = gene_a
|
12
|
+
end
|
13
|
+
first_gene_hashes = first_gene.to_hashes
|
14
|
+
second_gene_hashes = second_gene.to_hashes
|
15
|
+
|
16
|
+
total_length = first_gene_hashes.map(&:size).reduce(:+)
|
17
|
+
crossover_point = rand(0..(total_length - 1))
|
18
|
+
|
19
|
+
count = 0
|
20
|
+
new_information = first_gene_hashes.map.with_index do |part, index|
|
21
|
+
new_hash = {}
|
22
|
+
part.each do |k, v|
|
23
|
+
new_hash[k] = (count >= crossover_point) ? second_gene_hashes[index][k] : v
|
24
|
+
count += 1
|
25
|
+
end
|
26
|
+
new_hash
|
27
|
+
end
|
28
|
+
Gene.new(information: new_information,
|
29
|
+
fitness_evaluator: first_gene.fitness_evaluator,
|
30
|
+
gene_combiner: self)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module GeneGenie
|
2
|
+
module Combiner
|
3
|
+
# Picks alleles from each Gene randomly
|
4
|
+
class UniformCombiner
|
5
|
+
def call(first_gene, second_gene)
|
6
|
+
first_gene_hashes = first_gene.to_hashes
|
7
|
+
second_gene_hashes = second_gene.to_hashes
|
8
|
+
new_information = first_gene_hashes.map.with_index do |part, index|
|
9
|
+
new_hash = {}
|
10
|
+
part.each do |k, v|
|
11
|
+
new_hash[k] = (rand > 0.5) ? v : second_gene_hashes[index][k]
|
12
|
+
end
|
13
|
+
new_hash
|
14
|
+
end
|
15
|
+
Gene.new(information: new_information,
|
16
|
+
fitness_evaluator: first_gene.fitness_evaluator,
|
17
|
+
gene_combiner: self)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module GeneGenie
|
2
|
+
module Combiner
|
3
|
+
# Creates new allele value by creating a (random) weighted
|
4
|
+
# average of the two parent genes. Good for genes that represent numeric
|
5
|
+
# scalar values, but not for genes representing discrete info.
|
6
|
+
class WeightedAverageCombiner
|
7
|
+
def call(first_gene, second_gene)
|
8
|
+
first_gene_hashes = first_gene.to_hashes
|
9
|
+
second_gene_hashes = second_gene.to_hashes
|
10
|
+
new_information = first_gene_hashes.map.with_index do |part, index|
|
11
|
+
new_hash = {}
|
12
|
+
part.each do |k, v|
|
13
|
+
p_first = rand(0.0..100.0)
|
14
|
+
p_second = 100 - p_first
|
15
|
+
new_hash[k] = (((p_first * v) +
|
16
|
+
(p_second * second_gene_hashes[index][k]))/100).round
|
17
|
+
end
|
18
|
+
new_hash
|
19
|
+
end
|
20
|
+
Gene.new(information: new_information,
|
21
|
+
fitness_evaluator: first_gene.fitness_evaluator,
|
22
|
+
gene_combiner: self)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/gene_genie/gene.rb
CHANGED
@@ -1,15 +1,20 @@
|
|
1
|
+
require_relative 'combiner/one_point_combiner'
|
2
|
+
|
1
3
|
module GeneGenie
|
2
4
|
# A Gene is the basic unit of the genetic algorithm. Genes hold the
|
3
5
|
# information used to evaluate their fitness.
|
4
6
|
# They are combined into new Genes during the optimisation process.
|
5
7
|
# @since 0.0.1
|
6
8
|
class Gene
|
7
|
-
def initialize(information
|
9
|
+
def initialize(information:,
|
10
|
+
fitness_evaluator:,
|
11
|
+
gene_combiner: GeneGenie::Combiner::OnePointCombiner.new)
|
8
12
|
fail ArgumentError, 'information must be Array' unless information.kind_of? Array
|
9
13
|
fail ArgumentError, 'information must be Array of Hashes' unless information[0].kind_of? Hash
|
10
14
|
|
11
15
|
@information = information
|
12
16
|
@fitness_evaluator = fitness_evaluator
|
17
|
+
@combiner = gene_combiner
|
13
18
|
end
|
14
19
|
|
15
20
|
def to_hashes
|
@@ -20,22 +25,23 @@ module GeneGenie
|
|
20
25
|
@fitness ||= @fitness_evaluator.fitness(@information)
|
21
26
|
end
|
22
27
|
|
28
|
+
def fitness_evaluator
|
29
|
+
@fitness_evaluator
|
30
|
+
end
|
31
|
+
|
32
|
+
def normalised_fitness(minimum)
|
33
|
+
@normalised_fitness ||= fitness - minimum
|
34
|
+
end
|
35
|
+
|
23
36
|
def mutate(mutator)
|
24
37
|
@information = mutator.call @information
|
25
38
|
@fitness = nil
|
39
|
+
@normalised_fitness = nil
|
26
40
|
self
|
27
41
|
end
|
28
42
|
|
29
43
|
def combine(other_gene)
|
30
|
-
|
31
|
-
new_information = @information.map.with_index do |part, index|
|
32
|
-
new_hash = {}
|
33
|
-
part.each do |k, v|
|
34
|
-
new_hash[k] = (rand > 0.5) ? v : other_gene_hash[index][k]
|
35
|
-
end
|
36
|
-
new_hash
|
37
|
-
end
|
38
|
-
Gene.new(new_information, @fitness_evaluator)
|
44
|
+
@combiner.call(self, other_gene)
|
39
45
|
end
|
40
46
|
|
41
47
|
def <=>(other)
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require_relative 'gene'
|
2
|
+
require_relative 'combiner/weighted_average_combiner'
|
2
3
|
|
3
4
|
module GeneGenie
|
4
5
|
# GeneFactory
|
@@ -7,9 +8,10 @@ module GeneGenie
|
|
7
8
|
# The default implementation will produce random genes, but other approaches
|
8
9
|
# could be taken.
|
9
10
|
class GeneFactory
|
10
|
-
def initialize(template, fitness_evaluator)
|
11
|
+
def initialize(template, fitness_evaluator, gene_combiner=nil)
|
11
12
|
@template = template
|
12
13
|
@fitness_evaluator = fitness_evaluator
|
14
|
+
@combiner = gene_combiner || GeneGenie::Combiner::WeightedAverageCombiner.new
|
13
15
|
end
|
14
16
|
|
15
17
|
def create(size = 1)
|
@@ -26,7 +28,9 @@ module GeneGenie
|
|
26
28
|
gene_array = @template.map do |part|
|
27
29
|
create_hash_from_template_part(part)
|
28
30
|
end
|
29
|
-
Gene.new(gene_array,
|
31
|
+
Gene.new(information: gene_array,
|
32
|
+
fitness_evaluator: @fitness_evaluator,
|
33
|
+
gene_combiner: @combiner)
|
30
34
|
end
|
31
35
|
|
32
36
|
def create_hash_from_template_part(part)
|
data/lib/gene_genie/gene_pool.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require_relative 'gene_factory'
|
2
|
-
require_relative 'mutator/
|
2
|
+
require_relative 'mutator/nudge_mutator'
|
3
3
|
require_relative 'mutator/null_mutator'
|
4
4
|
require_relative 'selector/proportional_selector'
|
5
5
|
require_relative 'template_evaluator'
|
@@ -34,7 +34,7 @@ module GeneGenie
|
|
34
34
|
unless (template.instance_of? Array) && (template[0].instance_of? Hash)
|
35
35
|
fail ArgumentError, 'template must be an array of hashes of ranges'
|
36
36
|
end
|
37
|
-
gene_mutator =
|
37
|
+
gene_mutator = NudgeMutator.new(template, 0.01)
|
38
38
|
gene_factory = GeneFactory.new(template, fitness_evaluator)
|
39
39
|
|
40
40
|
template_evaluator = TemplateEvaluator.new(template)
|
@@ -55,13 +55,21 @@ module GeneGenie
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def best
|
58
|
-
@pool.max_by(&:fitness)
|
58
|
+
@best ||= @pool.max_by(&:fitness)
|
59
59
|
end
|
60
60
|
|
61
61
|
def best_fitness
|
62
62
|
best.fitness
|
63
63
|
end
|
64
64
|
|
65
|
+
def worst
|
66
|
+
@worst ||= @pool.min_by(&:fitness)
|
67
|
+
end
|
68
|
+
|
69
|
+
def worst_fitness
|
70
|
+
worst.fitness
|
71
|
+
end
|
72
|
+
|
65
73
|
def best_ever
|
66
74
|
@best_ever ||= best
|
67
75
|
end
|
@@ -73,7 +81,7 @@ module GeneGenie
|
|
73
81
|
new_pool << select_genes_combine_and_mutate
|
74
82
|
end
|
75
83
|
@pool = new_pool
|
76
|
-
|
84
|
+
update_stats
|
77
85
|
@generation += 1
|
78
86
|
|
79
87
|
@listeners.each { |l| l.call(self) }
|
@@ -86,28 +94,30 @@ module GeneGenie
|
|
86
94
|
end
|
87
95
|
|
88
96
|
def average_fitness
|
89
|
-
total_fitness / @pool.size
|
97
|
+
@average_fitness ||= total_fitness / @pool.size
|
90
98
|
end
|
91
99
|
|
92
100
|
def total_fitness
|
93
|
-
fitness_values.reduce(:+)
|
101
|
+
@total_fitness ||= fitness_values.reduce(:+)
|
94
102
|
end
|
95
103
|
|
96
|
-
def
|
97
|
-
@
|
104
|
+
def total_normalised_fitness
|
105
|
+
@total_normalised_fitness ||= normalised_fitness_values.reduce(:+)
|
98
106
|
end
|
99
107
|
|
100
|
-
def
|
101
|
-
@pool
|
102
|
-
end
|
103
|
-
|
104
|
-
def worst_fitness
|
105
|
-
worst.fitness
|
108
|
+
def genes
|
109
|
+
@pool
|
106
110
|
end
|
107
111
|
|
108
112
|
private
|
109
113
|
|
110
|
-
def
|
114
|
+
def update_stats
|
115
|
+
@best = nil
|
116
|
+
@worst = nil
|
117
|
+
@total_fitness = nil
|
118
|
+
@total_normalised_fitness = nil
|
119
|
+
@average_fitness = nil
|
120
|
+
|
111
121
|
@best_ever = best if best.fitness > best_ever.fitness
|
112
122
|
end
|
113
123
|
|
@@ -115,17 +125,17 @@ module GeneGenie
|
|
115
125
|
@selector.call(self)
|
116
126
|
end
|
117
127
|
|
118
|
-
def combine_genes(first, second)
|
119
|
-
first.combine(second)
|
120
|
-
end
|
121
|
-
|
122
128
|
def fitness_values
|
123
129
|
@pool.map(&:fitness)
|
124
130
|
end
|
125
131
|
|
132
|
+
def normalised_fitness_values
|
133
|
+
@pool.map{ |gene| gene.normalised_fitness(worst_fitness) }
|
134
|
+
end
|
135
|
+
|
126
136
|
def select_genes_combine_and_mutate
|
127
137
|
first_gene, second_gene = select_genes
|
128
|
-
new_gene =
|
138
|
+
new_gene = first_gene.combine(second_gene)
|
129
139
|
new_gene.mutate(@mutator)
|
130
140
|
end
|
131
141
|
end
|
data/lib/gene_genie/genie.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require_relative 'gene_pool'
|
2
|
+
require_relative 'listener/logging_listener'
|
2
3
|
|
3
4
|
# Namespace for GeneGenie genetic algorithm optimisation gem
|
4
5
|
# @since 0.0.1
|
@@ -6,6 +7,9 @@ module GeneGenie
|
|
6
7
|
# Top level, basic interface for GA optimisation.
|
7
8
|
# Genie will attempt to optimise based on best-guess defaults if none are
|
8
9
|
# provided
|
10
|
+
# Genie is basically a wrapper around GenePool that lets you get going as
|
11
|
+
# quickly as possible by providing a reasonable set of defaults.
|
12
|
+
# For more control and customisation, go straight to using GenePoo
|
9
13
|
# @since 0.0.1
|
10
14
|
class Genie
|
11
15
|
DEFAULT_NO_OF_GENERATIONS = 50
|
@@ -35,6 +39,10 @@ module GeneGenie
|
|
35
39
|
end
|
36
40
|
alias_method :optimize, :optimise
|
37
41
|
|
42
|
+
def register_listener(listener)
|
43
|
+
@gene_pool.register_listener(listener)
|
44
|
+
end
|
45
|
+
|
38
46
|
def best
|
39
47
|
@gene_pool.best_ever.to_hashes
|
40
48
|
end
|
@@ -56,8 +64,8 @@ module GeneGenie
|
|
56
64
|
DEFAULT_NO_OF_GENERATIONS.times do
|
57
65
|
current_fitness = best_fitness
|
58
66
|
@gene_pool.evolve
|
59
|
-
break if best_fitness < current_fitness *
|
60
|
-
(1 + (IMPROVEMENT_THRESHOLD / 100))
|
67
|
+
break if (best_fitness < current_fitness *
|
68
|
+
(1 + (IMPROVEMENT_THRESHOLD / 100)) && best_fitness > 0)
|
61
69
|
end
|
62
70
|
end
|
63
71
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module GeneGenie
|
2
|
+
# A NudgeMutator is very similar to a simple nutator, except that it will
|
3
|
+
# only change a value by a small amount, rather than to any valid amount.
|
4
|
+
# So, an allele with a rather specified in the template of 1..100, with a
|
5
|
+
# current value of 50 might change in the range 45..55 instead of 1..100.
|
6
|
+
# @since 0.2.0
|
7
|
+
class NudgeMutator
|
8
|
+
def initialize(template, mutation_rate = 0.04)
|
9
|
+
@template = template
|
10
|
+
@mutation_rate = mutation_rate * 1
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(genes)
|
14
|
+
genes.each_with_index do |hash, index|
|
15
|
+
hash.each do |k, v|
|
16
|
+
if rand < @mutation_rate
|
17
|
+
nudge_max = (@template[index][k].size * 0.05).ceil
|
18
|
+
hash[k] = rand(
|
19
|
+
[@template[index][k].min, (v - nudge_max).floor].max..
|
20
|
+
[@template[index][k].max, (v + nudge_max).ceil].min
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
genes
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
@@ -9,21 +9,12 @@ module GeneGenie
|
|
9
9
|
private
|
10
10
|
|
11
11
|
def pick_one(pool)
|
12
|
-
proportional_index = rand(total_normalised_fitness
|
12
|
+
proportional_index = rand(pool.total_normalised_fitness)
|
13
13
|
total = 0
|
14
14
|
pool.genes.each_with_index do |gene, index|
|
15
|
-
total += normalised_fitness(
|
15
|
+
total += gene.normalised_fitness(pool.worst_fitness)
|
16
16
|
return gene if total >= proportional_index || index == (pool.size - 1)
|
17
17
|
end
|
18
18
|
end
|
19
|
-
|
20
|
-
def total_normalised_fitness(pool)
|
21
|
-
pool.genes.map { |gene| normalised_fitness(gene,pool) }.reduce(:+)
|
22
|
-
end
|
23
|
-
|
24
|
-
def normalised_fitness(gene,pool)
|
25
|
-
gene.fitness -
|
26
|
-
pool.worst_fitness + 1
|
27
|
-
end
|
28
19
|
end
|
29
20
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module GeneGenie
|
2
2
|
# A Template Evaluator provides certain analysis and useful information
|
3
|
-
# about templates
|
3
|
+
# about templates.
|
4
|
+
# A template is always treated internally as an Array of Hashes.
|
4
5
|
# @since 0.0.2
|
5
6
|
class TemplateEvaluator
|
6
7
|
def initialize(template)
|
@@ -13,15 +14,34 @@ module GeneGenie
|
|
13
14
|
}.reduce(:*)
|
14
15
|
end
|
15
16
|
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
17
|
+
# Suggests a recommended GenePool size.
|
18
|
+
# returns a minimum of 6 unless the total number of permutations
|
19
|
+
# is below that.
|
20
|
+
# Otherwise, returns 1/1000th of the number of permutations up to a
|
21
|
+
# maximum of 20000
|
20
22
|
def recommended_size
|
21
23
|
[
|
22
|
-
[(permutations
|
23
|
-
[
|
24
|
+
[((Math.log(permutations))**2).ceil, 20000].min,
|
25
|
+
[6, permutations].min,
|
26
|
+
3
|
24
27
|
].max
|
25
28
|
end
|
29
|
+
|
30
|
+
# Verifies that the given hash conforms to the constraints specified in the
|
31
|
+
# hash template
|
32
|
+
def hash_valid?(hash_under_test)
|
33
|
+
begin
|
34
|
+
@template.each_with_index do |h, index|
|
35
|
+
h.each do |k, v|
|
36
|
+
return false unless hash_under_test[index][k]
|
37
|
+
return false unless hash_under_test[index][k] >= v.min
|
38
|
+
return false unless hash_under_test[index][k] <= v.max
|
39
|
+
end
|
40
|
+
end
|
41
|
+
rescue
|
42
|
+
return false
|
43
|
+
end
|
44
|
+
return true
|
45
|
+
end
|
26
46
|
end
|
27
47
|
end
|
data/lib/gene_genie/version.rb
CHANGED
metadata
CHANGED
@@ -1,31 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gene_genie
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mark Coleman
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-10-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '1.6'
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '1.6'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: rake
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
30
16
|
requirements:
|
31
17
|
- - ">="
|
@@ -39,7 +25,7 @@ dependencies:
|
|
39
25
|
- !ruby/object:Gem::Version
|
40
26
|
version: '0'
|
41
27
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
28
|
+
name: rake
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
44
30
|
requirements:
|
45
31
|
- - ">="
|
@@ -53,7 +39,7 @@ dependencies:
|
|
53
39
|
- !ruby/object:Gem::Version
|
54
40
|
version: '0'
|
55
41
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
42
|
+
name: rspec
|
57
43
|
requirement: !ruby/object:Gem::Requirement
|
58
44
|
requirements:
|
59
45
|
- - ">="
|
@@ -75,11 +61,15 @@ extra_rdoc_files: []
|
|
75
61
|
files:
|
76
62
|
- README.md
|
77
63
|
- lib/gene_genie.rb
|
64
|
+
- lib/gene_genie/combiner/one_point_combiner.rb
|
65
|
+
- lib/gene_genie/combiner/uniform_combiner.rb
|
66
|
+
- lib/gene_genie/combiner/weighted_average_combiner.rb
|
78
67
|
- lib/gene_genie/gene.rb
|
79
68
|
- lib/gene_genie/gene_factory.rb
|
80
69
|
- lib/gene_genie/gene_pool.rb
|
81
70
|
- lib/gene_genie/genie.rb
|
82
71
|
- lib/gene_genie/listener/logging_listener.rb
|
72
|
+
- lib/gene_genie/mutator/nudge_mutator.rb
|
83
73
|
- lib/gene_genie/mutator/null_mutator.rb
|
84
74
|
- lib/gene_genie/mutator/simple_gene_mutator.rb
|
85
75
|
- lib/gene_genie/selector/coin_flip_selector.rb
|
@@ -90,7 +80,7 @@ homepage: https://github.com/MEHColeman/gene_genie
|
|
90
80
|
licenses:
|
91
81
|
- MIT
|
92
82
|
metadata: {}
|
93
|
-
post_install_message:
|
83
|
+
post_install_message:
|
94
84
|
rdoc_options: []
|
95
85
|
require_paths:
|
96
86
|
- lib
|
@@ -105,9 +95,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
105
95
|
- !ruby/object:Gem::Version
|
106
96
|
version: '0'
|
107
97
|
requirements: []
|
108
|
-
|
109
|
-
|
110
|
-
signing_key:
|
98
|
+
rubygems_version: 3.1.6
|
99
|
+
signing_key:
|
111
100
|
specification_version: 4
|
112
101
|
summary: Genetic algorithm optimisation gem
|
113
102
|
test_files: []
|