darwinning 0.0.1 → 0.0.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.
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.