integrator 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.txt CHANGED
@@ -1,3 +1,13 @@
1
1
  This gem includes a framework for numerical integrators, both fixed-step and
2
2
  adaptive, and includes an excellent sample integrator of each type to get
3
3
  you started.
4
+
5
+ It's faster to write such things in C or C++, but better for
6
+ portability to keep it in Ruby. I'm using Ruby vector operations
7
+ rather than iterating manually so that at least the scalability isn't
8
+ too bad...
9
+
10
+ The Cash-Karp embedded Runge-Kutta 4/5 integrator's code is adapted
11
+ from the code in chapter 16 of "Numerical Recipes in C", second
12
+ edition. Adapting this stuff to Ruby and to OO operation is all done
13
+ ad-hoc and by me.
@@ -24,7 +24,7 @@ module Integrator
24
24
  @count = 0
25
25
  end
26
26
 
27
- private
27
+ protected
28
28
 
29
29
  def set_adaptive_sample_count(count, num_ok)
30
30
  @count = count
@@ -11,20 +11,18 @@ require "matrix"
11
11
  require "rubygems"
12
12
  require "integrator"
13
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
14
+ # This is the top-level driver, with logging, used by the more complex
15
+ # integrators in "Numerical Methods in C", second edition. Code is
16
+ # adapted from C, of course.
17
+ #
18
+ module AdaptiveIntegrator
21
19
  include Integrator
22
20
 
23
21
  MAXSTP = 10000
24
22
  TINY = 1.0e-30
25
23
 
26
- def initialize()
27
- Integrator_initialize()
24
+ def integrate_ad_step(y, dydx, x, h, eps, yscal, derivs)
25
+ raise "Adaptive integrators must implement integrate_ad_step!"
28
26
  end
29
27
 
30
28
  def adaptive_integrate(ystart, x1, x2, eps, h1, hmin, derivs)
@@ -77,33 +75,4 @@ class Adaptive_Cash_Karp_RK45 < OneStep_Cash_Karp_RK45
77
75
  raise "Too many steps in integration!"
78
76
  end
79
77
 
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
78
+ end # module AdaptiveIntegrator
@@ -15,6 +15,8 @@
15
15
  require "matrix"
16
16
 
17
17
  class Cash_Karp_RK4
18
+ include Integrator
19
+
18
20
  A2 = 0.2
19
21
  A3 = 0.3
20
22
  A4 = 0.6
@@ -45,6 +47,10 @@ class Cash_Karp_RK4
45
47
  DC5 = -277.00/14336.0
46
48
  DC6 = C6-0.25
47
49
 
50
+ def initialize
51
+ Integrator_initialize()
52
+ end
53
+
48
54
  def integrate_fixed_step(y, dydx, x, h, derivs)
49
55
  raise "Bad type!" unless [y, dydx].all? { |var| var.kind_of?(Vector) }
50
56
  raise "Bad type!" unless [x, h].all? { |var| var.kind_of?(Float) }
@@ -68,46 +74,3 @@ class Cash_Karp_RK4
68
74
  [yout, yerr]
69
75
  end
70
76
  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
@@ -9,9 +9,12 @@
9
9
  #
10
10
 
11
11
  require "matrix"
12
- require "integrator/rkck.rb"
12
+ require "integrator/rkck"
13
+ require "integrator/adaptive"
14
+
15
+ class Adaptive_Cash_Karp_RK45 < Cash_Karp_RK4
16
+ include AdaptiveIntegrator
13
17
 
14
- class OneStep_Cash_Karp_RK45 < Cash_Karp_RK4
15
18
  SAFETY = 0.9
16
19
  PGROW = -0.2
17
20
  PSHRNK = -0.25
@@ -45,7 +48,3 @@ class OneStep_Cash_Karp_RK45 < Cash_Karp_RK4
45
48
  end
46
49
 
47
50
  end
48
-
49
- if __FILE__ == $0
50
-
51
- end
@@ -2,7 +2,7 @@ module Integrator #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 0
5
- TINY = 1
5
+ TINY = 2
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -1,2 +1,2 @@
1
1
  require 'test/unit'
2
- require File.dirname(__FILE__) + '/../lib/integrator'
2
+ #require File.dirname(__FILE__) + '/../lib/integrator'
@@ -1,11 +1,65 @@
1
1
  require File.dirname(__FILE__) + '/test_helper.rb'
2
+ require 'integrator/adaptive.rb'
3
+ require 'integrator/rkck.rb'
4
+ require 'integrator/rkqs.rb'
2
5
 
3
6
  class TestIntegrator < Test::Unit::TestCase
4
7
 
5
8
  def setup
9
+ # Test both fixed-step and adaptive integrators
10
+ @fixed_integrator = Cash_Karp_RK4.new()
11
+ @ad_integrator = Adaptive_Cash_Karp_RK45.new()
6
12
  end
7
-
8
- def test_truth
9
- assert true
13
+
14
+ def assert_within_epsilon(actual, expected)
15
+ if expected.kind_of?(Vector)
16
+ expected.collect2(actual) { |x, y|
17
+ assert_within_epsilon(x, y)
18
+ }
19
+ else
20
+ (actual - expected).abs / expected < 0.001 or
21
+ raise "Values don't match! Actual was #{actual}, not #{expected}!"
22
+ end
23
+ end
24
+
25
+ def test_a_w_e
26
+ assert_within_epsilon(10000.0, 10000.00001)
27
+ assert_within_epsilon(10000.0, 10001)
28
+ end
29
+
30
+ def test_const_func
31
+ const_deriv_proc = proc do |x, y|
32
+ y.collect { |yi| 0.1 }
33
+ end
34
+ constvec = Vector.elements([1.0] * 100)
35
+ constderiv = const_deriv_proc.call(0, constvec)
36
+
37
+ # Integrate a constant derivative over a distance of 1.0
38
+ yout, yerr = @fixed_integrator.integrate_fixed_step(constvec, constderiv,
39
+ 0.0, 1.0,
40
+ const_deriv_proc)
41
+ assert_within_epsilon(yout, Vector.elements([ 1.1 ] * 100))
10
42
  end
43
+
44
+ def test_exp_func
45
+ # This takes the derivative of e^x for each vector element.
46
+ # The derivative of e^x is just e^x again.
47
+ exp_deriv_proc = proc do |x, y|
48
+ y.collect { |yi| yi }
49
+ end
50
+
51
+ onevec = Vector.elements([1.0] * 100)
52
+ expderiv = exp_deriv_proc.call(0, onevec)
53
+ dist = 0.01 # Keep this small because we only make one integration step
54
+ # Integrate e^x over a distance of 1.0, starting at e^0 == 1.0
55
+ yout, yerr = @fixed_integrator.integrate_fixed_step(onevec, expderiv,
56
+ 0.0, dist,
57
+ exp_deriv_proc)
58
+ assert_within_epsilon(yout, Vector.elements([ Math::E ** dist ] * 100))
59
+
60
+ y = @ad_integrator.adaptive_integrate(onevec, 0.0, 1.0, 0.001, 0.01,
61
+ 0.0001, exp_deriv_proc)
62
+ assert_within_epsilon(y, Vector.elements( [ Math::E ] * 100 ))
63
+ end
64
+
11
65
  end
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: integrator
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.0.1
7
- date: 2007-10-14 00:00:00 -07:00
6
+ version: 0.0.2
7
+ date: 2007-10-15 00:00:00 -07:00
8
8
  summary: Numerical integration framework
9
9
  require_paths:
10
10
  - lib