darwinning 0.1.1 → 0.1.2

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 00597c08fc47e731a886be9e68771c1e5a1d5ddc
4
+ data.tar.gz: d6f8e46f456c6c9734fef044cada94ebe951b8f4
5
+ SHA512:
6
+ metadata.gz: 8e0963dc3e6dd57ad76aa25565f1714cfcd74449c01d72bd9dadc9161715c4395b16bef6ee92e6c452fe79ea7ab1ba2c539a55127397264fd823f5081e6b8dbd
7
+ data.tar.gz: 5fdf2aa0bebd1bed384489440e415611f3560711913ccf7c140a0c4651e703ad4b86f6b88334fd45abae32e3ae47016567b744b926bef2d2b809932b212a783c
data/README.md CHANGED
@@ -123,4 +123,4 @@ Check out the `/examples` folder for more examples. That seems like a good place
123
123
  * [Dave Schwantes](https://github.com/dorkrawk "dorkrawk")
124
124
 
125
125
  ### With help from:
126
- [lots of great contributers](https://github.com/dorkrawk/darwinning/graphs/contributors)
126
+ [lots of great contributers](https://github.com/dorkrawk/darwinning/graphs/contributors) and work based on forks from: [Nanosim-LIG](https://github.com/Nanosim-LIG)
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,18 @@
1
+ require "./lib/darwinning/version"
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'darwinning'
5
+ s.version = Darwinning::VERSION
6
+
7
+ s.authors = ['Dave Schwantes']
8
+ s.email = 'dave.schwantes@gmail.com'
9
+ s.summary = 'A Ruby gem to aid in the use of genetic algorithms.'
10
+ s.description = 'Darwinning provides tools to build genetic algorithm solutions using a Gene, Organism, and Population structure.'
11
+ s.homepage = 'https://github.com/dorkrawk/darwinning'
12
+
13
+
14
+ s.files = Dir["{lib}/**/*", "[A-Z]*"] - ["Gemfile.lock"]
15
+ s.require_paths = ["lib"]
16
+
17
+ s.test_files = Dir.glob("{test,spec,features}/**/*.rb")
18
+ end
@@ -4,6 +4,8 @@ require_relative 'darwinning/evolution_types/mutation'
4
4
  require_relative 'darwinning/evolution_types/reproduction'
5
5
  require_relative 'darwinning/population'
6
6
  require_relative 'darwinning/config'
7
+ require_relative 'darwinning/monkey_patch'
8
+
7
9
 
8
10
  module Darwinning
9
11
  extend Config
@@ -13,7 +15,7 @@ module Darwinning
13
15
  gene_ranges.map { |k,v| Gene.new(name: k, value_range: v) }
14
16
  end
15
17
 
16
- def base.build_population(fitness_goal, population_size = 10, generations_limit = 100,
18
+ def base.build_population(fitness_goal, population_size = 10, generations_limit = 100,
17
19
  evolution_types = Population::DEFAULT_EVOLUTION_TYPES)
18
20
  Population.new(organism: self, population_size: population_size,
19
21
  generations_limit: generations_limit, fitness_goal: fitness_goal,
@@ -61,4 +63,4 @@ module Darwinning
61
63
  end
62
64
  gt
63
65
  end
64
- end
66
+ end
@@ -20,7 +20,7 @@ module Darwinning
20
20
 
21
21
  def mutate(members)
22
22
  members.map do |member|
23
- if (0..100).to_a.sample < mutation_rate * 100
23
+ if rand < mutation_rate
24
24
  re_express_random_genotype(member)
25
25
  else
26
26
  member
@@ -30,7 +30,7 @@ module Darwinning
30
30
 
31
31
  # Selects a random genotype from the organism and re-expresses its gene
32
32
  def re_express_random_genotype(member)
33
- random_index = (0..member.genotypes.length - 1).to_a.sample
33
+ random_index = rand(member.genotypes.length - 1)
34
34
  gene = member.genes[random_index]
35
35
 
36
36
  if member.class.superclass == Darwinning::Organism
@@ -0,0 +1,7 @@
1
+ unless 42.respond_to? :positive?
2
+ class Numeric
3
+ def positive?
4
+ self > 0
5
+ end
6
+ end
7
+ end
@@ -46,9 +46,9 @@ module Darwinning
46
46
  @genotypes = genotypes
47
47
  end
48
48
 
49
- @fitness = -1
49
+ @fitness = nil
50
50
  end
51
-
51
+
52
52
  def name
53
53
  self.class.name
54
54
  end
@@ -1,9 +1,7 @@
1
1
  module Darwinning
2
2
  class Population
3
3
 
4
- EPSILON = 0.01
5
-
6
- attr_reader :members, :generations_limit, :fitness_goal,
4
+ attr_reader :members, :generations_limit, :fitness_goal, :fitness_objective,
7
5
  :organism, :population_size, :generation,
8
6
  :evolution_types, :history
9
7
 
@@ -16,6 +14,7 @@ module Darwinning
16
14
  @organism = options.fetch(:organism)
17
15
  @population_size = options.fetch(:population_size)
18
16
  @fitness_goal = options.fetch(:fitness_goal)
17
+ @fitness_objective = options.fetch(:fitness_objective, :nullify) # :nullify, :maximize, :minimize
19
18
  @generations_limit = options.fetch(:generations_limit, 0)
20
19
  @evolution_types = options.fetch(:evolution_types, DEFAULT_EVOLUTION_TYPES)
21
20
  @members = []
@@ -23,7 +22,6 @@ module Darwinning
23
22
  @history = []
24
23
 
25
24
  build_population(@population_size)
26
- @history << @members
27
25
  end
28
26
 
29
27
  def build_population(population_size)
@@ -41,16 +39,19 @@ module Darwinning
41
39
  def set_members_fitness!(fitness_values)
42
40
  throw "Invaid number of fitness values for population size" if fitness_values.size != members.size
43
41
  members.to_enum.each_with_index { |m, i| m.fitness = fitness_values[i] }
42
+ sort_members
44
43
  end
45
44
 
46
45
  def make_next_generation!
47
46
  verify_population_size_is_positive!
47
+ sort_members
48
+ @history << @members
48
49
 
49
50
  new_members = []
50
51
 
51
52
  until new_members.length >= members.length
52
- m1 = weighted_select(members)
53
- m2 = weighted_select(members)
53
+ m1 = weighted_select
54
+ m2 = weighted_select
54
55
 
55
56
  new_members += apply_pairwise_evolutions(m1, m2)
56
57
  end
@@ -59,6 +60,7 @@ module Darwinning
59
60
  new_members.pop if new_members.length > members.length
60
61
 
61
62
  @members = apply_non_pairwise_evolutions(new_members)
63
+ sort_members
62
64
  @history << @members
63
65
  @generation += 1
64
66
  end
@@ -66,14 +68,18 @@ module Darwinning
66
68
  def evolution_over?
67
69
  # check if the fitness goal or generation limit has been met
68
70
  if generations_limit > 0
69
- generation == generations_limit || best_member.fitness == fitness_goal
71
+ generation == generations_limit || goal_attained?
70
72
  else
71
- best_member.fitness == fitness_goal
73
+ goal_attained?
72
74
  end
73
75
  end
74
76
 
75
77
  def best_member
76
- @members.sort_by { |m| m.fitness }.first
78
+ @members.first
79
+ end
80
+
81
+ def best_each_generation
82
+ @history.map(&:first)
77
83
  end
78
84
 
79
85
  def size
@@ -91,6 +97,28 @@ module Darwinning
91
97
 
92
98
  private
93
99
 
100
+ def goal_attained?
101
+ case @fitness_objective
102
+ when :nullify
103
+ best_member.fitness.abs <= fitness_goal
104
+ when :maximize
105
+ best_member.fitness >= fitness_goal
106
+ else
107
+ best_member.fitness <= fitness_goal
108
+ end
109
+ end
110
+
111
+ def sort_members
112
+ case @fitness_objective
113
+ when :nullify
114
+ @members = @members.sort_by { |m| m.fitness ? m.fitness.abs : m.fitness }
115
+ when :maximize
116
+ @members = @members.sort_by { |m| m.fitness }.reverse
117
+ else
118
+ @members = @members.sort_by { |m| m.fitness }
119
+ end
120
+ end
121
+
94
122
  def verify_population_size_is_positive!
95
123
  unless @population_size.positive?
96
124
  raise "Population size must be a positive number!"
@@ -99,7 +127,7 @@ module Darwinning
99
127
 
100
128
  def build_member
101
129
  member = organism.new
102
- unless member.class.superclass == Darwinning::Organism
130
+ unless member.class < Darwinning::Organism
103
131
  member.class.genes.each do |gene|
104
132
  gene_expression = gene.express
105
133
  member.send("#{gene.name}=", gene_expression)
@@ -108,27 +136,49 @@ module Darwinning
108
136
  member
109
137
  end
110
138
 
111
- def weighted_select(members)
112
- fitness_sum = members.inject(0) { |sum, m| sum + m.fitness }
113
-
114
- weighted_members = members.sort_by do |m|
115
- (m.fitness - fitness_goal).abs
116
- end.map do |m|
117
- [m, fitness_sum / ((m.fitness - fitness_goal).abs + EPSILON)]
139
+ def compute_normalized_fitness(membs=members)
140
+ normalized_fitness = nil
141
+ return membs.collect { |m| [1.0/membs.length, m] } if membs.first.fitness == membs.last.fitness
142
+ if @fitness_objective == :nullify
143
+ normalized_fitness = membs.collect { |m| [ m.fitness.abs <= fitness_goal ? Float::INFINITY : 1.0/(m.fitness.abs - fitness_goal), m] }
144
+ else
145
+ if @fitness_objective == :maximize
146
+ if fitness_goal == Float::INFINITY then
147
+ #assume goal to be at twice the maximum distance between fitness
148
+ goal = membs.first.fitness + ( membs.first.fitness - membs.last.fitness )
149
+ else
150
+ goal = fitness_goal
151
+ end
152
+ normalized_fitness = membs.collect { |m| [ m.fitness >= goal ? Float::INFINITY : 1.0/(goal - m.fitness), m] }
153
+ else
154
+ if fitness_goal == -Float::INFINITY then
155
+ goal = membs.first.fitness - ( membs.last.fitness - membs.first.fitness )
156
+ else
157
+ goal = fitness_goal
158
+ end
159
+ normalized_fitness = membs.collect { |m| [ m.fitness <= goal ? Float::INFINITY : 1.0/(m.fitness - goal), m] }
160
+ end
118
161
  end
119
-
120
- weight_sum = weighted_members.inject(0) { |sum, m| sum + m[1] }
121
- pick = (0..weight_sum).to_a.sample
122
-
123
- weighted_members.reverse! # In order to pop from the end we need the lowest ranked first
124
- pick_sum = 0
125
-
126
- until pick_sum > pick do
127
- selected_member = weighted_members.pop
128
- pick_sum += selected_member[1]
162
+ if normalized_fitness.first[0] == Float::INFINITY then
163
+ normalized_fitness.collect! { |m|
164
+ m[0] == Float::INFINITY ? [1.0, m[1]] : [0.0, m[1]]
165
+ }
129
166
  end
167
+ sum = normalized_fitness.collect(&:first).inject(0.0, :+)
168
+ normalized_fitness.collect { |m| [m[0]/sum, m[1]] }
169
+ end
130
170
 
131
- selected_member.first
171
+ def weighted_select(membs=members)
172
+ normalized_fitness = compute_normalized_fitness
173
+ normalized_cumulative_sums = []
174
+ normalized_cumulative_sums[0] = normalized_fitness[0]
175
+ (1...normalized_fitness.length).each { |i|
176
+ normalized_cumulative_sums[i] = [ normalized_cumulative_sums[i-1][0] + normalized_fitness[i][0], normalized_fitness[i][1] ]
177
+ }
178
+
179
+ normalized_cumulative_sums.last[0] = 1.0
180
+ cut = rand
181
+ return normalized_cumulative_sums.find { |e| cut < e[0] }[1]
132
182
  end
133
183
 
134
184
  def apply_pairwise_evolutions(m1, m2)
@@ -1,3 +1,3 @@
1
1
  module Darwinning
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
@@ -1,4 +1,4 @@
1
- require 'spec_helper'
1
+ require_relative 'spec_helper'
2
2
 
3
3
  describe Darwinning do
4
4
  let(:triple_pop) { NewTriple.build_population(0, 20, 1000) }
@@ -18,7 +18,7 @@ describe Darwinning do
18
18
  manual_fitness = (a_triple.first_digit + a_triple.second_digit + a_triple.third_digit - 15).abs
19
19
 
20
20
  expect(a_triple.fitness).to eq manual_fitness
21
- end
21
+ end
22
22
  end
23
23
 
24
24
  describe '#genes' do
@@ -53,7 +53,7 @@ describe Darwinning do
53
53
  end
54
54
  end
55
55
 
56
- describe 'population member' do
56
+ describe 'population member' do
57
57
  it 'is of the parent class' do
58
58
  expect(triple_pop_member.class.name).to eq "NewTriple"
59
59
  end
@@ -1,4 +1,4 @@
1
- require 'spec_helper'
1
+ require_relative 'spec_helper'
2
2
 
3
3
  describe Darwinning::Gene do
4
4
  let(:digit) { Darwinning::Gene.new(name: "digit", value_range: (0..9)) }
@@ -10,7 +10,7 @@ describe Darwinning::Gene do
10
10
  units: "o'clock"
11
11
  )
12
12
  }
13
-
13
+
14
14
  it "name should be set" do
15
15
  expect(digit.name).to eq "digit"
16
16
  end
@@ -26,4 +26,4 @@ describe Darwinning::Gene do
26
26
  it "units should be set" do
27
27
  expect(day_hour.units).to eq "o'clock"
28
28
  end
29
- end
29
+ end
@@ -1,4 +1,4 @@
1
- require 'spec_helper'
1
+ require_relative 'spec_helper'
2
2
 
3
3
  describe Darwinning::Organism do
4
4
  let(:org) { Darwinning::Organism.new }
@@ -16,8 +16,8 @@ describe Darwinning::Organism do
16
16
  expect(org.genotypes).to be {}
17
17
  end
18
18
 
19
- it "fitness should default to -1" do
20
- expect(org.fitness).to eq -1
19
+ it "fitness should default to nil" do
20
+ expect(org.fitness).to eq nil
21
21
  end
22
22
 
23
23
  it "child class should set name" do
@@ -32,4 +32,4 @@ describe Darwinning::Organism do
32
32
  expect(triple.genotypes.length).to eq 3
33
33
  end
34
34
 
35
- end
35
+ end
@@ -1,4 +1,4 @@
1
- require 'spec_helper'
1
+ require_relative 'spec_helper'
2
2
 
3
3
  describe Darwinning::Population do
4
4
  let(:pop_triple) {
@@ -58,4 +58,12 @@ describe Darwinning::Population do
58
58
 
59
59
  end
60
60
 
61
+ describe "#best_each_generation" do
62
+
63
+ it "should show the best member from each generations members" do
64
+ pop_triple.evolve!
65
+ expect(pop_triple.best_each_generation.last).to eq pop_triple.best_member
66
+ end
67
+ end
68
+
61
69
  end
metadata CHANGED
@@ -1,15 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: darwinning
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
5
- prerelease:
4
+ version: 0.1.2
6
5
  platform: ruby
7
6
  authors:
8
7
  - Dave Schwantes
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2016-10-26 00:00:00.000000000 Z
11
+ date: 2017-09-15 00:00:00.000000000 Z
13
12
  dependencies: []
14
13
  description: Darwinning provides tools to build genetic algorithm solutions using
15
14
  a Gene, Organism, and Population structure.
@@ -18,18 +17,25 @@ executables: []
18
17
  extensions: []
19
18
  extra_rdoc_files: []
20
19
  files:
20
+ - Gemfile
21
+ - README.md
22
+ - Rakefile
23
+ - darwinning-0.0.1.gem
24
+ - darwinning-0.0.2.gem
25
+ - darwinning-0.0.3.gem
26
+ - darwinning-0.1.0.gem
27
+ - darwinning-0.1.1.gem
28
+ - darwinning.gemspec
29
+ - lib/darwinning.rb
21
30
  - lib/darwinning/config.rb
31
+ - lib/darwinning/evolution_types.rb
22
32
  - lib/darwinning/evolution_types/mutation.rb
23
33
  - lib/darwinning/evolution_types/reproduction.rb
24
- - lib/darwinning/evolution_types.rb
25
34
  - lib/darwinning/gene.rb
35
+ - lib/darwinning/monkey_patch.rb
26
36
  - lib/darwinning/organism.rb
27
37
  - lib/darwinning/population.rb
28
38
  - lib/darwinning/version.rb
29
- - lib/darwinning.rb
30
- - Gemfile
31
- - Rakefile
32
- - README.md
33
39
  - spec/classes/chimps.rb
34
40
  - spec/classes/new_single.rb
35
41
  - spec/classes/new_triple.rb
@@ -43,27 +49,26 @@ files:
43
49
  - spec/spec_helper.rb
44
50
  homepage: https://github.com/dorkrawk/darwinning
45
51
  licenses: []
52
+ metadata: {}
46
53
  post_install_message:
47
54
  rdoc_options: []
48
55
  require_paths:
49
56
  - lib
50
57
  required_ruby_version: !ruby/object:Gem::Requirement
51
- none: false
52
58
  requirements:
53
- - - ! '>='
59
+ - - ">="
54
60
  - !ruby/object:Gem::Version
55
61
  version: '0'
56
62
  required_rubygems_version: !ruby/object:Gem::Requirement
57
- none: false
58
63
  requirements:
59
- - - ! '>='
64
+ - - ">="
60
65
  - !ruby/object:Gem::Version
61
66
  version: '0'
62
67
  requirements: []
63
68
  rubyforge_project:
64
- rubygems_version: 1.8.24
69
+ rubygems_version: 2.4.5
65
70
  signing_key:
66
- specification_version: 3
71
+ specification_version: 4
67
72
  summary: A Ruby gem to aid in the use of genetic algorithms.
68
73
  test_files:
69
74
  - spec/classes/chimps.rb