gene 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/Manifest +46 -0
  2. data/Rakefile +12 -9
  3. data/TODO +7 -0
  4. data/gene.gemspec +34 -0
  5. data/initializers/functional_extensions.rb +44 -0
  6. data/initializers/module_extensions.rb +14 -0
  7. data/initializers/object_extensions.rb +25 -0
  8. data/initializers/range_extensions.rb +9 -0
  9. data/initializers/runner.rb +15 -0
  10. data/initializers/symbol_extensions.rb +11 -0
  11. data/initializers/unbound_method_extensions.rb +3 -0
  12. data/lib/aligner.rb +34 -0
  13. data/lib/calculator.rb +42 -0
  14. data/lib/cell.rb +41 -0
  15. data/lib/color.rb +9 -0
  16. data/lib/dsl.rb +16 -0
  17. data/lib/gene.rb +67 -3
  18. data/lib/generator.rb +83 -0
  19. data/lib/geometry.rb +64 -0
  20. data/lib/hungarian.rb +205 -0
  21. data/lib/imagine.rb +22 -0
  22. data/lib/petri.rb +85 -0
  23. data/lib/point.rb +1 -0
  24. data/lib/trait.rb +60 -0
  25. data/tasks/test.rake +23 -0
  26. data/test/assets/Nova.jpg +0 -0
  27. data/test/assets/Rex.jpg +0 -0
  28. data/test/assets/Squares.jpg +0 -0
  29. data/test/test_helper.rb +6 -0
  30. data/test/unit/aligner_test.rb +91 -0
  31. data/test/unit/calculator_test.rb +100 -0
  32. data/test/unit/cell_test.rb +64 -0
  33. data/test/unit/color_test.rb +23 -0
  34. data/test/unit/dsl_test.rb +45 -0
  35. data/test/unit/functionals_extensions_test.rb +51 -0
  36. data/test/unit/gene_test.rb +76 -0
  37. data/test/unit/generator_test.rb +76 -0
  38. data/test/unit/geometry_test.rb +57 -0
  39. data/test/unit/hungarian_test.rb +196 -0
  40. data/test/unit/imagine_test.rb +54 -0
  41. data/test/unit/module_extensions_test.rb +40 -0
  42. data/test/unit/object_extensions_test.rb +34 -0
  43. data/test/unit/petri_test.rb +87 -0
  44. data/test/unit/range_extensions_test.rb +29 -0
  45. data/test/unit/symbol_extensions_test.rb +18 -0
  46. data/test/unit/trait_test.rb +97 -0
  47. data/test/unit/unbound_method_extensions_test.rb +11 -0
  48. metadata +118 -30
  49. data/History.txt +0 -6
  50. data/Manifest.txt +0 -7
  51. data/README.txt +0 -48
  52. data/bin/gene +0 -3
  53. data/test/test_gene.rb +0 -8
@@ -0,0 +1,46 @@
1
+ Manifest
2
+ Rakefile
3
+ TODO
4
+ initializers/functional_extensions.rb
5
+ initializers/module_extensions.rb
6
+ initializers/object_extensions.rb
7
+ initializers/range_extensions.rb
8
+ initializers/runner.rb
9
+ initializers/symbol_extensions.rb
10
+ initializers/unbound_method_extensions.rb
11
+ lib/aligner.rb
12
+ lib/calculator.rb
13
+ lib/cell.rb
14
+ lib/color.rb
15
+ lib/dsl.rb
16
+ lib/gene.rb
17
+ lib/generator.rb
18
+ lib/geometry.rb
19
+ lib/hungarian.rb
20
+ lib/imagine.rb
21
+ lib/petri.rb
22
+ lib/point.rb
23
+ lib/trait.rb
24
+ tasks/test.rake
25
+ test/assets/Nova.jpg
26
+ test/assets/Rex.jpg
27
+ test/assets/Squares.jpg
28
+ test/test_helper.rb
29
+ test/unit/aligner_test.rb
30
+ test/unit/calculator_test.rb
31
+ test/unit/cell_test.rb
32
+ test/unit/color_test.rb
33
+ test/unit/dsl_test.rb
34
+ test/unit/functionals_extensions_test.rb
35
+ test/unit/gene_test.rb
36
+ test/unit/generator_test.rb
37
+ test/unit/geometry_test.rb
38
+ test/unit/hungarian_test.rb
39
+ test/unit/imagine_test.rb
40
+ test/unit/module_extensions_test.rb
41
+ test/unit/object_extensions_test.rb
42
+ test/unit/petri_test.rb
43
+ test/unit/range_extensions_test.rb
44
+ test/unit/symbol_extensions_test.rb
45
+ test/unit/trait_test.rb
46
+ test/unit/unbound_method_extensions_test.rb
data/Rakefile CHANGED
@@ -1,12 +1,15 @@
1
- # -*- ruby -*-
1
+ require "rubygems"
2
+ require "rake"
3
+ require "echoe"
2
4
 
