integrator 0.0.1

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/History.txt ADDED
@@ -0,0 +1,7 @@
1
+ == 0.0.1 2007-10-13
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
5
+
6
+ * Added test code
7
+ * Only for Cash-Karp RK45 fixed and adaptive integrators
data/License.txt ADDED
@@ -0,0 +1,5 @@
1
+ Originally Copyright (c) 2007 Noah Gibbs
2
+
3
+ This code is released into the public domain by the author in 2007. It may
4
+ be used for any purpose whatsoever, though it comes with no warranty of any
5
+ kind.
data/Manifest.txt ADDED
@@ -0,0 +1,27 @@
1
+ History.txt
2
+ License.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ config/hoe.rb
7
+ config/requirements.rb
8
+ lib/integrator.rb
9
+ lib/integrator/adaptive.rb
10
+ lib/integrator/rkck.rb
11
+ lib/integrator/rkqs.rb
12
+ lib/integrator/version.rb
13
+ log/debug.log
14
+ script/destroy
15
+ script/generate
16
+ script/txt2html
17
+ setup.rb
18
+ tasks/deployment.rake
19
+ tasks/environment.rake
20
+ tasks/website.rake
21
+ test/test_helper.rb
22
+ test/test_integrator.rb
23
+ website/index.html
24
+ website/index.txt
25
+ website/javascripts/rounded_corners_lite.inc.js
26
+ website/stylesheets/screen.css
27
+ website/template.rhtml
data/README.txt ADDED
@@ -0,0 +1,3 @@
1
+ This gem includes a framework for numerical integrators, both fixed-step and
2
+ adaptive, and includes an excellent sample integrator of each type to get
3
+ you started.
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require 'config/requirements'
2
+ require 'config/hoe' # setup Hoe + all gem configuration
3
+
4
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
data/config/hoe.rb ADDED
@@ -0,0 +1,70 @@
1
+ require 'integrator/version'
2
+
3
+ AUTHOR = 'Noah Gibbs'
4
+ EMAIL = "angelbob@nospam.users.sourceforge.net"
5
+ DESCRIPTION = "Numerical integration framework"
6
+ GEM_NAME = 'integrator' # what ppl will type to install your gem
7
+ RUBYFORGE_PROJECT = 'diffeq' # The unix name for your project
8
+ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
9
+ DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
10
+
11
+ @config_file = "~/.rubyforge/user-config.yml"
12
+ @config = nil
13
+ RUBYFORGE_USERNAME = "unknown"
14
+ def rubyforge_username
15
+ unless @config
16
+ begin
17
+ @config = YAML.load(File.read(File.expand_path(@config_file)))
18
+ rescue
19
+ puts <<-EOS
20
+ ERROR: No rubyforge config file found: #{@config_file}
21
+ Run 'rubyforge setup' to prepare your env for access to Rubyforge
22
+ - See http://newgem.rubyforge.org/rubyforge.html for more details
23
+ EOS
24
+ exit
25
+ end
26
+ end
27
+ RUBYFORGE_USERNAME.replace @config["username"]
28
+ end
29
+
30
+
31
+ REV = nil
32
+ # UNCOMMENT IF REQUIRED:
33
+ # REV = `svn info`.each {|line| if line =~ /^Revision:/ then k,v = line.split(': '); break v.chomp; else next; end} rescue nil
34
+ VERS = Integrator::VERSION::STRING + (REV ? ".#{REV}" : "")
35
+ RDOC_OPTS = ['--quiet', '--title', 'integrator documentation',
36
+ "--opname", "index.html",
37
+ "--line-numbers",
38
+ "--main", "README",
39
+ "--inline-source"]
40
+
41
+ class Hoe
42
+ def extra_deps
43
+ @extra_deps.reject! { |x| Array(x).first == 'hoe' }
44
+ @extra_deps
45
+ end
46
+ end
47
+
48
+ # Generate all the Rake tasks
49
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
50
+ hoe = Hoe.new(GEM_NAME, VERS) do |p|
51
+ p.author = AUTHOR
52
+ p.description = DESCRIPTION
53
+ p.email = EMAIL
54
+ p.summary = DESCRIPTION
55
+ p.url = HOMEPATH
56
+ p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
57
+ p.test_globs = ["test/**/test_*.rb"]
58
+ p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
59
+
60
+ # == Optional
61
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\\n\\n")
62
+ #p.extra_deps = [] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
63
+
64
+ #p.spec_extras = {} # A hash of extra values to set in the gemspec.
65
+
66
+ end
67
+
68
+ CHANGES = hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
69
+ PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
70
+ hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
@@ -0,0 +1,17 @@
1
+ require 'fileutils'
2
+ include FileUtils
3
+
4
+ require 'rubygems'
5
+ %w[rake hoe newgem rubigen].each do |req_gem|
6
+ begin
7
+ require req_gem
8
+ rescue LoadError
9
+ puts "This Rakefile requires the '#{req_gem}' RubyGem."
10
+ puts "Installation: gem install #{req_gem} -y"
11
+ exit
12
+ end
13
+ end
14
+
15
+ $:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
16
+
17
+ require 'integrator'
@@ -0,0 +1,109 @@
1
+ #
2
+ # Integrator library
3
+ # Copyright (C) 2007 Noah Gibbs
4
+ #
5
+ # This library is in the public domain, and may be redistributed in any
6
+ # way.
7
+ #
8
+
9
+ require "matrix"
10
+
11
+ require "rubygems"
12
+ require "integrator"
13
+
14
+ require "integrator/rkqs.rb"
15
+
16
+ # This is the top-level driver, with logging, for Cash-Karp adaptive
17
+ # Runge-Kutta fourth-order integration with error checking. The
18
+ # algorithm is from "Numerical Methods in C", second edition.
19
+
20
+ class Adaptive_Cash_Karp_RK45 < OneStep_Cash_Karp_RK45
21
+ include Integrator
22
+
23
+ MAXSTP = 10000
24
+ TINY = 1.0e-30
25
+
26
+ def initialize()
27
+ Integrator_initialize()
28
+ end
29
+
30
+ def adaptive_integrate(ystart, x1, x2, eps, h1, hmin, derivs)
31
+
32
+ x = x1
33
+ h1 = -h1 unless h1 >= 0
34
+ x2 - x1 >= 0 ? h = h1 : h = -h1
35
+ @nok = @nbad = @count = 0
36
+
37
+ y = ystart
38
+ xsav = x - @dxsav * 2.0 if @kmax > 0
39
+
40
+ nstp = 1
41
+ while nstp <= MAXSTP
42
+ dydx = derivs.call(x, y)
43
+ #yscal = y.abs + (dydx * h).abs + Vector( [TINY] * y.length)
44
+ yscal = y.collect2(dydx) { |yi, dyi| yi.abs + (dyi * h).abs + TINY }
45
+
46
+ if @kmax > 0 and @count < @kmax - 1 and (x-xsav).abs > @dxsav.abs
47
+ @count += 1
48
+ @xp[@count] = x
49
+ @yp[@count] = y
50
+ xsav = x
51
+ end
52
+
53
+ h = x2 - x if ((x + h - x2)*(x + h - x1) > 0.0)
54
+ x, y, hdid, hnext = integrate_ad_step(y, dydx, x, h, eps,
55
+ yscal, derivs)
56
+ if hdid == h
57
+ @nok += 1
58
+ else
59
+ @nbad += 1
60
+ end
61
+
62
+ if (x-x2)*(x2-x1) >= 0.0
63
+ if @kmax != 0
64
+ @count += 1
65
+ @xp[@count] = x
66
+ @yp[@count] = y
67
+ end
68
+ return y
69
+ end
70
+
71
+ raise "Step size too small!" if hnext.abs <= hmin
72
+ h = hnext
73
+
74
+ nstp += 1
75
+ end
76
+
77
+ raise "Too many steps in integration!"
78
+ end
79
+
80
+ end
81
+
82
+ if __FILE__ == $0
83
+
84
+ rk45 = Adaptive_Cash_Karp_RK45.new()
85
+
86
+ def assert_within_epsilon(actual, expected)
87
+ if expected.kind_of?(Vector)
88
+ actual.collect2(expected) { |x, y|
89
+ assert_within_epsilon(x, y)
90
+ }
91
+ else
92
+ (actual - expected).abs / expected < 0.001 or
93
+ raise "Values don't match! Actual was #{actual}, not #{expected}!"
94
+ end
95
+ end
96
+ assert_within_epsilon(10000.0, 10001)
97
+
98
+ # This takes the derivative of e^x for each vector element.
99
+ # The derivative of e^x is just e^x again.
100
+ exp_deriv = proc { |x, y| y.collect { |yi| yi } }
101
+
102
+ exp_start = Vector.elements([1.0] * 100)
103
+
104
+ y = rk45.adaptive_integrate(exp_start, 0.0, 1.0, 0.001, 0.01, 0.0001,
105
+ exp_deriv)
106
+ assert_within_epsilon(y, Vector.elements( [ Math::E ] * 100 ))
107
+
108
+ print "All tests completed...\n"
109
+ end
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/ruby -w
2
+
3
+ #
4
+ # Integrator library
5
+ # Copyright (C) 2007 Noah Gibbs
6
+ #
7
+ # This library is in the public domain, and may be redistributed in any
8
+ # way.
9
+ #
10
+
11
+ # Cash-Karp embedded Runge-Kutta integration This algorithm is taken
12
+ # from "Numerical Recipes in C", second edition, though the ruby
13
+ # translation is my own. This is the fixed step-size version.
14
+
15
+ require "matrix"
16
+
17
+ class Cash_Karp_RK4
18
+ A2 = 0.2
19
+ A3 = 0.3
20
+ A4 = 0.6
21
+ A5 = 1.0
22
+ A6 = 0.875
23
+ B21 = 0.2
24
+ B31 = 3.0/40.0
25
+ B32 = 9.0/40.0
26
+ B41 = 0.3
27
+ B42 = -0.9
28
+ B43 = 1.2
29
+ B51 = -11.0/54.0
30
+ B52 = 2.5
31
+ B53 = -70.0/27.0
32
+ B54 = 35.0/27.0
33
+ B61 = 1631.0/55296.0
34
+ B62 = 175.0/512.0
35
+ B63 = 575.0/13824.0
36
+ B64 = 44275.0/110592.0
37
+ B65 = 253.0/4096.0
38
+ C1 = 37.0/378.0
39
+ C3 = 250.0/621.0
40
+ C4 = 125.0/594.0
41
+ C6 = 512.0/1771.0
42
+ DC1 = C1-2825.0/27648.0
43
+ DC3 = C3-18575.0/48384.0
44
+ DC4 = C4-13525.0/55296.0
45
+ DC5 = -277.00/14336.0
46
+ DC6 = C6-0.25
47
+
48
+ def integrate_fixed_step(y, dydx, x, h, derivs)
49
+ raise "Bad type!" unless [y, dydx].all? { |var| var.kind_of?(Vector) }
50
+ raise "Bad type!" unless [x, h].all? { |var| var.kind_of?(Float) }
51
+ raise "Bad method!" unless derivs.kind_of?(Proc)
52
+
53
+ ytemp = y + dydx * B21 * h
54
+ ak2 = derivs.call(x + A2*h, ytemp)
55
+ ytemp = y + (dydx * B31 + ak2 * B32) * h
56
+ ak3 = derivs.call(x + A3*h, ytemp)
57
+ ytemp = y + (dydx * B41 + ak2 * B42 + ak3 * B43) * h
58
+ ak4 = derivs.call(x + A4*h, ytemp)
59
+ ytemp = y + (dydx * B51 + ak2 * B52 + ak3 * B53 + ak4 * B54) * h
60
+ ak5 = derivs.call(x + A5*h, ytemp)
61
+ ytemp = y + (dydx * B61 + ak2 * B62 + ak3 * B63 + ak4 * B64 +
62
+ ak5 * B65) * h
63
+ ak6 = derivs.call(x + A6*h, ytemp)
64
+ yout = y + (dydx * C1 + ak3 * C3 + ak4 * C4 + ak6 * C6) * h
65
+ yerr = (dydx * DC1 + ak3 * DC3 + ak4 * DC4 + ak5 * DC5 + ak6 * DC6) * h
66
+
67
+ # Return new values and error terms
68
+ [yout, yerr]
69
+ end
70
+ end
71
+
72
+ if __FILE__ == $0
73
+
74
+ def assert_within_epsilon(actual, expected)
75
+ if expected.kind_of?(Vector)
76
+ expected.collect2(actual) { |x, y|
77
+ assert_within_epsilon(x, y)
78
+ }
79
+ else
80
+ (actual - expected).abs / expected < 0.001 or
81
+ raise "Values don't match! Actual was #{actual}, not #{expected}!"
82
+ end
83
+ end
84
+ assert_within_epsilon(10000.0, 10000.00001)
85
+
86
+ const_deriv = proc do |x, y|
87
+ y.collect { |yi| 0.1 }
88
+ end
89
+
90
+ exp_deriv = proc do |x, y|
91
+ y.collect { |yi| yi }
92
+ end
93
+
94
+ integ = Cash_Karp_RK4.new()
95
+
96
+ constvec = Vector.elements([1.0] * 100)
97
+ constderiv = const_deriv.call(0, constvec)
98
+
99
+ # Integrate a constant derivative over a distance of 1.0
100
+ yout, yerr = integ.integrate_fixed_step(constvec, constderiv, 0.0, 1.0,
101
+ const_deriv)
102
+ assert_within_epsilon(yout, Vector.elements([ 1.1 ] * 100))
103
+
104
+ onevec = Vector.elements([1.0] * 100)
105
+ expderiv = exp_deriv.call(0, onevec)
106
+ dist = 0.01 # Keep this small because we only make one integration step
107
+ # Integrate e^x over a distance of 1.0, starting at e^0 == 1.0
108
+ yout, yerr = integ.integrate_fixed_step(onevec, expderiv, 0.0, dist,
109
+ exp_deriv)
110
+ assert_within_epsilon(yout, Vector.elements([ Math::E ** dist ] * 100))
111
+
112
+ print "Tests completed...\n"
113
+ end
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/ruby -w
2
+
3
+ #
4
+ # Integrator library
5
+ # Copyright (C) 2007 Noah Gibbs
6
+ #
7
+ # This library is in the public domain, and may be redistributed in any
8
+ # way.
9
+ #
10
+
11
+ require "matrix"
12
+ require "integrator/rkck.rb"
13
+
14
+ class OneStep_Cash_Karp_RK45 < Cash_Karp_RK4
15
+ SAFETY = 0.9
16
+ PGROW = -0.2
17
+ PSHRNK = -0.25
18
+ ERRCON = 1.89e-4
19
+
20
+ def integrate_ad_step(y, dydx, x, htry, eps, yscal, derivs)
21
+ h = htry
22
+
23
+ while(true) do
24
+ ytemp, yerr = integrate_fixed_step(y, dydx, x, h, derivs)
25
+ errmax = yerr.collect2(yscal) { |err, scal| (err/scal).abs }.max
26
+ errmax /= eps
27
+ break if errmax <= 1.0
28
+ htemp = SAFETY * h * (errmax ** PSHRNK)
29
+ h = h >= 0.0 ? [htemp, 0.1 * h].max : [htemp, 0.1 * h].min
30
+ xnew = x + h
31
+ raise "Step underflow!" if xnew == x
32
+ end
33
+
34
+ if errmax > ERRCON
35
+ hnext = SAFETY * h * (errmax ** PGROW)
36
+ else
37
+ hnext = 5 * h
38
+ end
39
+
40
+ hdid = h
41
+ x += h
42
+ y = ytemp
43
+
44
+ [x, y, hdid, hnext]
45
+ end
46
+
47
+ end
48
+
49
+ if __FILE__ == $0
50
+
51
+ end
@@ -0,0 +1,9 @@
1
+ module Integrator #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 0
5
+ TINY = 1
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
data/lib/integrator.rb ADDED
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/ruby -w
2
+
3
+ #
4
+ # Integrator library
5
+ # Copyright (C) 2007 Noah Gibbs
6
+ #
7
+ # This library is in the public domain, and may be redistributed in any
8
+ # way.
9
+ #
10
+
11
+ $:.unshift File.dirname(__FILE__)
12
+
13
+ module Integrator
14
+
15
+ def Integrator_initialize()
16
+ @kmax = 0 # Max steps to save
17
+ @dxsav = 0.01 # Minimum step size to save data
18
+ @xp = nil # Array to save X coords
19
+ @yp = nil # Array to save Y vectors
20
+
21
+ # Stats from most recent integrate call
22
+ @nok = 0
23
+ @nbad = 0
24
+ @count = 0
25
+ end
26
+
27
+ private
28
+
29
+ def set_adaptive_sample_count(count, num_ok)
30
+ @count = count
31
+ @nok = num_ok
32
+ @nbad = count - num_ok
33
+ end
34
+
35
+ public
36
+
37
+ # Set sample logging. Num_max is the maximum number of
38
+ # samples to log, and min_x_save is the minimum distance
39
+ # between x samples to bother to log.
40
+ #
41
+ def set_max_samples(num_max, min_x_save = 0.01)
42
+ @kmax = num_max
43
+ @xp = []
44
+ @yp = []
45
+ @dxsav = min_x_save
46
+ end
47
+
48
+ # Integrate from time t_start to t_end, starting from initial
49
+ # value y0. Use the 'derivs' proc to evaluate derivatives at
50
+ # a given point. Start by taking a step of size step_start,
51
+ # take steps no smaller than step_min, and attempt to keep the
52
+ # error per step down to a maximum of epsilon.
53
+ #
54
+ def integrate(y0, t_start, t_end, derivs, step_start = 0.1,
55
+ step_min = 0.0001, epsilon = 0.01)
56
+ raise "y0 must be a vector!" unless y0.kind_of?(Vector)
57
+ raise "Must give derivative proc!" unless derivs.kind_of?(Proc)
58
+
59
+ adaptive_integrate(y0, t_start, t_end, epsilon,
60
+ step_start, step_min, derivs)
61
+ end
62
+
63
+ end
data/log/debug.log ADDED
File without changes
data/script/destroy ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.join(File.dirname(__FILE__), '..')
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
data/script/generate ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.join(File.dirname(__FILE__), '..')
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
data/script/txt2html ADDED
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ begin
5
+ require 'newgem'
6
+ rescue LoadError
7
+ puts "\n\nGenerating the website requires the newgem RubyGem"
8
+ puts "Install: gem install newgem\n\n"
9
+ exit(1)
10
+ end
11
+ require 'redcloth'
12
+ require 'syntax/convertors/html'
13
+ require 'erb'
14
+ require File.dirname(__FILE__) + '/../lib/integrator/version.rb'
15
+
16
+ version = Integrator::VERSION::STRING
17
+ download = 'http://rubyforge.org/projects/integrator'
18
+
19
+ class Fixnum
20
+ def ordinal
21
+ # teens
22
+ return 'th' if (10..19).include?(self % 100)
23
+ # others
24
+ case self % 10
25
+ when 1: return 'st'
26
+ when 2: return 'nd'
27
+ when 3: return 'rd'
28
+ else return 'th'
29
+ end
30
+ end
31
+ end
32
+
33
+ class Time
34
+ def pretty
35
+ return "#{mday}#{mday.ordinal} #{strftime('%B')} #{year}"
36
+ end
37
+ end
38
+
39
+ def convert_syntax(syntax, source)
40
+ return Syntax::Convertors::HTML.for_syntax(syntax).convert(source).gsub(%r!^<pre>|</pre>$!,'')
41
+ end
42
+
43
+ if ARGV.length >= 1
44
+ src, template = ARGV
45
+ template ||= File.join(File.dirname(__FILE__), '/../website/template.rhtml')
46
+
47
+ else
48
+ puts("Usage: #{File.split($0).last} source.txt [template.rhtml] > output.html")
49
+ exit!
50
+ end
51
+
52
+ template = ERB.new(File.open(template).read)
53
+
54
+ title = nil
55
+ body = nil
56
+ File.open(src) do |fsrc|
57
+ title_text = fsrc.readline
58
+ body_text = fsrc.read
59
+ syntax_items = []
60
+ body_text.gsub!(%r!<(pre|code)[^>]*?syntax=['"]([^'"]+)[^>]*>(.*?)</\1>!m){
61
+ ident = syntax_items.length
62
+ element, syntax, source = $1, $2, $3
63
+ syntax_items << "<#{element} class='syntax'>#{convert_syntax(syntax, source)}</#{element}>"
64
+ "syntax-temp-#{ident}"
65
+ }
66
+ title = RedCloth.new(title_text).to_html.gsub(%r!<.*?>!,'').strip
67
+ body = RedCloth.new(body_text).to_html
68
+ body.gsub!(%r!(?:<pre><code>)?syntax-temp-(\d+)(?:</code></pre>)?!){ syntax_items[$1.to_i] }
69
+ end
70
+ stat = File.stat(src)
71
+ created = stat.ctime
72
+ modified = stat.mtime
73
+
74
+ $stdout << template.result(binding)