davidrichards-tegu_gears 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 3
3
+ :major: 0
4
+ :minor: 0
@@ -0,0 +1,6 @@
1
+ class Factorial
2
+ include TeguGears
3
+ def process(n)
4
+ n <= 1 ? 1 : n * Factorial.call(n-1)
5
+ end
6
+ end
@@ -0,0 +1,7 @@
1
+ # The Fibonacci numbers: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, etc.
2
+ class Fibonacci
3
+ include TeguGears
4
+ def process(n)
5
+ n <= 2 ? 1 : Fibonacci.call(n-1) + Fibonacci.call(n-2)
6
+ end
7
+ end
@@ -0,0 +1,59 @@
1
+ class SSY
2
+ include TeguGears
3
+ def process(*y)
4
+ y_mean = Mean.call(*y)
5
+ y.inject{|s,e| s + (e - y_mean)**2}
6
+ end
7
+ end
8
+
9
+ class SSX < SSY; end
10
+
11
+ class SSXY
12
+ include TeguGears
13
+ def process(x, y)
14
+ raise ArgumentError, "x and y need to be the same size" unless x.size == y.size
15
+ x_mean = Mean.call(*x)
16
+ y_mean = Mean.call(*y)
17
+ (0...x.size).inject(0.0) {|s,i| s + (y[i] - y_mean) * (x[i] - x_mean) }
18
+ end
19
+ end
20
+
21
+ class Mean
22
+ include TeguGears
23
+ def process(*x)
24
+ x.inject{|s,e| s + e} / x.size.to_f
25
+ end
26
+ end
27
+
28
+ class MaximumLikelihoodSlope
29
+ include TeguGears
30
+ def process(x, y)
31
+ raise ArgumentError, "x and y need to be the same size" unless x.size == y.size
32
+ b = SSXY.call(x, y) / SSX.call(x)
33
+ end
34
+ end
35
+
36
+ class MaximumLikelihoodIntercept
37
+ include TeguGears
38
+ def process(x,y)
39
+ raise ArgumentError, "x and y need to be the same size" unless x.size == y.size
40
+ Mean.call(*y) - MaximumLikelihoodSlope.call(x,y) * Mean.call(*x)
41
+ end
42
+ end
43
+
44
+ class SSE
45
+ include TeguGears
46
+ def process(x,y)
47
+ raise ArgumentError, "x and y need to be the same size" unless x.size == y.size
48
+ (0...x.size).inject(0.0) do |s, i|
49
+ s + (y[i] - MaximumLikelihoodIntercept.call(x,y) - MaximumLikelihoodSlope.call(x,y) * x[i] ) ** 2
50
+ end
51
+ end
52
+ end
53
+
54
+ class RSquared
55
+ include TeguGears
56
+ def process(x,y)
57
+ (SSY.call(*y) - SSE.call(x,y)) / SSY.call(*y)
58
+ end
59
+ end
@@ -0,0 +1,52 @@
1
+ require 'rubygems'
2
+ gem 'activesupport'
3
+ require 'activesupport'
4
+
5
+ # Add to these if you want to add class and instance methods.
6
+ module TeguGears
7
+ module InstanceMethods; end
8
+ module ClassMethods; end
9
+ end
10
+
11
+ $:.unshift(File.dirname(__FILE__))
12
+ require 'tegu_gears/memoize'
13
+ Dir.glob("#{File.dirname(__FILE__)}/tegu_gears/*.rb").each { |file| require file }
14
+ module TeguGears
15
+ def self.included(base)
16
+ base.send(:include, Memoize)
17
+ base.send(:include, InstanceMethods)
18
+ base.send(:extend, ClassMethods)
19
+ end
20
+ end
21
+
22
+ # Now that I have some tools setup, require some libraries that will be
23
+ # generally useful. This will be migrated to a configurable load process
24
+ # at some point soon.
25
+
26
+ # Requires libraries, only if they're available.
27
+ def safe_load(val=nil, &block)
28
+ begin
29
+ if block
30
+ block.call
31
+ else
32
+ require val
33
+ end
34
+ # Very important that this is Exception, and not StandardError
35
+ rescue Exception => e
36
+ false
37
+ end
38
+ end
39
+
40
+ safe_load 'rubygems'
41
+ safe_load 'mathn'
42
+ safe_load {require bigdecimal; require 'bigdecimal/math'}
43
+ safe_load 'set'
44
+ safe_load 'matrix'
45
+ safe_load 'narray'
46
+ safe_load 'rnum'
47
+ safe_load 'gratr'
48
+ safe_load 'tenacious_g'
49
+ safe_load 'rbtree'
50
+
51
+ # Require the examples too, they are meant to be generally interesting or useful.
52
+ Dir.glob("#{File.dirname(__FILE__)}/examples/*.rb").each { |file| require file }
@@ -0,0 +1,25 @@
1
+ module TeguGears #:nodoc:
2
+
3
+ module InstanceMethods
4
+
5
+ # This allows me to compose two classes using TeguGears.
6
+ def compose(f)
7
+ lambda {|*args| f.call(self.call(*args))}
8
+ end
9
+ alias :| :compose
10
+ end
11
+ end
12
+
13
+ class Object
14
+ # Composes any number of functions. The first argument is the input.
15
+ # Use Struct if the argument list needs to be more expansive.
16
+ def compose(*args)
17
+ param = args.shift
18
+ args.inject {|composition, f| make_binomial_composition(composition, f)}.call(param)
19
+ end
20
+
21
+ def make_binomial_composition(f, g)
22
+ lambda{|*args| g.call(f.call(*args))}
23
+ end
24
+ private :make_binomial_composition
25
+ end
@@ -0,0 +1,3 @@
1
+ $:.<< File.dirname(__FILE__)
2
+
3
+ require 'dynamics/node'
@@ -0,0 +1,6 @@
1
+ # I'm using explicit nodes here to carry the meaning of the dynamics. I
2
+ # have done it another way, where I patch in some methods to anything
3
+ # that moves. That's a really obtrusive way to build a system, it turns
4
+ # out. So, I'll mix things in explicitly and see how that goes.
5
+ module DynamicsNode
6
+ end
@@ -0,0 +1,62 @@
1
+ require 'rubygems'
2
+ require 'gratr'
3
+ require 'modelling'
4
+
5
+ class Value
6
+ include Modelling
7
+ attributes :value => Proc.new {0.0}
8
+ end
9
+
10
+ class Value
11
+ attr_accessor :value
12
+ def initialize(value)
13
+ @value = value.to_f
14
+ end
15
+ end
16
+
17
+ class Beta < Value; end
18
+ class Vote < Value; end
19
+ class Income < Value; end
20
+ class Education < Value; end
21
+ class PartisanIdentification < Value; end
22
+ class NeighborhoodContext < Value; end
23
+
24
+ class Operator
25
+ include TeguGears
26
+
27
+ # Auto-collects the in-nodes, multiplying what should be multiplied
28
+ def process
29
+
30
+ args.inject { |sum, e| sum + e }
31
+ end
32
+ end
33
+
34
+
35
+ class System
36
+ class << self
37
+ def instance
38
+ @@instance ||= new
39
+ end
40
+ end
41
+
42
+ attr_reader :graph
43
+ def initialize
44
+ @graph = GRATR::Digraph.new
45
+ end
46
+
47
+ def add_edge(a,b,w=nil)
48
+ self.graph.add_edge!(a,b,w)
49
+ end
50
+
51
+ def method_missing(sym, *args, &block)
52
+ self.graph.send(sym, *args, &block)
53
+ end
54
+ end
55
+
56
+ class Object
57
+ def method_missing(sym, *args, &block)
58
+ System.instance.send(sym, *args, &block)
59
+ end
60
+ end
61
+
62
+ # swap(old, new)
@@ -0,0 +1,2 @@
1
+ class Operator
2
+ end
@@ -0,0 +1,63 @@
1
+ module TeguGears #:nodoc:
2
+
3
+ # Should get more sophisticated with a configuration tool at some point.
4
+ REPOSITORY_CLASS = Hash
5
+
6
+ # This works like a pass through to other caching tools, or keeps things
7
+ # in a hash. Each class hosting TeguGear has its own entry in this
8
+ # repository, as does each composition. Other caching tools may be
9
+ # useful to monitor the cache size, distribute it, or reduce different
10
+ # kinds of caching. I use a key/value interface, because that should
11
+ # work well with Memcache, Tokyo Cabinet, Redis, Hash, CouchDB, and
12
+ # other tools.
13
+ #
14
+ # I refactored to a centrally-managed repository because distributed
15
+ # caching doesn't make sense for expensive computations. There's
16
+ # probably another abstraction available that will allow distributed
17
+ # caching only when configured, but I'll get to that when I write a
18
+ # configuration harness.
19
+ class MemoRepository
20
+
21
+ class << self
22
+ def instance
23
+ @@instance ||= new(REPOSITORY_CLASS.new)
24
+ end
25
+
26
+ def method_missing(sym, *args, &block)
27
+ instance.send(sym, *args, &block)
28
+ end
29
+ end
30
+
31
+ attr_reader :store
32
+ def initialize(store=Hash.new)
33
+ @store = store
34
+ end
35
+
36
+ def method_missing(sym, *args, &block)
37
+ self.store.send(sym, *args, &block)
38
+ end
39
+
40
+ # Expects a hash on the other end, for now.
41
+ def set(caller, key, value)
42
+ key = simplify_key(key)
43
+ self.store[caller] ||= REPOSITORY_CLASS.new
44
+ self.store[caller][key] = value
45
+ end
46
+
47
+ # Because I opened things up to accept an array of params instead of a single param.
48
+ def simplify_key(key)
49
+ (key.is_a?(Array) and key.size == 1) ? key.first : key
50
+ end
51
+ protected :simplify_key
52
+
53
+ def for(caller, key=nil)
54
+ self.store[caller] ||= REPOSITORY_CLASS.new
55
+ key ? self.store[caller][key] : self.store[caller]
56
+ end
57
+
58
+ def flush_for(caller)
59
+ self.store[caller] = REPOSITORY_CLASS.new
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,51 @@
1
+ module TeguGears #:nodoc:
2
+ # If I can get used to using a struct or a single input variable, I can
3
+ # do things this way. Otherwise, I need to get deeper into the meta
4
+ # programming to work around how to call things in a signatureless
5
+ # environment. Not an easy nut to crack, btw.
6
+ module Memoize
7
+ module ClassMethods
8
+ def instance
9
+ @inst ||= new
10
+ end
11
+
12
+ def method_missing(sym, *args, &block)
13
+ instance.send(sym, *args, &block)
14
+ end
15
+ end
16
+
17
+ module InstanceMethods
18
+ def call(*x)
19
+ function(*x)
20
+ end
21
+
22
+ def memoize
23
+ @memoize = true unless defined?(@memoize)
24
+ @memoize
25
+ end
26
+
27
+ def memoize=(val)
28
+ @memoize = val
29
+ end
30
+
31
+ def function(*x)
32
+ self.memoize ? memoized(*x) : process(*x)
33
+ end
34
+
35
+ def memoized(*x)
36
+ MemoRepository.for(self, x) || MemoRepository.set(self, x, process(*x))
37
+ end
38
+
39
+ def flush
40
+ MemoRepository.flush_for(self)
41
+ end
42
+
43
+ end
44
+
45
+ def self.included(base)
46
+ base.send(:extend, ClassMethods)
47
+ base.send(:include, InstanceMethods)
48
+ end
49
+ end
50
+
51
+ end
@@ -0,0 +1,8 @@
1
+ require File.join(File.dirname(__FILE__), "/../../spec_helper")
2
+
3
+ describe Factorial do
4
+ it "should be able to return the factorial of a number" do
5
+ Factorial.call(3).should eql(6)
6
+ Factorial.call(6).should eql(720)
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ require File.join(File.dirname(__FILE__), "/../../spec_helper")
2
+
3
+ describe Fibonacci do
4
+ it "should calculate the fibonacci, up to n" do
5
+ Fibonacci.call(3).should eql(2)
6
+ Fibonacci.call(4).should eql(3)
7
+ Fibonacci.call(5).should eql(5)
8
+ end
9
+ end
@@ -0,0 +1,64 @@
1
+ require File.join(File.dirname(__FILE__), "/../../spec_helper")
2
+
3
+ describe TeguGears, "compose" do
4
+ before(:all) do
5
+ class Square
6
+ include TeguGears
7
+ def process(n)
8
+ n ** 2
9
+ end
10
+ end
11
+
12
+ class SquareRoot
13
+ include TeguGears
14
+ def process(n)
15
+ Math.sqrt(n)
16
+ end
17
+ end
18
+ end
19
+
20
+ it "should compose two functions manually" do
21
+ square_root = SquareRoot.instance
22
+ square = Square.instance
23
+ (square_root | square).call(2).should be_close(2.0, 0.0001)
24
+ end
25
+
26
+ it "should be able to compose two functions from their host classes" do
27
+ (SquareRoot | Square).call(2).should be_close(2.0, 0.0001)
28
+ end
29
+
30
+ it "should read the composition left to right" do
31
+ plus_two = lambda{|x| x + 2}
32
+ times_two = lambda{|x| x * 2}
33
+ # IF this was right to left, it would be 10
34
+ (plus_two | times_two).call(4).should eql(12)
35
+ (times_two | plus_two).call(4).should eql(10)
36
+ end
37
+
38
+ context "from Object" do
39
+ it "should have a version available for any lambda or method" do
40
+ compose(2, SquareRoot, Square).should be_close(2.0, 0.0001)
41
+ end
42
+
43
+ it "should be able to take any number of compositions at once" do
44
+ cube_root = lambda{|x| x ** (1/3.0)}
45
+ half = lambda{|x| x / 2.0}
46
+ twice = lambda{|x| x * 2.0}
47
+ quadruple = lambda{|x| x * 4.0}
48
+ compose(2, quadruple, twice, half, cube_root).should eql(2.0)
49
+ end
50
+
51
+ it "should read left to right" do
52
+ plus_two = lambda{|x| x + 2}
53
+ times_two = lambda{|x| x * 2}
54
+ # IF this was right to left, it would be 10
55
+ compose(4, plus_two, times_two).should eql(12)
56
+ compose(4, times_two, plus_two).should eql(10)
57
+ end
58
+ end
59
+
60
+ after(:all) do
61
+ Object.send(:remove_const, :Square)
62
+ Object.send(:remove_const, :SquareRoot)
63
+ end
64
+ end
@@ -0,0 +1,83 @@
1
+ require File.join(File.dirname(__FILE__), "/../../spec_helper")
2
+
3
+ describe MemoRepository do
4
+ it "should default to a hash" do
5
+ @mr = MemoRepository.new
6
+ @mr.store.should be_is_a(Hash)
7
+ end
8
+
9
+ it "should pass unknown calls to the store" do
10
+ @mr = MemoRepository.new
11
+ @mr.store.should_receive(:blah).and_return(true)
12
+ @mr.blah
13
+ end
14
+
15
+ it "should be able to take a different kind of repository" do
16
+ @mr = MemoRepository.new([])
17
+ @mr.store.should be_is_a(Array)
18
+ end
19
+
20
+ it "should provide a quasi-singleton" do
21
+ MemoRepository.instance.should be_is_a(MemoRepository)
22
+ end
23
+
24
+ it "should set the REPOSITORY_CLASS to a Hash" do
25
+ REPOSITORY_CLASS.should eql(Hash)
26
+ end
27
+
28
+ it "should construct REPOSITORY_CLASS for the singleton" do
29
+ REPOSITORY_CLASS.should_receive(:new).and_return(Hash.new)
30
+ MemoRepository.instance
31
+ end
32
+
33
+ it "should pass unknown class calls down to the instance" do
34
+ MemoRepository.instance.should_receive(:weird).and_return(true)
35
+ MemoRepository.weird
36
+ end
37
+
38
+ context "scoped access" do
39
+ before(:all) do
40
+ class A
41
+ include TeguGears
42
+ def process(n)
43
+ n + 1
44
+ end
45
+ end
46
+ end
47
+
48
+ it "should offer a set interface, that passes a scoped memo into the repository" do
49
+ MemoRepository.set(A, 1, 2)
50
+ MemoRepository.store.keys.should be_include(A)
51
+ end
52
+
53
+ context "for" do
54
+ it "should offer a default empty hash" do
55
+ MemoRepository.instance.for(:non_existent).should be_is_a(Hash)
56
+ MemoRepository.instance.for(:non_existent).should be_empty
57
+ end
58
+
59
+ it "should offer whatever cache is available" do
60
+ MemoRepository.set(A, 1, 2)
61
+ @a_repo = MemoRepository.for(A)
62
+ @a_repo.keys.should eql([1])
63
+ @a_repo.values.should eql([2])
64
+ end
65
+
66
+ it "should take an optional second parameter, which return the specific value that has been cached." do
67
+ MemoRepository.set(A, 1, 3)
68
+ MemoRepository.for(A, 1).should eql(3)
69
+ MemoRepository.flush_for(A)
70
+ end
71
+ end
72
+
73
+ it "should be able to flush a single scope" do
74
+ MemoRepository.for(A).should_not be_empty
75
+ MemoRepository.flush_for(A)
76
+ MemoRepository.for(A).should be_empty
77
+ end
78
+
79
+ after(:all) do
80
+ Object.send(:remove_const, :A)
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,81 @@
1
+ require File.join(File.dirname(__FILE__), "/../../spec_helper")
2
+
3
+ describe Memoize do
4
+ before(:all) do
5
+ class A
6
+ include Memoize
7
+
8
+ def process(x)
9
+ x**2
10
+ end
11
+ end
12
+ end
13
+
14
+ context "class methods" do
15
+ it "should make instance available" do
16
+ A.should be_respond_to(:instance)
17
+ end
18
+
19
+ it "should return an instance of the host (the class that includes Memoize)" do
20
+ A.instance.should be_is_a(A)
21
+ end
22
+
23
+ it "should pass unknown calls to host instance" do
24
+ @a = A.instance
25
+ @a.should_receive(:blah).and_return(true)
26
+ A.blah
27
+ end
28
+ end
29
+
30
+ context "instance methods" do
31
+
32
+ before(:each) do
33
+ @a = A.new
34
+ end
35
+
36
+ it "should map call to function" do
37
+ @a.should_receive(:function).and_return(true)
38
+ @a.call(1)
39
+ end
40
+
41
+ it "should make memoize accessible" do
42
+ @a.memoize = false
43
+ @a.memoize.should eql(false)
44
+ @a.memoize = true
45
+ @a.memoize.should eql(true)
46
+ end
47
+
48
+ it "should default memoize to true" do
49
+ @a.memoize.should eql(true)
50
+ end
51
+
52
+ it "should expect process to define the class' core function. This is the only method that a host class needs to implement." do
53
+ @a.should_receive(:process).and_return('found me')
54
+ @a.call(:anything).should eql('found me')
55
+ end
56
+
57
+ it "should not refer to the cache when memoize is set to false" do
58
+ @a.memoize = false
59
+ MemoRepository.should_not_receive(:for)
60
+ @a.call(2)
61
+ end
62
+
63
+ it "should be able to clear the cache" do
64
+ @a.call(2)
65
+ @a.flush
66
+ MemoRepository.for(@a).should be_empty
67
+ end
68
+
69
+ it "should use the MemoRepository" do
70
+ MemoRepository.flush_for(A)
71
+ MemoRepository.for(@a)[2].should be_nil
72
+ @a.call(2)
73
+ MemoRepository.for(@a)[2].should eql(4)
74
+ end
75
+ end
76
+
77
+ after(:all) do
78
+ Object.send(:remove_const, :A)
79
+ end
80
+ end
81
+
@@ -0,0 +1,10 @@
1
+ $: << File.join(File.dirname(__FILE__), "/../lib")
2
+ require 'rubygems'
3
+ require 'spec'
4
+ require 'tegu_gears'
5
+
6
+ Spec::Runner.configure do |config|
7
+
8
+ end
9
+
10
+ include TeguGears
@@ -0,0 +1,17 @@
1
+ require File.join(File.dirname(__FILE__), "/spec_helper")
2
+
3
+ describe "TeguGears" do
4
+ it "should require rubygems" do
5
+ defined?(Gem).should eql('constant')
6
+ end
7
+
8
+ it "should require the examples" do
9
+ defined?(Fibonacci).should eql('constant')
10
+ end
11
+
12
+ it "should automatically include any sub-modules when included" do
13
+ class A; include TeguGears end
14
+ A.included_modules.should be_include(TeguGears::Memoize)
15
+ Object.send(:remove_const, :A)
16
+ end
17
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: davidrichards-tegu_gears
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - David Richards
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-04-28 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: TODO
17
+ email: davidlamontrichards@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - VERSION.yml
26
+ - lib/examples
27
+ - lib/examples/factorial.rb
28
+ - lib/examples/fibonacci.rb
29
+ - lib/examples/regression.rb
30
+ - lib/tegu_gears
31
+ - lib/tegu_gears/compose.rb
32
+ - lib/tegu_gears/dynamics
33
+ - lib/tegu_gears/dynamics/node.rb
34
+ - lib/tegu_gears/dynamics/noodle.rb
35
+ - lib/tegu_gears/dynamics/operator.rb
36
+ - lib/tegu_gears/dynamics.rb
37
+ - lib/tegu_gears/memo_repository.rb
38
+ - lib/tegu_gears/memoize.rb
39
+ - lib/tegu_gears.rb
40
+ - spec/lib
41
+ - spec/lib/examples
42
+ - spec/lib/examples/factorial_spec.rb
43
+ - spec/lib/examples/fibonacci_spec.rb
44
+ - spec/lib/tegu_gears
45
+ - spec/lib/tegu_gears/compose_spec.rb
46
+ - spec/lib/tegu_gears/memo_repository_spec.rb
47
+ - spec/lib/tegu_gears/memoize_spec.rb
48
+ - spec/spec_helper.rb
49
+ - spec/tegu_gears_spec.rb
50
+ has_rdoc: true
51
+ homepage: http://github.com/davidrichards/tegu_gears
52
+ post_install_message:
53
+ rdoc_options:
54
+ - --inline-source
55
+ - --charset=UTF-8
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ version:
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ version:
70
+ requirements: []
71
+
72
+ rubyforge_project:
73
+ rubygems_version: 1.2.0
74
+ signing_key:
75
+ specification_version: 2
76
+ summary: TODO
77
+ test_files: []
78
+