integration 0.1.3 → 0.1.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b284dcf8ea2203fe8f91d2d8f46a5a2e866c9aa9
4
- data.tar.gz: 0973511e4961d3ff5f3cb1378396c20594456e51
3
+ metadata.gz: 4dbd1debd129047a700d50d3934dd668da72b23d
4
+ data.tar.gz: 572db4475376afd86960f615c795312106ffab5b
5
5
  SHA512:
6
- metadata.gz: df5ef6303e64fc54085aa861cd82e158d37efb196e6465423d0a293f862c9091bedf8baa631df226e4dd39942e6c2017a925909e1e7200f3e1bd891a32daa794
7
- data.tar.gz: 533823b375f08fe5811affb0586252f2c5f3e4ea7deb09cbc7572530c05dc4ac6c9ddc2c6cf6c9218b169890eaadb0b0a8d78f15b737088f9b2c808fa47f3e24
6
+ metadata.gz: fc7f3547bee2355ff9f846f6a9b7e91d5d44058ac4eabcddad19b1e25ab019708e6fc1e03b4169b798f871f7aa79c5b3b9ff27842d852f5514616f54cc3d08be
7
+ data.tar.gz: 007a90888618d8b75c6aec71340644db283ead8b3b5d5ba70d6fc7736f04d57f5a5349507245d81759c85fd910e87444e86f0fe69a2b9b46096adbd56b592313
data/.gitignore CHANGED
@@ -2,3 +2,7 @@
2
2
  Gemfile.lock