3
- require 'rubygems'
4
- require 'hoe'
5
- require './lib/gene.rb'
6
-
7
- Hoe.new('gene', Gene::VERSION) do |p|
8
- # p.rubyforge_name = 'genex' # if different than lowercase project name
9
- p.developer('Evan Senter', 'evansenter@gmail.com')
5
+ Echoe.new("gene", "0.1.0") do |config|
6
+ config.description = "Sample genetic program in Ruby"
7
+ config.url = "http://github.com/evansenter/gene"
8
+ config.author = "Evan Senter"
9
+ config.email = "evansenter@gmail.com"
10
+ config.ignore_pattern = ["tmp/*", "script/*"]
11
+ config.dependencies = ["rmagick"]
12
+ config.development_dependencies = []
10
13
  end
11
14
 
12
- # vim: syntax=Ruby
15
+ Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |task| load task }
data/TODO ADDED
@@ -0,0 +1,7 @@
1
+ * Things aren't working because the image is coming out white! (Cell#draw_image)
2
+ * Reflesh test coverage in Generator
3
+ * Think about using the garbage collector...
4
+
5
+ require "initializers/runner.rb"
6
+ petri = Petri.new("./test/assets/Squares.jpg")
7
+ petri.evolve while petri.dish.first.fitness < 0.7
@@ -0,0 +1,34 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{gene}
5
+ s.version = "0.1.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Evan Senter"]
9
+ s.date = %q{2010-07-20}
10
+ s.description = %q{Sample genetic program in Ruby}
11
+ s.email = %q{evansenter@gmail.com}
12
+ s.extra_rdoc_files = ["TODO", "lib/aligner.rb", "lib/calculator.rb", "lib/cell.rb", "lib/color.rb", "lib/dsl.rb", "lib/gene.rb", "lib/generator.rb", "lib/geometry.rb", "lib/hungarian.rb", "lib/imagine.rb", "lib/petri.rb", "lib/point.rb", "lib/trait.rb", "tasks/test.rake"]
13
+ s.files = ["Manifest", "Rakefile", "TODO", "initializers/functional_extensions.rb", "initializers/module_extensions.rb", "initializers/object_extensions.rb", "initializers/range_extensions.rb", "initializers/runner.rb", "initializers/symbol_extensions.rb", "initializers/unbound_method_extensions.rb", "lib/aligner.rb", "lib/calculator.rb", "lib/cell.rb", "lib/color.rb", "lib/dsl.rb", "lib/gene.rb", "lib/generator.rb", "lib/geometry.rb", "lib/hungarian.rb", "lib/imagine.rb", "lib/petri.rb", "lib/point.rb", "lib/trait.rb", "tasks/test.rake", "test/assets/Nova.jpg", "test/assets/Rex.jpg", "test/assets/Squares.jpg", "test/test_helper.rb", "test/unit/aligner_test.rb", "test/unit/calculator_test.rb", "test/unit/cell_test.rb", "test/unit/color_test.rb", "test/unit/dsl_test.rb", "test/unit/functionals_extensions_test.rb", "test/unit/gene_test.rb", "test/unit/generator_test.rb", "test/unit/geometry_test.rb", "test/unit/hungarian_test.rb", "test/unit/imagine_test.rb", "test/unit/module_extensions_test.rb", "test/unit/object_extensions_test.rb", "test/unit/petri_test.rb", "test/unit/range_extensions_test.rb", "test/unit/symbol_extensions_test.rb", "test/unit/trait_test.rb", "test/unit/unbound_method_extensions_test.rb", "gene.gemspec"]
14
+ s.homepage = %q{http://github.com/evansenter/gene}
15
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Gene"]
16
+ s.require_paths = ["lib"]
17
+ s.rubyforge_project = %q{gene}
18
+ s.rubygems_version = %q{1.3.7}
19
+ s.summary = %q{Sample genetic program in Ruby}
20
+ s.test_files = ["test/test_helper.rb", "test/unit/aligner_test.rb", "test/unit/calculator_test.rb", "test/unit/cell_test.rb", "test/unit/color_test.rb", "test/unit/dsl_test.rb", "test/unit/functionals_extensions_test.rb", "test/unit/gene_test.rb", "test/unit/generator_test.rb", "test/unit/geometry_test.rb", "test/unit/hungarian_test.rb", "test/unit/imagine_test.rb", "test/unit/module_extensions_test.rb", "test/unit/object_extensions_test.rb", "test/unit/petri_test.rb", "test/unit/range_extensions_test.rb", "test/unit/symbol_extensions_test.rb", "test/unit/trait_test.rb", "test/unit/unbound_method_extensions_test.rb"]
21
+
22
+ if s.respond_to? :specification_version then
23
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
24
+ s.specification_version = 3
25
+
26
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
27
+ s.add_runtime_dependency(%q<rmagick>, [">= 0"])
28
+ else
29
+ s.add_dependency(%q<rmagick>, [">= 0"])
30
+ end
31
+ else
32
+ s.add_dependency(%q<rmagick>, [">= 0"])
33
+ end
34
+ end
@@ -0,0 +1,44 @@
1
+ module FunctionalExtensions
2
+ def apply(enum)
3
+ enum.map &self
4
+ end
5
+ alias | apply
6
+
7
+ def reduce(enum)
8
+ enum.inject &self
9
+ end
10
+ alias <= reduce
11
+
12
+ def compose(function)
13
+ if self.respond_to?(:arity) && self.arity == 1
14
+ lambda { |*args| self[function[*args]] }
15
+ else
16
+ lambda { |*args| self[*function[*args]] }
17
+ end
18
+ end
19
+ alias * compose
20
+
21
+ def apply_head(*first)
22
+ lambda { |*rest| self[*first.concat(rest)] }
23
+ end
24
+ alias >> apply_head
25
+
26
+ def apply_tail(*last)
27
+ lambda { |*rest| self[*rest.concat(last)] }
28
+ end
29
+ alias << apply_tail
30
+
31
+ def memoize
32
+ cache = {}
33
+ lambda do |*args|
34
+ unless cache.has_key?(args)
35
+ cache[args] = self[*args]
36
+ end
37
+ cache[args]
38
+ end
39
+ end
40
+ alias +@ memoize
41
+ end
42
+
43
+ class Method; include FunctionalExtensions; end
44
+ class Proc; include FunctionalExtensions; end
@@ -0,0 +1,14 @@
1
+ class Module
2
+ alias [] instance_method
3
+
4
+ def []=(symbol, function)
5
+ self.instance_eval { define_method(symbol, function) }
6
+ end
7
+
8
+ def alias_method_chain(target, feature)
9
+ method_name, extension = target.to_s.match(/(\w+)(\W?)/).to_a[1..-1]
10
+
11
+ alias_method "#{method_name}_without_#{feature}#{extension}", target
12
+ alias_method target, "#{method_name}_with_#{feature}#{extension}"
13
+ end
14
+ end
@@ -0,0 +1,25 @@
1
+ class Object
2
+ def returning(value)
3
+ yield value
4
+ value
5
+ end
6
+
7
+ def send_if(condition, method, *arguments)
8
+ block = arguments.pop if arguments.last.is_a?(Proc) && arguments.length == 1
9
+ if condition
10
+ block ? eval("self.#{method} &block") : self.send(method, *arguments)
11
+ else
12
+ self
13
+ end
14
+ end
15
+
16
+ def assert_at_least(number_required, number_present, message = nil)
17
+ unless number_present >= number_required
18
+ raise(ArgumentError, message || "You must provide at least #{number_required} objects (#{number_present} #{number_present == 1 ? 'was' : 'were'} provided)")
19
+ end
20
+ end
21
+
22
+ def class_metaclass
23
+ class << self.class; self; end
24
+ end
25
+ end
@@ -0,0 +1,9 @@
1
+ class Range
2
+ def min
3
+ self.begin.is_a?(Float) ? self.begin : super
4
+ end
5
+
6
+ def max
7
+ self.end.is_a?(Float) ? self.end : super
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ # Pull in necessary libraries.
2
+ require "RMagick"
3
+
4
+ # Get all source files except this one, pulling initializers before the lib_files.
5
+ initializers = Dir.glob(File.join(File.dirname(__FILE__), "**", "*.rb")) - [__FILE__]
6
+ lib_files = Dir.glob(File.join(File.dirname(__FILE__), "..", "lib", "**", "*.rb"))
7
+
8
+ # Set dynamic autoload on all of them based on their filename.
9
+ constants = (initializers + lib_files).inject([]) do |constants, file_path|
10
+ constant = File.basename(file_path, ".rb").split(/_/).map(&:capitalize).join.to_sym
11
+ autoload(constant, file_path)
12
+ end
13
+
14
+ # Pull in the initializer files!
15
+ initializers.map(&method(:require))
@@ -0,0 +1,11 @@
1
+ class Symbol
2
+ def [](object)
3
+ object.method(self)
4
+ end
5
+
6
+ def []=(object, function)
7
+ symbol = self
8
+ eigenclass = (class << object; self; end)
9
+ eigenclass.instance_eval { define_method(symbol, function) }
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ class UnboundMethod
2
+ alias [] bind
3
+ end
@@ -0,0 +1,34 @@
1
+ module Aligner
2
+ def align_crossover
3
+ optimal_alignment = Hungarian.new(crossover_map).solve
4
+
5
+ returning({ :cell_1 => [], :cell_2 => [] }) do |alignment_hash|
6
+ optimal_alignment.each do |tuple|
7
+ alignment_hash[:cell_1] << tuple.first
8
+ alignment_hash[:cell_2] << tuple.last
9
+ end
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def crossover_map
16
+ @cells.first.genes.map do |gene_1|
17
+ @cells.last.genes.map do |gene_2|
18
+ distance_between(middle_point_of(gene_1), middle_point_of(gene_2))
19
+ end
20
+ end
21
+ end
22
+
23
+ def distance_between(point_1, point_2)
24
+ square = lambda { |value| value ** 2 }
25
+ Math.sqrt(square[point_2.x - point_1.x] + square[point_2.y - point_1.y])
26
+ end
27
+
28
+ def middle_point_of(gene)
29
+ sum = lambda { |a, b| a + b }
30
+ average = lambda { |axis| (sum <= gene.polygon.points.map(&axis).map(&:value)) / gene.polygon.num_points.to_f }
31
+
32
+ Point.new(average[:x], average[:y])
33
+ end
34
+ end
@@ -0,0 +1,42 @@
1
+ module Calculator
2
+ def self.included(base)
3
+ base.extend ClassMethods
4
+ end
5
+
6
+ module ClassMethods
7
+ def generate_value(max)
8
+ case max
9
+ when 0: raise(ArgumentError, "Trying to call Calculator.generate_value(0), which is not supported at this time due to ambiguity of intention")
10
+ when Float: rand(0)
11
+ when Fixnum: rand(max)
12
+ else raise(ArgumentError, "Can't generate values of type #{max.class} in Calculator.generate_value(max)")
13
+ end
14
+ end
15
+
16
+ def get_normal_random_variable
17
+ x = y = sum = 0
18
+ loop do
19
+ x = get_uniform_random_variable
20
+ y = get_uniform_random_variable
21
+ break if (sum = (x ** 2) + (y ** 2)) < 1
22
+ end
23
+ rand(2).zero? ? convert(x, sum) : convert(y, sum)
24
+ end
25
+
26
+ def get_uniform_random_variable
27
+ rand(0) * random_sign_change
28
+ end
29
+
30
+ private
31
+
32
+ def random_sign_change
33
+ rand(2).zero? ? 1 : -1
34
+ end
35
+
36
+ def convert(variable, sum)
37
+ # http://en.wikipedia.org/wiki/Marsaglia_polar_method
38
+ return 0 if sum.zero?
39
+ variable * Math.sqrt(-2 * Math.log(sum) / sum)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,41 @@
1
+ class Cell < Dsl
2
+ attr_accessor :fitness
3
+ attr_reader :genes, :image
4
+
5
+ def genes_by_alpha
6
+ genes.sort { |gene_1, gene_2| gene_2.color.a.value <=> gene_1.color.a.value }
7
+ end
8
+
9
+ def genes_from_alignment_map(alignment)
10
+ alignment.map { |index| genes[index] }
11
+ end
12
+
13
+ private
14
+
15
+ def finish_init
16
+ fill_out_genes
17
+ draw_image
18
+ end
19
+
20
+ def fill_out_genes
21
+ @genes = num_genes.times.map { |index| (@genes ||= [])[index] || Gene.new }
22
+ end
23
+
24
+ def draw_image
25
+ @image = Magick::Image.new(image_dimensions.x, image_dimensions.y)
26
+ pen = Magick::Draw.new
27
+
28
+ genes_by_alpha.each do |gene|
29
+ pen.fill(gene.color.rgba_format)
30
+ pen.polygon(*gene.hulled_sequence)
31
+ pen.draw(@image)
32
+ end
33
+ end
34
+
35
+ def method_missing(name, *args, &block)
36
+ case name.to_s
37
+ when /^gene_(\d+)$/: (@genes ||= [])[$1.to_i] = Gene.new(&block)
38
+ else super
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,9 @@
1
+ Color = Struct.new(:r, :g, :b, :a) do
2
+ def rgb
3
+ [r, g, b]
4
+ end
5
+
6
+ def rgba_format
7
+ "rgba(" + (rgb.map(&:percentify) << a.value).join(", ") + ")"
8
+ end
9
+ end
@@ -0,0 +1,16 @@
1
+ class Dsl
2
+ def initialize(*args, &block)
3
+ if block_given?
4
+ @_original_self = block.binding.eval("self")
5
+ instance_eval(&block)
6
+ remove_instance_variable :@_original_self
7
+ end
8
+ finish_init if defined? :finish_init
9
+ end
10
+
11
+ def method_missing(name, *args, &block)
12
+ return Petri.send(name) if Petri.respond_to?(name)
13
+
14
+ @_original_self ? @_original_self.send(name, *args, &block) : super
15
+ end
16
+ end
@@ -1,3 +1,67 @@
1
- class Gene
2
- VERSION = "0.0.1"
3
- end
1
+ class Gene < Dsl
2
+ include Geometry
3
+
4
+ attr_reader :polygon, :color
5
+
6
+ private
7
+
8
+ def finish_init
9
+ fill_out_polygon
10
+ fill_out_color
11
+ initialize_singleton_methods_for_polygon_and_color
12
+ end
13
+
14
+ def fill_out_polygon
15
+ @polygon ||= []
16
+ @polygon = num_points.times.map do |index|
17
+ if polygon[index]
18
+ returning(polygon[index]) do |point|
19
+ point.x ||= Trait.new(0...image_dimensions.x)
20
+ point.y ||= Trait.new(0...image_dimensions.y)
21
+ end
22
+ else
23
+ Point.new(
24
+ Trait.new(0...image_dimensions.x),
25
+ Trait.new(0...image_dimensions.y)
26
+ )
27
+ end
28
+ end
29
+ end
30
+
31
+ def fill_out_color
32
+ @color ||= Color.new
33
+ color.each_pair { |channel, trait| color.send(:"#{channel}=", Trait.new(0.0..1.0)) unless trait }
34
+ end
35
+
36
+ def initialize_singleton_methods_for_polygon_and_color
37
+ :points[polygon] = lambda { self }
38
+ :num_points[polygon] = lambda { size }
39
+ end
40
+
41
+ def point_at(index, x, y)
42
+ point = (@polygon ||= [])[index] ||= Point.new
43
+
44
+ point.x = Trait.new(0...image_dimensions.x) { set_value x }
45
+ point.y = Trait.new(0...image_dimensions.y) { set_value y }
46
+ end
47
+
48
+ def point_from(index, name, &block)
49
+ point = (@polygon ||= [])[index] ||= Point.new
50
+
51
+ point.send(:"#{name}=", Trait.new(0...image_dimensions.send(:"#{name}"), &block))
52
+ end
53
+
54
+ def color_from(channel, &block)
55
+ @color ||= Color.new
56
+ color.send(:"#{channel}=", Trait.new(0.0..1.0, &block))
57
+ end
58
+
59
+ def method_missing(name, *args, &block)
60
+ case name.to_s
61
+ when /^point_(\d+)$/: point_at($1.to_i, *args)
62
+ when /^point_(\d+)_(\w)$/: point_from($1.to_i, $2, &block)
63
+ when /^trait_(\w)$/: color_from($1, &block)
64
+ else super
65
+ end
66
+ end
67
+ end