gene 0.0.1 → 0.1.0

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.
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