darwinning 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,7 +1,18 @@
1
1
  Darwinning
2
2
  ==========
3
+ [![Gem Version](https://badge.fury.io/rb/darwinning.png)](http://badge.fury.io/rb/darwinning)
4
+
5
+ [gem]: https://rubygems.org/gems/darwinning
6
+
3
7
  A Ruby gem to aid in the use of genetic algorithms.
4
8
 
9
+ Installation
10
+ --------
11
+
12
+ ```
13
+ gem install darwinning
14
+ ```
15
+
5
16
  Examples
6
17
  --------
7
18
 
@@ -28,7 +39,7 @@ class Triple < Darwinning::Organism
28
39
  end
29
40
 
30
41
  p = Darwinning::Population.new(Triple, 10, 0, 0.1, 100)
31
- p.evolve
42
+ p.evolve!
32
43
 
33
44
  p.best_member.nice_print # prints the member representing the solution
34
45
  ```
data/lib/darwinning.rb CHANGED
@@ -4,5 +4,5 @@ require_relative 'darwinning/population'
4
4
  require_relative 'darwinning/config'
5
5
 
6
6
  module Darwinning
7
- extend Config
7
+ extend Config
8
8
  end
@@ -1,6 +1,6 @@
1
1
  module Darwinning
2
- module Config
3
- # crossover mask?
4
- # ordered vs weighted fitness
5
- end
2
+ module Config
3
+ # crossover mask?
4
+ # ordered vs weighted fitness
5
+ end
6
6
  end
@@ -1,21 +1,21 @@
1
1
  module Darwinning
2
- class Gene
3
- attr_accessor :name, :value, :value_range, :invalid_values, :units
2
+ class Gene
3
+ attr_accessor :name, :value, :value_range, :invalid_values, :units
4
4
 
5
- def initialize(name = "", value_range = [], invalid_values = [], units = "")
5
+ def initialize(name = "", value_range = [], invalid_values = [], units = "")
6
6
 
7
- @name = name
8
- @value_range = value_range.to_a
9
- @invalid_values = invalid_values.to_a
10
- @units = units
11
- end
7
+ @name = name
8
+ @value_range = value_range.to_a
9
+ @invalid_values = invalid_values.to_a
10
+ @units = units
11
+ end
12
12
 
13
- def express
14
- (@value_range - @invalid_values).sample
15
- end
13
+ def express
14
+ (@value_range - @invalid_values).sample
15
+ end
16
16
 
17
- def is_valid_value?(value)
18
- @value_range.include?(value) and not @invalid_values.include?(value)
19
- end
20
- end
17
+ def is_valid_value?(value)
18
+ @value_range.include?(value) and not @invalid_values.include?(value)
19
+ end
20
+ end
21
21
  end
@@ -1,9 +1,9 @@
1
1
  # Found from http://www.railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/
2
2
  module ClassLevelInheritableAttributes
3
3
  def self.included(base)
4
- base.extend(ClassMethods)
4
+ base.extend(ClassMethods)
5
5
  end
6
-
6
+
7
7
  module ClassMethods
8
8
  def inheritable_attributes(*args)
9
9
  @inheritable_attributes ||= [:inheritable_attributes]
@@ -15,7 +15,7 @@ module ClassLevelInheritableAttributes
15
15
  end
16
16
  @inheritable_attributes
17
17
  end
18
-
18
+
19
19
  def inherited(subclass)
20
20
  @inheritable_attributes.each do |inheritable_attribute|
21
21
  instance_var = "@#{inheritable_attribute}"
@@ -26,47 +26,47 @@ module ClassLevelInheritableAttributes
26
26
  end
27
27
 
28
28
  module Darwinning
29
- class Organism
29
+ class Organism
30
30
 
31
- include ClassLevelInheritableAttributes
32
- inheritable_attributes :genes, :name
33
- attr_accessor :genotypes, :fitness, :name, :genes
31
+ include ClassLevelInheritableAttributes
32
+ inheritable_attributes :genes, :name
33
+ attr_accessor :genotypes, :fitness, :name, :genes
34
34
 
35
- @genes = [] # Gene instances
36
- @name = ""
35
+ @genes = [] # Gene instances
36
+ @name = ""
37
37
 
38
- def initialize(genotypes = [])
39
- #TODO: catch errors if genotype.length != @genotypes.length
40
- # catch if genotype[x] is not a valid value for @gene[x]
38
+ def initialize(genotypes = [])
39
+ #TODO: catch errors if genotype.length != @genotypes.length
40
+ # catch if genotype[x] is not a valid value for @gene[x]
41
41
 
42
- if genotypes == []
43
- # fill genotypes with expressed Genes
44
- @genotypes = self.class.genes.map { |g| g.express } # Gene expressions
45
- else
46
- @genotypes = genotypes
47
- end
48
- @fitness = -1
49
- end
42
+ if genotypes == []
43
+ # fill genotypes with expressed Genes
44
+ @genotypes = self.class.genes.map { |g| g.express } # Gene expressions
45
+ else
46
+ @genotypes = genotypes
47
+ end
48
+ @fitness = -1
49
+ end
50
50
 
51
- # Selects a random genotype from the organism and rexpresses its gene
52
- def mutate!
53
- random_index = (0..@genotypes.length-1).to_a.sample
54
- @genotypes[random_index] = self.class.genes[random_index].express
55
- self
56
- end
51
+ # Selects a random genotype from the organism and rexpresses its gene
52
+ def mutate!
53
+ random_index = (0..@genotypes.length-1).to_a.sample
54
+ @genotypes[random_index] = self.class.genes[random_index].express
55
+ self
56
+ end
57
57
 
58
- def nice_print
59
- puts self.class.name == "" ? "[no name]" : self.class.name
60
- self.class.genes.to_enum.each_with_index { |g, i| puts " #{g.name}: #{@genotypes[i]} #{g.units}" }
61
- puts " fitness: #{fitness}"
62
- end
58
+ def nice_print
59
+ puts self.class.name == "" ? "[no name]" : self.class.name
60
+ self.class.genes.to_enum.each_with_index { |g, i| puts " #{g.name}: #{@genotypes[i]} #{g.units}" }
61
+ puts " fitness: #{fitness}"
62
+ end
63
63
 
64
- def name
65
- self.class.name
66
- end
64
+ def name
65
+ self.class.name
66
+ end
67
67
 
68
- def genes
69
- self.class.genes
70
- end
71
- end
68
+ def genes
69
+ self.class.genes
70
+ end
71
+ end
72
72
  end
@@ -1,120 +1,120 @@
1
1
  module Darwinning
2
2
 
3
- class Population
4
- attr_accessor :members, :mutation_rate, :generations_limit, :fitness_goal, :organism, :generation
5
-
6
- def initialize(organism, population_size, fitness_goal, mutation_rate = 0.0, generations_limit = 0, manual_fitness = false)
7
- @organism = organism
8
- @fitness_goal = fitness_goal
9
- @mutation_rate = mutation_rate
10
- @generations_limit = generations_limit
11
- @manual_fitness = manual_fitness
12
- @members = []
13
- @generation = 0 # initial population is generation 0
14
-
15
- build_population(population_size)
16
- end
17
-
18
- def build_population(population_size)
19
- population_size.times do |i|
20
- @members << @organism.new
21
- end
22
- end
23
-
24
- def evolve
25
- until evolution_over?
26
- make_next_generation!
27
- end
28
- end
29
-
30
- def crossover(m1, m2)
31
- genotypes1 = []
32
- genotypes2 = []
33
-
34
- m1.genotypes.zip(m2.genotypes).each do |g|
35
- if m1.genotypes.index(g[0]) % 2 == 0
36
- genotypes1 << g[0]
37
- genotypes2 << g[1]
38
- else
39
- genotypes1 << g[1]
40
- genotypes2 << g[0]
41
- end
42
- end
43
-
44
- [@organism.new(genotypes1), @organism.new(genotypes2)]
45
- end
46
-
47
- def sexytimes(m1, m2)
48
- crossover(m1, m2)
49
- end
50
-
51
- def weighted_select(members)
52
- e = 0.01
53
- fitness_sum = members.inject(0) { |sum, m| sum + m.fitness }
54
-
55
- weighted_members = members.sort_by { |m| (m.fitness - @fitness_goal).abs }.map { |m| [m, fitness_sum / ((m.fitness - @fitness_goal).abs + e)] }
56
-
57
- weight_sum = weighted_members.inject(0) { |sum, m| sum + m[1] }
58
- pick = (0..weight_sum).to_a.sample
59
-
60
- weighted_members.reverse! # In order to pop from the end we need the lowest ranked first
61
- pick_sum = 0
62
-
63
- until pick_sum > pick do
64
- selected_member = weighted_members.pop
65
- pick_sum += selected_member[1]
66
- end
67
-
68
- selected_member[0]
69
- end
70
-
71
- def mutate!
72
- @members.map! { |m|
73
- if (0..100).to_a.sample < @mutation_rate*100
74
- m.mutate!
75
- else
76
- m
77
- end
78
- }
79
- end
80
-
81
- def set_members_fitness!(fitness_values)
82
- @members.to_enum.each_with_index { |m, i| m.fitness = fitness_values[i] }
83
- end
84
-
85
- def make_next_generation!
86
- temp_members = @members
87
- used_members = []
88
- new_members = []
89
-
90
- until new_members.length == @members.length/2
91
- m1 = weighted_select(@members - used_members)
92
- used_members << m1
93
- m2 = weighted_select(@members - used_members)
94
- used_members << m2
95
-
96
- new_members << crossover(m1,m2)
97
- end
98
-
99
- new_members.flatten!
100
-
101
- @members = new_members
102
-
103
- mutate!
104
- @generation += 1
105
- end
106
-
107
- def evolution_over?
108
- # check if the fiteness goal or generation limit has been met
109
- @generation == @generations_limit or best_member.fitness == @fitness_goal
110
- end
111
-
112
- def best_member
113
- @members.sort_by { |m| m.fitness }[0]
114
- end
115
-
116
- def size
117
- @members.length
118
- end
119
- end
3
+ class Population
4
+ attr_accessor :members, :mutation_rate, :generations_limit, :fitness_goal, :organism, :generation
5
+
6
+ def initialize(organism, population_size, fitness_goal, mutation_rate = 0.0, generations_limit = 0, manual_fitness = false)
7
+ @organism = organism
8
+ @fitness_goal = fitness_goal
9
+ @mutation_rate = mutation_rate
10
+ @generations_limit = generations_limit
11
+ @manual_fitness = manual_fitness
12
+ @members = []
13
+ @generation = 0 # initial population is generation 0
14
+
15
+ build_population(population_size)
16
+ end
17
+
18
+ def build_population(population_size)
19
+ population_size.times do |i|
20
+ @members << @organism.new
21
+ end
22
+ end
23
+
24
+ def evolve!
25
+ until evolution_over?
26
+ make_next_generation!
27
+ end
28
+ end
29
+
30
+ def crossover(m1, m2)
31
+ genotypes1 = []
32
+ genotypes2 = []
33
+
34
+ m1.genotypes.zip(m2.genotypes).each do |g|
35
+ if m1.genotypes.index(g[0]) % 2 == 0
36
+ genotypes1 << g[0]
37
+ genotypes2 << g[1]
38
+ else
39
+ genotypes1 << g[1]
40
+ genotypes2 << g[0]
41
+ end
42
+ end
43
+
44
+ [@organism.new(genotypes1), @organism.new(genotypes2)]
45
+ end
46
+
47
+ def sexytimes(m1, m2)
48
+ crossover(m1, m2)
49
+ end
50
+
51
+ def weighted_select(members)
52
+ e = 0.01
53
+ fitness_sum = members.inject(0) { |sum, m| sum + m.fitness }
54
+
55
+ weighted_members = members.sort_by { |m| (m.fitness - @fitness_goal).abs }.map { |m| [m, fitness_sum / ((m.fitness - @fitness_goal).abs + e)] }
56
+
57
+ weight_sum = weighted_members.inject(0) { |sum, m| sum + m[1] }
58
+ pick = (0..weight_sum).to_a.sample
59
+
60
+ weighted_members.reverse! # In order to pop from the end we need the lowest ranked first
61
+ pick_sum = 0
62
+
63
+ until pick_sum > pick do
64
+ selected_member = weighted_members.pop
65
+ pick_sum += selected_member[1]
66
+ end
67
+
68
+ selected_member[0]
69
+ end
70
+
71
+ def mutate!
72
+ @members.map! { |m|
73
+ if (0..100).to_a.sample < @mutation_rate*100
74
+ m.mutate!
75
+ else
76
+ m
77
+ end
78
+ }
79
+ end
80
+
81
+ def set_members_fitness!(fitness_values)
82
+ @members.to_enum.each_with_index { |m, i| m.fitness = fitness_values[i] }
83
+ end
84
+
85
+ def make_next_generation!
86
+ temp_members = @members
87
+ used_members = []
88
+ new_members = []
89
+
90
+ until new_members.length == @members.length/2
91
+ m1 = weighted_select(@members - used_members)
92
+ used_members << m1
93
+ m2 = weighted_select(@members - used_members)
94
+ used_members << m2
95
+
96
+ new_members << crossover(m1,m2)
97
+ end
98
+
99
+ new_members.flatten!
100
+
101
+ @members = new_members
102
+
103
+ mutate!
104
+ @generation += 1
105
+ end
106
+
107
+ def evolution_over?
108
+ # check if the fiteness goal or generation limit has been met
109
+ @generation == @generations_limit or best_member.fitness == @fitness_goal
110
+ end
111
+
112
+ def best_member
113
+ @members.sort_by { |m| m.fitness }[0]
114
+ end
115
+
116
+ def size
117
+ @members.length
118
+ end
119
+ end
120
120
  end
@@ -1,3 +1,3 @@
1
1
  module Darwinning
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -1,87 +1,87 @@
1
1
  require 'darwinning'
2
2
 
3
3
  describe Darwinning::Gene do
4
- before do
5
- @digit = Darwinning::Gene.new("digit", (0..9))
6
- @day_hour = Darwinning::Gene.new("hour", (0..23), [0,1,2,3,4,20,21,22,23], "o'clock")
7
- end
4
+ before do
5
+ @digit = Darwinning::Gene.new("digit", (0..9))
6
+ @day_hour = Darwinning::Gene.new("hour", (0..23), [0,1,2,3,4,20,21,22,23], "o'clock")
7
+ end
8
8
 
9
- it "name should be set" do
10
- @digit.name.should == "digit"
11
- end
9
+ it "name should be set" do
10
+ @digit.name.should == "digit"
11
+ end
12
12
 
13
- it "value range should be set" do
14
- @digit.value_range.should == (0..9).to_a
15
- end
13
+ it "value range should be set" do
14
+ @digit.value_range.should == (0..9).to_a
15
+ end
16
16
 
17
- it "invalid values should be set" do
18
- @day_hour.invalid_values.should == [0,1,2,3,4,20,21,22,23]
19
- end
17
+ it "invalid values should be set" do
18
+ @day_hour.invalid_values.should == [0,1,2,3,4,20,21,22,23]
19
+ end
20
20
 
21
- it "units should be set" do
22
- @day_hour.units.should == "o'clock"
23
- end
21
+ it "units should be set" do
22
+ @day_hour.units.should == "o'clock"
23
+ end
24
24
 
25
- describe "#express" do
25
+ describe "#express" do
26
26
 
27
- it "expressed value should be within range" do
28
- (0..9).to_a.include?(@digit.express).should == true # uncertain test
29
- end
27
+ it "expressed value should be within range" do
28
+ (0..9).to_a.include?(@digit.express).should == true # uncertain test
29
+ end
30
30
 
31
- it "expressed value should not be invalid value" do
32
- @day_hour.invalid_values.include?(@digit.express).should == false # uncertain test
33
- end
34
- end
31
+ it "expressed value should not be invalid value" do
32
+ @day_hour.invalid_values.include?(@digit.express).should == false # uncertain test
33
+ end
34
+ end
35
35
 
36
36
  end
37
37
 
38
38
  describe Darwinning::Organism do
39
- before do
40
- @org = Darwinning::Organism.new
41
-
42
- class Triple < Darwinning::Organism
43
- @name = "Triple"
44
- @genes = [
45
- Darwinning::Gene.new("first digit", (0..9)),
46
- Darwinning::Gene.new("second digit", (0..9)),
47
- Darwinning::Gene.new("third digit", (0..9))
48
- ]
49
-
50
- def fitness
51
- # Try to get the sum of the 3 digits to add up to 15
52
- (genotypes.inject{ |sum, x| sum + x } - 15).abs
53
- end
54
- end
55
- @triple = Triple.new
56
- end
57
-
58
- it "name should default to blank" do
59
- @org.name.should == ""
60
- end
61
-
62
- it "genes should default to empty array" do
63
- @org.genes.should == []
64
- end
65
-
66
- it "genotypes should initialize to empty array if genes is empty" do
67
- @org.genotypes.should == []
68
- end
69
-
70
- it "fitness should default to -1" do
71
- @org.fitness.should == -1
72
- end
73
-
74
- it "child class should set name" do
75
- @triple.name.should == "Triple"
76
- end
77
-
78
- it "child class should set genes" do
79
- @triple.genes.length.should == 3 # not the best test...
80
- end
81
-
82
- it "child class should initialize genotypes from genes" do
83
- @triple.genotypes.length.should == 3 # not the best test...
84
- end
39
+ before do
40
+ @org = Darwinning::Organism.new
41
+
42
+ class Triple < Darwinning::Organism
43
+ @name = "Triple"
44
+ @genes = [
45
+ Darwinning::Gene.new("first digit", (0..9)),
46
+ Darwinning::Gene.new("second digit", (0..9)),
47
+ Darwinning::Gene.new("third digit", (0..9))
48
+ ]
49
+
50
+ def fitness
51
+ # Try to get the sum of the 3 digits to add up to 15
52
+ (genotypes.inject{ |sum, x| sum + x } - 15).abs
53
+ end
54
+ end
55
+ @triple = Triple.new
56
+ end
57
+
58
+ it "name should default to blank" do
59
+ @org.name.should == ""
60
+ end
61
+
62
+ it "genes should default to empty array" do
63
+ @org.genes.should == []
64
+ end
65
+
66
+ it "genotypes should initialize to empty array if genes is empty" do
67
+ @org.genotypes.should == []
68
+ end
69
+
70
+ it "fitness should default to -1" do
71
+ @org.fitness.should == -1
72
+ end
73
+
74
+ it "child class should set name" do
75
+ @triple.name.should == "Triple"
76
+ end
77
+
78
+ it "child class should set genes" do
79
+ @triple.genes.length.should == 3 # not the best test...
80
+ end
81
+
82
+ it "child class should initialize genotypes from genes" do
83
+ @triple.genotypes.length.should == 3 # not the best test...
84
+ end
85
85
 
86
86
  end
87
87
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: darwinning
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-23 00:00:00.000000000 Z
12
+ date: 2013-05-29 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Darwinning provides tools to build genetic algorithm solutions using
15
15
  a Gene, Organism, and Population structure.