integrator 0.0.1 → 0.0.2

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