4
4
5
5
! Thanks to Chris Riedel who developed the methods in this module.
6
6
7
+ ! This module only supports standard beta distributions for which the
8
+ ! lower bound is 0 and the upper bound is 1.
9
+
7
10
module beta_distribution_mod
8
11
9
12
use types_mod, only : r8 , PI, missing_r8
@@ -12,7 +15,7 @@ module beta_distribution_mod
12
15
13
16
use random_seq_mod, only : random_seq_type, random_uniform
14
17
15
- use distribution_params_mod, only : distribution_params_type
18
+ use distribution_params_mod, only : distribution_params_type, BETA_DISTRIBUTION
16
19
17
20
use normal_distribution_mod, only : inv_cdf
18
21
@@ -36,11 +39,11 @@ subroutine test_beta
36
39
37
40
! This routine provides limited tests of the numerics in this module. It begins
38
41
! by comparing a handful of cases of the pdf and cdf to results from Matlab. It
39
- ! then tests the quality of the inverse cdf for a single shape/scale pair. Failing
42
+ ! then tests the quality of the inverse cdf for a single alpha/beta pair. Failing
40
43
! these tests suggests a serious problem. Passing them does not indicate that
41
44
! there are acceptable results for all possible inputs.
42
45
43
- real (r8 ) :: x, y, p, inv
46
+ real (r8 ) :: x, y, inv
44
47
real (r8 ) :: alpha, beta, max_diff
45
48
integer :: i
46
49
@@ -61,41 +64,37 @@ subroutine test_beta
61
64
! Compare to matlab
62
65
write (* , * ) ' Absolute value of differences should be less than 1e-15'
63
66
do i = 1 , 7
64
- pdf_diff(i) = beta_pdf(mx(i), malpha(i), mbeta(i)) - mpdf(i)
65
- cdf_diff(i) = beta_cdf(mx(i), malpha(i), mbeta(i), 0.0_r8 , 1.0_r8 ) - mcdf(i)
67
+ pdf_diff(i) = abs ( beta_pdf(mx(i), malpha(i), mbeta(i)) - mpdf(i) )
68
+ cdf_diff(i) = abs ( beta_cdf(mx(i), malpha(i), mbeta(i)) - mcdf(i) )
66
69
write (* , * ) i, pdf_diff(i), cdf_diff(i)
67
70
end do
68
- if (abs ( maxval (pdf_diff)) < 1e-15_r8 .and. abs ( maxval (cdf_diff) ) < 1e-15_r8 ) then
71
+ if (maxval (pdf_diff) < 1e-15_r8 .and. maxval (cdf_diff) < 1e-15_r8 ) then
69
72
write (* , * ) ' Matlab Comparison Tests: PASS'
70
73
else
71
74
write (* , * ) ' Matlab Comparison Tests: FAIL'
72
75
endif
73
76
74
-
75
77
! Test many x values for cdf and inverse cdf for a single set of alpha and beta
76
78
alpha = 5.0_r8
77
79
beta = 2.0_r8
78
80
79
81
max_diff = - 1.0_r8
80
82
do i = 0 , 1000
81
83
x = i / 1000.0_r8
82
- p = beta_pdf(x, alpha, beta)
83
- y = beta_cdf(x, alpha, beta, 0.0_r8 , 1.0_r8 )
84
- inv = inv_beta_cdf(y, alpha, beta, 0.0_r8 , 1.0_r8 )
84
+ y = beta_cdf(x, alpha, beta)
85
+ inv = inv_beta_cdf(y, alpha, beta)
85
86
max_diff = max (abs (x - inv), max_diff)
86
87
end do
87
88
88
89
write (* , * ) ' ----------------------------'
89
90
write (* , * ) ' max difference in inversion is ' , max_diff
90
91
write (* , * ) ' max difference should be less than 1e-14'
91
-
92
92
if (max_diff < 1e-14_r8 ) then
93
93
write (* , * ) ' Inversion Tests: PASS'
94
94
else
95
95
write (* , * ) ' Inversion Tests: FAIL'
96
96
endif
97
97
98
-
99
98
end subroutine test_beta
100
99
101
100
!- ----------------------------------------------------------------------
@@ -106,20 +105,25 @@ function inv_beta_cdf_params(quantile, p) result(x)
106
105
real (r8 ), intent (in ) :: quantile
107
106
type (distribution_params_type), intent (in ) :: p
108
107
108
+ ! Only standard beta is currently supported. Fail if bounds are not 0 and 1
109
+ if (p% lower_bound /= 0.0_r8 .or. p% upper_bound /= 1.0_r8 ) then
110
+ errstring = ' Only standard beta distribution with bounds of 0 and 1 is supported'
111
+ call error_handler(E_ERR, ' inv_beta_cdf_params' , errstring, source)
112
+ endif
113
+
109
114
x = inv_cdf(quantile, beta_cdf_params, inv_beta_first_guess_params, p)
110
115
111
116
end function inv_beta_cdf_params
112
117
113
118
!- ----------------------------------------------------------------------
114
119
115
- function inv_beta_cdf (quantile , alpha , beta , lower_bound , upper_bound ) result(x)
120
+ function inv_beta_cdf (quantile , alpha , beta ) result(x)
116
121
117
122
real (r8 ) :: x
118
123
real (r8 ), intent (in ) :: quantile
119
124
real (r8 ), intent (in ) :: alpha, beta
120
- real (r8 ), intent (in ) :: lower_bound, upper_bound
121
125
122
- ! Given a quantile, finds the value of x for which the scaled beta cdf
126
+ ! Given a quantile, finds the value of x for which the beta cdf
123
127
! with alpha and beta has approximately this quantile
124
128
125
129
type (distribution_params_type) :: p
@@ -129,15 +133,13 @@ function inv_beta_cdf(quantile, alpha, beta, lower_bound, upper_bound) result(x)
129
133
call error_handler(E_ERR, ' inv_beta_cdf' , errstring, source)
130
134
endif
131
135
136
+ ! Load the p type for the generic cdf calls
132
137
p% params(1 ) = alpha; p% params(2 ) = beta
133
- ! Beta must be bounded on both sides
134
- p% lower_bound = lower_bound ; p% upper_bound = upper_bound
138
+ p % bounded_below = .true. ; p % bounded_above = .true.
139
+ p% lower_bound = 0.0_r8 ; p% upper_bound = 1.0_r8
135
140
136
141
x = inv_beta_cdf_params(quantile, p)
137
142
138
- ! Undo the scaling
139
- x = x * (upper_bound - lower_bound) + lower_bound
140
-
141
143
end function inv_beta_cdf
142
144
143
145
!- --------------------------------------------------------------------------
@@ -184,25 +186,34 @@ function beta_cdf_params(x, p)
184
186
real (r8 ), intent (in ) :: x
185
187
type (distribution_params_type), intent (in ) :: p
186
188
189
+ ! A translation routine that is required to use the generic cdf optimization routine
190
+ ! Extracts the appropriate information from the distribution_params_type that is needed
191
+ ! for a call to the function beta_cdf below.
192
+
187
193
real (r8 ) :: alpha, beta
188
194
195
+ ! Only standard beta is currently supported. Fail if bounds are not 0 and 1
196
+ if (p% lower_bound /= 0.0_r8 .or. p% upper_bound /= 1.0_r8 ) then
197
+ errstring = ' Only standard beta distribution with bounds of 0 and 1 is supported'
198
+ call error_handler(E_ERR, ' beta_cdf_params' , errstring, source)
199
+ endif
200
+
189
201
alpha = p% params(1 ); beta = p% params(2 )
190
- beta_cdf_params = beta_cdf(x, alpha, beta, p % lower_bound, p % upper_bound )
202
+ beta_cdf_params = beta_cdf(x, alpha, beta)
191
203
192
204
end function beta_cdf_params
193
205
194
206
!- --------------------------------------------------------------------------
195
207
196
- function beta_cdf (x , alpha , beta , lower_bound , upper_bound )
208
+ function beta_cdf (x , alpha , beta )
197
209
198
210
! Returns the cumulative distribution of a beta function with alpha and beta
199
211
! at the value x
200
212
201
- ! Returns a large negative value if called with illegal values
213
+ ! Returns a failed_value if called with illegal values
202
214
203
215
real (r8 ) :: beta_cdf
204
216
real (r8 ), intent (in ) :: x, alpha, beta
205
- real (r8 ), intent (in ) :: lower_bound, upper_bound
206
217
207
218
! Parameters must be positive
208
219
if (alpha <= 0.0_r8 .or. beta <= 0.0_r8 ) then
@@ -251,7 +262,7 @@ function random_beta(r, alpha, beta)
251
262
! Draw from U(0, 1) to get a quantile
252
263
quantile = random_uniform(r)
253
264
! Invert cdf to get a draw from beta
254
- random_beta = inv_beta_cdf(quantile, alpha, beta, 0.0_r8 , 1.0_r8 )
265
+ random_beta = inv_beta_cdf(quantile, alpha, beta)
255
266
256
267
end function random_beta
257
268
@@ -339,24 +350,25 @@ function inv_beta_first_guess_params(quantile, p)
339
350
real (r8 ), intent (in ) :: quantile
340
351
type (distribution_params_type), intent (in ) :: p
341
352
353
+ ! A translation routine that is required to use the generic first_guess for
354
+ ! the cdf optimization routine.
355
+ ! Extracts the appropriate information from the distribution_params_type that is needed
356
+ ! for a call to the function inv_beta_first_guess below.
357
+
342
358
real (r8 ) :: alpha, beta
343
359
344
360
alpha = p% params(1 ); beta = p% params(2 )
345
- inv_beta_first_guess_params = inv_beta_first_guess(quantile, alpha, beta, &
346
- p% bounded_below, p% bounded_above, p% lower_bound, p% upper_bound)
361
+ inv_beta_first_guess_params = inv_beta_first_guess(quantile, alpha, beta)
347
362
348
363
end function inv_beta_first_guess_params
349
364
350
365
!- --------------------------------------------------------------------------
351
366
352
- function inv_beta_first_guess (x , alpha , beta , &
353
- bounded_below , bounded_above , lower_bound , upper_bound )
367
+ function inv_beta_first_guess (x , alpha , beta )
354
368
355
369
real (r8 ) :: inv_beta_first_guess
356
370
real (r8 ), intent (in ) :: x
357
371
real (r8 ), intent (in ) :: alpha, beta
358
- logical , intent (in ) :: bounded_below, bounded_above
359
- real (r8 ), intent (in ) :: lower_bound, upper_bound
360
372
361
373
! Need some sort of first guess, should be smarter here
362
374
! For starters, take the mean for this alpha and beta
@@ -389,19 +401,22 @@ end subroutine beta_alpha_beta
389
401
390
402
!- --------------------------------------------------------------------------
391
403
392
- subroutine set_beta_params_from_ens (ens , num , lower_bound , upper_bound , p )
404
+ subroutine set_beta_params_from_ens (ens , num , p )
393
405
394
- integer , intent (in ) :: num
395
- real (r8 ), intent (in ) :: ens(num)
396
- real (r8 ), intent (in ) :: lower_bound, upper_bound
397
- type (distribution_params_type), intent (inout ) :: p
406
+ integer , intent (in ) :: num
407
+ real (r8 ), intent (in ) :: ens(num)
408
+ type (distribution_params_type), intent (out ) :: p
398
409
399
410
real (r8 ) :: alpha, beta
400
411
412
+ ! Set up the description of the beta distribution defined by the ensemble
413
+ p% distribution_type = BETA_DISTRIBUTION
414
+
401
415
! Set the bounds info
402
- p% lower_bound = lower_bound; p% upper_bound = upper_bound
416
+ p% bounded_below = .true. ; p% bounded_above = .true.
417
+ p% lower_bound = 0.0_r8 ; p% upper_bound = 1.0_r8
403
418
404
- ! Get alpha and beta for the scaled ensemble
419
+ ! Get alpha and beta for the ensemble
405
420
call beta_alpha_beta(ens, num, alpha, beta)
406
421
p% params(1 ) = alpha
407
422
p% params(2 ) = beta
0 commit comments