3
3
  coverage/*
4
4
  pkg/*
5
+
6
+ doc/
7
+ .yardoc/
8
+
@@ -0,0 +1,4 @@
1
+ --title "Integration"
2
+ --markup markdown
3
+ lib/**/*.rb
4
+ README.md History.txt
@@ -0,0 +1,61 @@
1
+ Integration is part of SciRuby, a collaborative effort to bring scientific computation to Ruby. If you want to help, please
2
+ do so!
3
+
4
+ This guide covers ways in which you can contribute to the development of SciRuby and, more specifically, Integration.
5
+
6
+ ## How to help
7
+
8
+ There are various ways to help Integration: bug reports, coding and documentation. All of them are important.
9
+
10
+ First, you can help implement new features or bug fixes. To do that, visit our [issue tracker][2]. If you find something that you want to work on, post it in the issue or on our [mailing list][1].
11
+
12
+ You need to send tests together with your code. No exceptions. You can ask for our opinion, but we won't accept patches without good spec coverage.
13
+
14
+ We use RSpec for testing. If you aren't familiar with it, there's a good [guide to better specs with RSpec](http://betterspecs.org/) that shows a bit of the syntax and how to use it properly.
15
+ However, the best resource is probably the specs that already exist -- so just read them.
16
+
17
+ And don't forget to write documentation (we use YARD). It's necessary to allow others to know what's available in the library. There's a section on it later in this guide.
18
+
19
+ We only accept bug reports and pull requests in GitHub. You'll need to create a new (free) account if you don't have one already. To learn how to create a pull request, please see [this guide on collaborating](https://help.github.com/categories/63/articles).
20
+
21
+ If you have a question about how to use Integration or SciRuby in general or a feature/change in mind, please ask the [sciruby-dev mailing list][1].
22
+
23
+ Thanks!
24
+
25
+ ## Coding
26
+
27
+ To start working on Integration, clone the repository and use bundler to install the dependencies:
28
+
29
+ ```bash
30
+ $ git clone git://github.com/SciRuby/integration.git
31
+ $ cd integration
32
+ $ bundle install
33
+ ```
34
+
35
+ If everything's fine until now, you can create a new branch to work on your feature:
36
+
37
+ ```bash
38
+ $ git branch new-feature
39
+ $ git checkout new-feature
40
+ ```
41
+
42
+ Before commiting any code, please read our
43
+ [Contributor Agreement](http://github.com/SciRuby/sciruby/wiki/Contributor-Agreement).
44
+
45
+ ## Style guide
46
+
47
+ Follow the [GitHub styleguide](https://github.com/styleguide/ruby). If you have any doubt, contact us.
48
+
49
+ ## Documentation
50
+
51
+ We are using [YARD](http://yardoc.org/) for documenting the source. There is still a lot to do: more references, more examples, better names for parameters, etc. We accept patches for each and every one of these problems -- if you want to send a patch improving documentation, we will be pleased to review it!
52
+
53
+ See the [YARD guides](http://www.yardoc.org/guides/index.html) for more information.
54
+
55
+ ## Conclusion
56
+
57
+ This guide was heavily based on the
58
+ [Contributing to Ruby on Rails guide](http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html).
59
+
60
+ [1]: https://groups.google.com/forum/?fromgroups#!forum/sciruby-dev
61
+ [2]: https://github.com/sciruby/integration/issues
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
1
  source "https://www.rubygems.org"
2
2
 
3
+ gem 'rb-gsl', '~> 1.16'
4
+
3
5
  gemspec
@@ -1,3 +1,8 @@
1
+ === 0.1.4 / 2015-05-23
2
+ * GSL will be used if available. The tests will only pass if rb-gsl is installed.
3
+ * Specs depending on GSL will fail without `rb-gsl` installed.
4
+ * Added CONTRIBUTING.md file.
5
+
1
6
  === 0.1.3 / 2015-05-22
2
7
  * Remove rb-gsl dependency.
3
8
 
@@ -3,20 +3,27 @@ $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
3
3
  require 'integration/version'
4
4
 
5
5
  Gem::Specification.new do |s|
6
- s.name = "integration"
6
+ s.name = 'integration'
7
7
  s.version = Integration::VERSION
8
- s.authors = ["Claudio Bustos","Ben Gimpert"]
9
- s.description = "Numerical integration for Ruby, with a simple interface"
10
- s.email = ["clbustos@gmail.com", "No Email"]
8
+
9
+ s.authors = ['Claudio Bustos', 'Ben Gimpert']
10
+ s.email = ['clbustos@gmail.com']
11
+ s.homepage = 'http://sciruby.com'
12
+
13
+ s.summary = 'Numerical integration for Ruby with a simple interface.'
14
+ s.description = 'Numerical integration for Ruby with a simple interface.'
15
+ s.license = 'See README.md.'
16
+
11
17
  s.files = `git ls-files`.split("\n")
12
18
  s.test_files = `git ls-files -- {test,spec,features,benchmark}/*`.split("\n")
13
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
14
- s.require_paths = ["lib"]
15
- s.summary = "A suite for integration in Ruby"
20
+ s.require_paths = ['lib']
21
+
22
+ s.required_ruby_version = '>= 1.9.3'
16
23
 
17
24
  s.add_runtime_dependency 'text-table', '~> 1.2'
18
25
 
26
+ s.add_development_dependency 'bundler', '>= 1.3.0', '< 2.0'
19
27
  s.add_development_dependency 'rake', '~> 10.4'
20
- s.add_development_dependency 'bundler', '~> 1.9'
21
28
  s.add_development_dependency 'rspec', '~> 3.2'
22
29
  end
@@ -23,554 +23,178 @@
23
23
  # be used in advertising or otherwise to promote the sale, use or other dealings
24
24
  # in this Software without prior written authorization from Beng.
25
25
 
26
+ require 'integration/methods'
27
+
26
28
  # Diverse integration methods
27
29
  # Use Integration.integrate as wrapper to direct access to methods
28
30
  #
29
- # Method API
30
- #
31
-
32
31
  class Integration
33
- # Minus Infinity
34
- MInfinity=:minfinity
35
- # Infinity
36
- Infinity=:infinity
37
- class << self
38
-
39
- # Create a method 'has_<library>' on Module
40
- # which require a library and return true or false
41
- # according to success of failure
42
- def create_has_library(library) #:nodoc:
43
- define_singleton_method("has_#{library}?") do
44
- cv="@@#{library}"
45
- if !class_variable_defined? cv
46
- begin
47
- require library.to_s
48
- class_variable_set(cv, true)
49
- rescue LoadError
50
- class_variable_set(cv, false)
51
- end
52
- end
53
- class_variable_get(cv)
54
- end
55
- end
56
- # Rectangle method
57
- # +n+ implies number of subdivisions
58
- # Source:
59
- # * Ayres : Outline of calculus
60
- def rectangle(t1, t2, n, &f)
61
- d=(t2-t1) / n.to_f
62
- n.times.inject(0) {|ac,i|
63
- ac+f[t1+d*(i+0.5)]
64
- }*d
65
- end
66
- alias_method :midpoint, :rectangle
67
- # Trapezoid method
68
- # +n+ implies number of subdivisions
69
- # Source:
70
- # * Ayres : Outline of calculus
71
- def trapezoid(t1, t2, n, &f)
72
- d=(t2-t1) / n.to_f
73
- (d/2.0)*(f[t1]+
74
- 2*(1..(n-1)).inject(0){|ac,i|
75
- ac+f[t1+d*i]
76
- }+f[t2])
77
- end
78
-
79
- # Simpson's rule
80
- # +n+ implies number of subdivisions
81
- # Source:
82
- # * Ayres : Outline of calculus
83
- def simpson(t1, t2, n, &f)
84
- n += 1 unless n % 2 == 0
85
- d=(t2-t1) / n.to_f
86
- out= (d / 3.0)*(f[t1.to_f].to_f+
87
- ((1..(n-1)).inject(0) {|ac,i|
88
- ac+((i%2==0) ? 2 : 4)*f[t1+d*i]
89
- })+f[t2.to_f].to_f)
90
- out
91
- end
92
-
93
- # Simpson's 3/8 Rule
94
- # +n+ implies number of subdivisions
95
- # Source:
96
- # * Burden, Richard L. and Faires, J. Douglas (2000): Numerical Analysis (7th ed.). Brooks/Cole
97
- def simpson3by8(t1, t2, n, &f)
98
- d = (t2-t1) / n.to_f
99
- ac = 0
100
- (0..n-1).each do |i|
101
- ac+=(d/8.0)*(f[t1+i*d]+3*f[t1+i*d+d/3]+3*f[t1+i*d+2*d/3]+f[t1+(i+1)*d])
102
- end
103
- ac
104
- end
105
-
106
- # Boole's Rule
107
- # +n+ implies number of subdivisions
108
- # Source:
109
- # Weisstein, Eric W. "Boole's Rule." From MathWorld—A Wolfram Web Resource
110
- def boole(t1, t2, n, &f)
111
- d = (t2-t1) / n.to_f
112
- ac = 0
113
- (0..n-1).each do |i|
114
- ac+=(d/90.0)*(7*f[t1+i*d]+32*f[t1+i*d+d/4]+12*f[t1+i*d+d/2]+32*f[t1+i*d+3*d/4]+7*f[t1+(i+1)*d])
115
- end
116
- ac
117
- end
118
-
119
- # Open Trapezoid method
120
- # +n+ implies number of subdivisions
121
- # Values computed at mid point and end point instead of starting points
122
- def open_trapezoid(t1, t2, n, &f)
123
- d = (t2-t1) / n.to_f
124
- ac = 0
125
- (0..n-1).each do |i|
126
- ac+=(d/2.0)*(f[t1+i*d+d/3]+f[t1+i*d+2*d/3])
127
- end
128
- ac
129
- end
130
-
131
- # Milne's Method
132
- # +n+ implies number of subdivisions
133
- # Source:
134
- # Abramowitz, M. and Stegun, I. A. (Eds.).
135
- # Handbook of Mathematical Functions with Formulas, Graphs, and Mathematical Tables,
136
- # 9th printing. New York: Dover, pp. 896-897, 1972.
137
- def milne(t1, t2, n, &f)
138
- d = (t2-t1) / n.to_f
139
- ac = 0
140
- (0..n-1).each do |i|
141
- ac+=(d/3.0)*(2*f[t1+i*d+d/4]-f[t1+i*d+d/2]+2*f[t1+i*d+3*d/4])
142
- end
143
- ac
144
- end
145
-
146
- # Adaptive Quadrature
147
- # Calls the Simpson's rule recursively on subintervals
148
- # in case the error exceeds the desired tolerance
149
- # +tolerance+ is the desired tolerance of error
150
- def adaptive_quadrature(a, b, tolerance)
151
- h = (b.to_f - a) / 2
152
- fa = yield(a)
153
- fc = yield(a + h)
154
- fb = yield(b)
155
- s = h * (fa + (4 * fc) + fb) / 3
156
- helper = Proc.new { |a, b, fa, fb, fc, h, s, level|
157
- if level < 1/tolerance.to_f
158
- fd = yield(a + (h / 2))
159
- fe = yield(a + (3 * (h / 2)))
160
- s1 = h * (fa + (4.0 * fd) + fc) / 6
161
- s2 = h * (fc + (4.0 * fe) + fb) / 6
162
- if ((s1 + s2) - s).abs <= tolerance
163
- s1 + s2
164
- else
165
- helper.call(a, a + h, fa, fc, fd, h / 2, s1, level + 1) +
166
- helper.call(a + h, b, fc, fb, fe, h / 2, s2, level + 1)
167
- end
168
- else
169
- raise "Integral did not converge"
170
- end
171
- }
172
- return helper.call(a, b, fa, fb, fc, h, s, 1)
173
- end
174
-
175
- # Gaussian Quadrature
176
- # n-point Gaussian quadrature rule gives an exact result for polynomials of degree 2n − 1 or less
177
- def gauss(t1, t2, n)
178
- case n
179
- when 1
180
- z = [0.0]
181
- w = [2.0]
182
- when 2
183
- z = [-0.57735026919, 0.57735026919]
184
- w = [1.0, 1.0]
185
- when 3
186
- z = [-0.774596669241, 0.0, 0.774596669241]
187
- w = [0.555555555556, 0.888888888889, 0.555555555556]
188
- when 4
189
- z = [-0.861136311594, -0.339981043585, 0.339981043585, 0.861136311594]
190
- w = [0.347854845137, 0.652145154863, 0.652145154863, 0.347854845137]
191
- when 5
192
- z = [-0.906179845939, -0.538469310106, 0.0, 0.538469310106, 0.906179845939]
193
- w = [0.236926885056, 0.478628670499, 0.568888888889, 0.478628670499, 0.236926885056]
194
- when 6
195
- z = [-0.932469514203, -0.661209386466, -0.238619186083, 0.238619186083, 0.661209386466, 0.932469514203]
196
- w = [0.171324492379, 0.360761573048, 0.467913934573, 0.467913934573, 0.360761573048, 0.171324492379]
197
- when 7
198
- z = [-0.949107912343, -0.741531185599, -0.405845151377, 0.0, 0.405845151377, 0.741531185599, 0.949107912343]
199
- w = [0.129484966169, 0.279705391489, 0.381830050505, 0.417959183673, 0.381830050505, 0.279705391489, 0.129484966169]
200
- when 8
201
- z = [-0.960289856498, -0.796666477414, -0.525532409916, -0.183434642496, 0.183434642496, 0.525532409916, 0.796666477414, 0.960289856498]
202
- w = [0.10122853629, 0.222381034453, 0.313706645878, 0.362683783378, 0.362683783378, 0.313706645878, 0.222381034453, 0.10122853629]
203
- when 9
204
- z = [-0.968160239508, -0.836031107327, -0.613371432701, -0.324253423404, 0.0, 0.324253423404, 0.613371432701, 0.836031107327, 0.968160239508]
205
- w = [0.0812743883616, 0.180648160695, 0.260610696403, 0.31234707704, 0.330239355001, 0.31234707704, 0.260610696403, 0.180648160695, 0.0812743883616]
206
- when 10
207
- z = [-0.973906528517, -0.865063366689, -0.679409568299, -0.433395394129, -0.148874338982, 0.148874338982, 0.433395394129, 0.679409568299, 0.865063366689, 0.973906528517]
208
- w = [0.0666713443087, 0.149451349151, 0.219086362516, 0.26926671931, 0.295524224715, 0.295524224715, 0.26926671931, 0.219086362516, 0.149451349151, 0.0666713443087]
209
- else
210
- raise "Invalid number of spaced abscissas #{n}, should be 1-10"
211
- end
212
-
213
- sum = 0
214
- (0...n).each do |i|
215
- t = ((t1.to_f + t2) / 2.0) + (((t2 - t1) / 2.0) * z[i])
216
- sum += w[i] * yield(t)
217
- end
218
- return ((t2 - t1) / 2.0) * sum
219
- end
220
-
221
- # Gauss Kronrod Rule:
222
- # Provides a 3n+1 order estimate while re-using the function values of a lower-order(n order) estimate
223
- # Source:
224
- # "Gauss–Kronrod quadrature formula", Encyclopedia of Mathematics, Springer, ISBN 978-1-55608-010-4
225
- def gauss_kronrod(t1,t2,n,points)
226
- #g7k15
227
- case points
228
- when 15
229
-
230
- z = [-0.9914553711208126, -0.9491079123427585, -0.8648644233597691,
231
- -0.7415311855993945, -0.5860872354676911, -0.4058451513773972,
232
- -0.20778495500789848, 0.0, 0.20778495500789848,
233
- 0.4058451513773972, 0.5860872354676911, 0.7415311855993945,
234
- 0.8648644233597691, 0.9491079123427585, 0.9914553711208126]
235
-
236
- w = [0.022935322010529224, 0.06309209262997856, 0.10479001032225019,
237
- 0.14065325971552592, 0.1690047266392679, 0.19035057806478542,
238
- 0.20443294007529889, 0.20948214108472782, 0.20443294007529889,
239
- 0.19035057806478542, 0.1690047266392679, 0.14065325971552592,
240
- 0.10479001032225019, 0.06309209262997856, 0.022935322010529224]
241
-
242
- when 21
243
- #g10k21
244
-
245
- z = [-0.9956571630258081, -0.9739065285171717, -0.9301574913557082,
246
- -0.8650633666889845, -0.7808177265864169, -0.6794095682990244,
247
- -0.5627571346686047, -0.4333953941292472, -0.2943928627014602,
248
- -0.14887433898163122, 0.0, 0.14887433898163122,
249
- 0.2943928627014602, 0.4333953941292472, 0.5627571346686047,
250
- 0.6794095682990244, 0.7808177265864169, 0.8650633666889845,
251
- 0.9301574913557082, 0.9739065285171717, 0.9956571630258081]
252
-
253
- w = [0.011694638867371874, 0.032558162307964725,
254
- 0.054755896574351995, 0.07503967481091996, 0.0931254545836976,
255
- 0.10938715880229764, 0.12349197626206584, 0.13470921731147334,
256
- 0.14277593857706009, 0.14773910490133849, 0.1494455540029169,
257
- 0.14773910490133849, 0.14277593857706009, 0.13470921731147334,
258
- 0.12349197626206584, 0.10938715880229764, 0.0931254545836976,
259
- 0.07503967481091996, 0.054755896574351995, 0.032558162307964725,
260
- 0.011694638867371874]
261
-
262
- when 31
263
- #g15k31
264
-
265
- z = [-0.9980022986933971, -0.9879925180204854, -0.9677390756791391,
266
- -0.937273392400706, -0.8972645323440819, -0.8482065834104272,
267
- -0.790418501442466, -0.7244177313601701, -0.650996741297417,
268
- -0.5709721726085388, -0.4850818636402397, -0.3941513470775634,
269
- -0.29918000715316884, -0.20119409399743451, -0.1011420669187175,
270
- 0.0, 0.1011420669187175, 0.20119409399743451,
271
- 0.29918000715316884, 0.3941513470775634, 0.4850818636402397,
272
- 0.5709721726085388, 0.650996741297417, 0.7244177313601701,
273
- 0.790418501442466, 0.8482065834104272, 0.8972645323440819,
274
- 0.937273392400706, 0.9677390756791391, 0.9879925180204854,
275
- 0.9980022986933971]
276
32
 
277
- w = [0.005377479872923349, 0.015007947329316122, 0.02546084732671532,
278
- 0.03534636079137585, 0.04458975132476488, 0.05348152469092809,
279
- 0.06200956780067064, 0.06985412131872826, 0.07684968075772038,
280
- 0.08308050282313302, 0.08856444305621176, 0.09312659817082532,
281
- 0.09664272698362368, 0.09917359872179196, 0.10076984552387559,
282
- 0.10133000701479154, 0.10076984552387559, 0.09917359872179196,
283
- 0.09664272698362368, 0.09312659817082532, 0.08856444305621176,
284
- 0.08308050282313302, 0.07684968075772038, 0.06985412131872826,
285
- 0.06200956780067064, 0.05348152469092809, 0.04458975132476488,
286
- 0.03534636079137585, 0.02546084732671532, 0.015007947329316122,
287
- 0.005377479872923349]
288
-
289
- when 41
290
- #g20k41
291
-
292
- z = [-0.9988590315882777, -0.9931285991850949, -0.9815078774502503,
293
- -0.9639719272779138, -0.9408226338317548, -0.912234428251326,
294
- -0.878276811252282, -0.8391169718222188, -0.7950414288375512,
295
- -0.7463319064601508, -0.6932376563347514, -0.636053680726515,
296
- -0.5751404468197103, -0.5108670019508271, -0.4435931752387251,
297
- -0.37370608871541955, -0.301627868114913, -0.22778585114164507,
298
- -0.15260546524092267, -0.07652652113349734, 0.0,
299
- 0.07652652113349734, 0.15260546524092267, 0.22778585114164507,
300
- 0.301627868114913, 0.37370608871541955, 0.4435931752387251,
301
- 0.5108670019508271, 0.5751404468197103, 0.636053680726515,
302
- 0.6932376563347514, 0.7463319064601508, 0.7950414288375512,
303
- 0.8391169718222188, 0.878276811252282, 0.912234428251326,
304
- 0.9408226338317548, 0.9639719272779138, 0.9815078774502503,
305
- 0.9931285991850949, 0.9988590315882777]
306
-
307
- w = [0.0030735837185205317, 0.008600269855642943,
308
- 0.014626169256971253, 0.020388373461266523, 0.02588213360495116,
309
- 0.0312873067770328, 0.036600169758200796, 0.041668873327973685,
310
- 0.04643482186749767, 0.05094457392372869, 0.05519510534828599,
311
- 0.05911140088063957, 0.06265323755478117, 0.06583459713361842,
312
- 0.06864867292852161, 0.07105442355344407, 0.07303069033278667,
313
- 0.07458287540049918, 0.07570449768455667, 0.07637786767208074,
314
- 0.07660071191799965, 0.07637786767208074, 0.07570449768455667,
315
- 0.07458287540049918, 0.07303069033278667, 0.07105442355344407,
316
- 0.06864867292852161, 0.06583459713361842, 0.06265323755478117,
317
- 0.05911140088063957, 0.05519510534828599, 0.05094457392372869,
318
- 0.04643482186749767, 0.041668873327973685, 0.036600169758200796,
319
- 0.0312873067770328, 0.02588213360495116, 0.020388373461266523,
320
- 0.014626169256971253, 0.008600269855642943,
321
- 0.0030735837185205317]
322
-
323
- when 61
324
- #g30k61
325
-
326
- z = [-0.9994844100504906, -0.9968934840746495, -0.9916309968704046,
327
- -0.9836681232797472, -0.9731163225011262, -0.9600218649683075,
328
- -0.94437444474856, -0.9262000474292743, -0.9055733076999078,
329
- -0.8825605357920527, -0.8572052335460612, -0.8295657623827684,
330
- -0.799727835821839, -0.7677774321048262, -0.7337900624532268,
331
- -0.6978504947933158, -0.6600610641266269, -0.6205261829892429,
332
- -0.5793452358263617, -0.5366241481420199, -0.49248046786177857,
333
- -0.44703376953808915, -0.4004012548303944, -0.3527047255308781,
334
- -0.30407320227362505, -0.25463692616788985,
335
- -0.20452511668230988, -0.15386991360858354,
336
- -0.10280693796673702, -0.0514718425553177, 0.0,
337
- 0.0514718425553177, 0.10280693796673702, 0.15386991360858354,
338
- 0.20452511668230988, 0.25463692616788985, 0.30407320227362505,
339
- 0.3527047255308781, 0.4004012548303944, 0.44703376953808915,
340
- 0.49248046786177857, 0.5366241481420199, 0.5793452358263617,
341
- 0.6205261829892429, 0.6600610641266269, 0.6978504947933158,
342
- 0.7337900624532268, 0.7677774321048262, 0.799727835821839,
343
- 0.8295657623827684, 0.8572052335460612, 0.8825605357920527,
344
- 0.9055733076999078, 0.9262000474292743, 0.94437444474856,
345
- 0.9600218649683075, 0.9731163225011262, 0.9836681232797472,
346
- 0.9916309968704046, 0.9968934840746495, 0.9994844100504906]
347
-
348
- w = [0.0013890136986770077, 0.003890461127099884,
349
- 0.0066307039159312926, 0.009273279659517764,
350
- 0.011823015253496341, 0.014369729507045804, 0.01692088918905327,
351
- 0.019414141193942382, 0.021828035821609193, 0.0241911620780806,
352
- 0.0265099548823331, 0.02875404876504129, 0.030907257562387762,
353
- 0.03298144705748372, 0.034979338028060025, 0.03688236465182123,
354
- 0.038678945624727595, 0.040374538951535956,
355
- 0.041969810215164244, 0.04345253970135607, 0.04481480013316266,
356
- 0.04605923827100699, 0.04718554656929915, 0.04818586175708713,
357
- 0.04905543455502978, 0.04979568342707421, 0.05040592140278235,
358
- 0.05088179589874961, 0.051221547849258774, 0.05142612853745902,
359
- 0.05149472942945157, 0.05142612853745902, 0.051221547849258774,
360
- 0.05088179589874961, 0.05040592140278235, 0.04979568342707421,
361
- 0.04905543455502978, 0.04818586175708713, 0.04718554656929915,
362
- 0.04605923827100699, 0.04481480013316266, 0.04345253970135607,
363
- 0.041969810215164244, 0.040374538951535956,
364
- 0.038678945624727595, 0.03688236465182123, 0.034979338028060025,
365
- 0.03298144705748372, 0.030907257562387762, 0.02875404876504129,
366
- 0.0265099548823331, 0.0241911620780806, 0.021828035821609193,
367
- 0.019414141193942382, 0.01692088918905327, 0.014369729507045804,
368
- 0.011823015253496341, 0.009273279659517764,
369
- 0.0066307039159312926, 0.003890461127099884,
370
- 0.0013890136986770077]
371
-
372
- else # using 15 point quadrature
373
-
374
- n = 15
375
-
376
- z = [-0.9914553711208126, -0.9491079123427585, -0.8648644233597691,
377
- -0.7415311855993945, -0.5860872354676911, -0.4058451513773972,
378
- -0.20778495500789848, 0.0, 0.20778495500789848,
379
- 0.4058451513773972, 0.5860872354676911, 0.7415311855993945,
380
- 0.8648644233597691, 0.9491079123427585, 0.9914553711208126]
381
-
382
- w = [0.022935322010529224, 0.06309209262997856, 0.10479001032225019,
383
- 0.14065325971552592, 0.1690047266392679, 0.19035057806478542,
384
- 0.20443294007529889, 0.20948214108472782, 0.20443294007529889,
385
- 0.19035057806478542, 0.1690047266392679, 0.14065325971552592,
386
- 0.10479001032225019, 0.06309209262997856, 0.022935322010529224]
387
-
388
- end
33
+ # Minus Infinity
34
+ MInfinity = :minfinity
389
35
 
390
- sum = 0
391
- (0...n).each do |i|
392
- t = ((t1.to_f + t2) / 2.0) + (((t2 - t1) / 2.0) * z[i])
393
- sum += w[i] * yield(t)
394
- end
36
+ # Infinity
37
+ Infinity = :infinity
395
38
 
396
- ((t2 - t1) / 2.0) * sum
397
- end
39
+ # Pure Ruby methods available.
40
+ RUBY_METHODS = [:rectangle, :trapezoid, :simpson, :adaptive_quadrature,
41
+ :gauss, :romberg, :monte_carlo, :gauss_kronrod,
42
+ :simpson3by8, :boole, :open_trapezoid, :milne]
398
43
 
399
- # Romberg Method:
400
- # It is obtained by applying extrapolation techniques repeatedly on the trapezoidal rule
401
- def romberg(a, b, tolerance,max_iter=20)
402
- # NOTE one-based arrays are used for convenience
403
- h = b.to_f - a
404
- m = 1
405
- close = 1
406
- r = [[(h / 2) * (yield(a) + yield(b))]]
407
- j = 0
408
- hn=lambda {|n| h/(2**n)}
409
- while j <= max_iter && tolerance < close
410
- j+=1
411
- r.push((j+1).times.map{[]})
412
- ul=2**(j-1)
413
- r[j][0]=r[j-1][0] / 2.0 + hn[j] * (1..ul).inject(0) {|ac,k| ac+yield(a + (2*k-1)* hn[j])}
414
- (1..j).each do |k|
415
- r[j][k] = ( (4**k) * r[j][k-1] - r[j-1][k-1]) / ((4**k)-1)
416
- end
417
- close = (r[j][j] - r[j-1][j-1])
418
- end
419
- r[j][j]
420
- end
44
+ # Methods available when using the `rb-gsl` gem.
45
+ GSL_METHODS = [:qng, :qag]
421
46
 
422
- # Monte Carlo:
423
- # Uses a non deterministic(probabilistic) approach for calculation of definite integrals
424
- # Estimates the integral by randomly choosing points in a set and then calculating the number of points that fall in the desired area
425
- def monte_carlo(t1, t2, n)
426
- width = (t2 - t1).to_f
427
- height = nil
428
- vals = []
429
- n.times do
430
- t = t1 + (rand() * width)
431
- ft = yield(t)
432
- height = ft if height.nil? || ft > height
433
- vals << ft
434
- end
435
- area_ratio = 0
436
- vals.each do |ft|
437
- area_ratio += (ft / height.to_f) / n.to_f
438
- end
439
- return (width * height) * area_ratio
440
- end
47
+ class << self
441
48
 
442
- def is_infinite?(v)
443
- v == Infinity || v == MInfinity
49
+ # Check if `value` is plus or minus infinity.
50
+ #
51
+ # @param value Value to be tested.
52
+ def infinite?(value)
53
+ value == Integration::Infinity || value == Integration::MInfinity
444
54
  end
445
55
 
446
- # Methods available on pure ruby
447
- RUBY_METHOD=[:rectangle,:trapezoid,:simpson, :adaptive_quadrature , :gauss, :romberg, :monte_carlo, :gauss_kronrod, :simpson3by8, :boole, :open_trapezoid, :milne]
448
-
449
- # Methods available with Ruby/GSL library
450
- GSL_METHOD=[:qng, :qag]
451
-
452
- # Get the integral for a function +f+, with bounds +t1+ and
453
- # +t2+ given a hash of +options+.
454
- # If Ruby/GSL is available, you could use +Integration::Minfinity+
455
- # and +Integration::Infinity+ as bounds. Method
456
- # Options are
56
+ # Get the integral for a function +f+, with bounds +t1+ and +t2+ given a
57
+ # hash of +options+. If Ruby/GSL is available, you can use
58
+ # +Integration::Minfinity+ and +Integration::Infinity+ as bounds. Method
59
+ #
60
+ # Options are:
457
61
  # [:tolerance] Maximum difference between real and calculated integral.
458
- # Default: 1e-10
459
- # [:initial_step] Initial number of subdivitions
460
- # [:step] Subdivitions increment on each iteration
62
+ # Default: 1e-10.
63
+ # [:initial_step] Initial number of subdivisions.
64
+ # [:step] Subdivition increment on each iteration.
461
65
  # [:method] Integration method.
462
- # Methods are
463
- # [:rectangle] for [:initial_step+:step*iteration] quadrilateral subdivisions
464
- # [:trapezoid] for [:initial_step+:step*iteration] trapezoid-al subdivisions
465
- # [:simpson] for [:initial_step+:step*iteration] parabolic subdivisions
466
- # [:adaptive_quadrature] for recursive appoximations until error [tolerance]
467
- # [:gauss] [:initial_step+:step*iteration] weighted subdivisons using translated -1 -> +1 endpoints
468
- # [:romberg] extrapolation of recursion approximation until error < [tolerance]
469
- # [:monte_carlo] make [:initial_step+:step*iteration] random samples, and check for above/below curve
470
- # [:qng] GSL QNG non-adaptive Gauss-Kronrod integration
471
- # [:qag] GSL QAG adaptive integration, with support for infinite bounds
472
- def integrate(t1,t2,options=Hash.new, &f)
473
- inf_bounds=(is_infinite?(t1) or is_infinite?(t2))
474
- raise "No function passed" unless block_given?
475
- raise "Non-numeric bounds" unless ((t1.is_a? Numeric) and (t2.is_a? Numeric)) or inf_bounds
476
- if(inf_bounds)
477
- lower_bound=t1
478
- upper_bound=t2
479
- options[:method]=:qag if options[:method].nil?
66
+ #
67
+ # Available methods are:
68
+ #
69
+ # [:rectangle] for [:initial_step+:step*iteration] quadrilateral subdivisions.
70
+ # [:trapezoid] for [:initial_step+:step*iteration] trapezoid-al subdivisions.
71
+ # [:simpson] for [:initial_step+:step*iteration] parabolic subdivisions.
72
+ # [:adaptive_quadrature] for recursive appoximations until error [tolerance].
73
+ # [:gauss] [:initial_step+:step*iteration] weighted subdivisons using
74
+ # translated -1 -> +1 endpoints.
75
+ # [:romberg] extrapolation of recursion approximation until error < [tolerance].
76
+ # [:monte_carlo] make [:initial_step+:step*iteration] random samples, and
77
+ # check for above/below curve.
78
+ # [:qng] GSL QNG non-adaptive Gauss-Kronrod integration.
79
+ # [:qag] GSL QAG adaptive integration, with support for infinite bounds.
80
+ def integrate(t1, t2, options = {}, &f)
81
+ inf_bounds = (infinite?(t1) || infinite?(t2))
82
+
83
+ fail 'No function passed' unless block_given?
84
+ fail 'Non-numeric bounds' unless ((t1.is_a? Numeric) && (t2.is_a? Numeric)) || inf_bounds
85
+
86
+ if inf_bounds
87
+ lower_bound = t1
88
+ upper_bound = t2
89
+ options[:method] = :qag if options[:method].nil?
480
90
  else
481
91
  lower_bound = [t1, t2].min
482
92
  upper_bound = [t1, t2].max
483
93
  end
484
- def_method=(has_gsl?) ? :qag : :simpson
485
- default_opts={:tolerance=>1e-10, :initial_step=>16, :step=>16, :method=>def_method}
486
- options=default_opts.merge(options)
487
- if RUBY_METHOD.include? options[:method]
488
- raise "Ruby methods doesn't support infinity bounds" if inf_bounds
489
- integrate_ruby(lower_bound,upper_bound,options,&f)
490
- elsif GSL_METHOD.include? options[:method]
491
- integrate_gsl(lower_bound,upper_bound,options,&f)
94
+
95
+ def_method = (Integration.has_gsl?) ? :qag : :simpson
96
+ default_opts = { tolerance: 1e-10, initial_step: 16, step: 16, method: def_method }
97
+ options = default_opts.merge(options)
98
+
99
+ if RUBY_METHODS.include? options[:method]
100
+ fail "Ruby methods doesn't support infinity bounds" if inf_bounds
101
+ integrate_ruby(lower_bound, upper_bound, options, &f)
102
+ elsif GSL_METHODS.include? options[:method]
103
+ integrate_gsl(lower_bound, upper_bound, options, &f)
492
104
  else
493
- raise "Unknown integration method \"#{options[:method]}\""
105
+ fail "Unknown integration method \"#{options[:method]}\""
494
106
  end
495
107
  end
496
108
 
497
- # TODO: Document method
498
- def integrate_gsl(lower_bound,upper_bound,options,&f)
499
-
109
+ # Integrate using the GSL bindings.
110
+ def integrate_gsl(lower_bound, upper_bound, options, &f)
500
111
  f = GSL::Function.alloc(&f)
501
- method=options[:method]
502
- tolerance=options[:tolerance]
503
-
504
- if(method==:qag)
505
- w = GSL::Integration::Workspace.alloc()
506
- if(is_infinite?(lower_bound) and is_infinite?(upper_bound))
507
- #puts "ambos"
508
- val=f.qagi([tolerance,0.0], 1000, w)
509
- elsif is_infinite?(lower_bound)
510
- #puts "inferior #{upper_bound}"
511
- val=f.qagil(upper_bound, [tolerance, 0], w)
512
- elsif is_infinite?(upper_bound)
513
- #puts "superior"
514
- val=f.qagiu(lower_bound, [tolerance, 0], w)
112
+ method = options[:method]
113
+ tolerance = options[:tolerance]
114
+
115
+ if (method == :qag)
116
+ w = GSL::Integration::Workspace.alloc
117
+
118
+ val = if infinite?(lower_bound) && infinite?(upper_bound)
119
+ f.qagi([tolerance, 0.0], 1000, w)
120
+ elsif infinite?(lower_bound)
121
+ f.qagil(upper_bound, [tolerance, 0], w)
122
+ elsif infinite?(upper_bound)
123
+ f.qagiu(lower_bound, [tolerance, 0], w)
515
124
  else
516
-
517
- val=f.qag([lower_bound,upper_bound],[tolerance,0.0], GSL::Integration::GAUSS61, w)
125
+ f.qag([lower_bound, upper_bound], [tolerance, 0.0], GSL::Integration::GAUSS61, w)
518
126
  end
519
- elsif(method==:qng)
520
- val=f.qng([lower_bound, upper_bound], [tolerance, 0.0])
127
+
128
+ elsif (method == :qng)
129
+ val = f.qng([lower_bound, upper_bound], [tolerance, 0.0])
130
+
521
131
  else
522
- raise "Unknown integration method \"#{method}\""
132
+ fail "Unknown integration method \"#{method}\""
523
133
  end
134
+
524
135
  val[0]
525
136
  end
526
137
 
527
- def integrate_ruby(lower_bound,upper_bound,options,&f)
528
- method=options[:method]
529
- tolerance=options[:tolerance]
530
- initial_step=options[:initial_step]
531
- step=options[:step]
138
+ def integrate_ruby(lower_bound, upper_bound, options, &f)
139
+ method = options[:method]
140
+ tolerance = options[:tolerance]
141
+ initial_step = options[:initial_step]
142
+ step = options[:step]
532
143
  points = options[:points]
144
+
533
145
  begin
534
146
  method_obj = Integration.method(method.to_s.downcase)
535
147
  rescue
536
148
  raise "Unknown integration method \"#{method}\""
537
149
  end
538
- current_step=initial_step
539
150
 
540
- if(method==:adaptive_quadrature or method==:romberg or method==:gauss or method== :gauss_kronrod)
541
- if(method==:gauss )
542
- initial_step=10 if initial_step>10
151
+ current_step = initial_step
152
+
153
+ if [:adaptive_quadrature, :romberg, :gauss, :gauss_kronrod].include? method
154
+ if (method == :gauss)
155
+ initial_step = 10 if initial_step > 10
543
156
  tolerance = initial_step
544
157
  method_obj.call(lower_bound, upper_bound, tolerance, &f)
545
- elsif (method==:gauss_kronrod)
546
- initial_step=10 if initial_step>10
547
- tolerance=initial_step
548
- points = points if points != nil
158
+ elsif (method == :gauss_kronrod)
159
+ initial_step = 10 if initial_step > 10
160
+ tolerance = initial_step
161
+ points = points unless points.nil?
549
162
  method_obj.call(lower_bound, upper_bound, tolerance, points, &f)
550
163
  else
551
164
  method_obj.call(lower_bound, upper_bound, tolerance, &f)
552
165
  end
553
166
  else
554
- #puts "iniciando"
555
- value=method_obj.call(lower_bound, upper_bound, current_step, &f)
556
- previous=value+(tolerance*2)
557
- diffs=[]
558
- while((previous-value).abs > tolerance) do
559
- #puts("Valor:#{value}, paso:#{current_step}")
560
- #puts(current_step)
561
- diffs.push((previous-value).abs)
562
- #diffs.push value
563
- current_step+=step
564
- previous=value
565
- #puts "Llamando al metodo"
566
-
567
- value=method_obj.call(lower_bound, upper_bound, current_step, &f)
167
+ value = method_obj.call(lower_bound, upper_bound, current_step, &f)
168
+ previous = value + (tolerance * 2)
169
+ diffs = []
170
+
171
+ while (previous - value).abs > tolerance
172
+ diffs.push((previous - value).abs)
173
+ current_step += step
174
+ previous = value
175
+ value = method_obj.call(lower_bound, upper_bound, current_step, &f)
568
176
  end
569
177
 
570
178
  value
571
179
  end
572
180
  end
573
- end
574
181
 
575
- create_has_library :gsl
182
+ # Check if GSL is available. Require the library if it is present. Return a
183
+ # boolean indicating its availability.
184
+ #
185
+ # @return [Boolean] Whether GSL is available.
186
+ def has_gsl?
187
+ gsl_available = '@@gsl'
188
+ if class_variable_defined? gsl_available
189
+ class_variable_get(gsl_available)
190
+ else
191
+ begin
192
+ require 'gsl'
193
+ class_variable_set(gsl_available, true)
194
+ rescue LoadError
195
+ class_variable_set(gsl_available, false)
196
+ end
197
+ end
198
+ end
199
+ end
576
200
  end