1 module rmathd.nbinomial; 2 3 public import rmathd.common; 4 public import rmathd.beta; 5 public import rmathd.poisson; 6 public import rmathd.toms708; 7 public import rmathd.normal; 8 public import rmathd.gamma; 9 10 /* 11 ** 12 ** dmd nbinomial.d common.d beta.d poisson.d toms708 normal.d gamma.d && ./nbinomial 13 */ 14 15 16 T dnbinom(T: double)(T x, T size, T prob, int give_log) 17 { 18 T ans, p; 19 mixin R_D__0!give_log; 20 mixin R_D__1!give_log; 21 22 if (isNaN(x) || isNaN(size) || isNaN(prob)) 23 return x + size + prob; 24 25 if (prob <= 0 || prob > 1 || size < 0) 26 return T.nan; 27 mixin (R_D_nonint_check!(x)); 28 if (x < 0 || !isFinite(x)) 29 return R_D__0; 30 /* limiting case as size approaches zero is point mass at zero */ 31 if (x == 0 && size == 0) 32 return R_D__1; 33 x = nearbyint(x); 34 if(!isFinite(size)) size = DBL_MAX; 35 36 ans = dbinom_raw!T(size, x + size, prob, 1 - prob, give_log); 37 p = (cast(T)size)/(size + x); 38 return((give_log) ? log(p) + ans : p * ans); 39 } 40 41 42 T dnbinom_mu(T: double)(T x, T size, T mu, int give_log) 43 { 44 /* originally, just set prob := size / (size + mu) and called dbinom_raw(), 45 * but that suffers from cancellation when mu << size */ 46 47 mixin R_D__0!give_log; 48 mixin R_D__1!give_log; 49 50 if (isNaN(x) || isNaN(size) || isNaN(mu)) 51 return x + size + mu; 52 53 if (mu < 0 || size < 0) 54 return T.nan; 55 mixin (R_D_nonint_check!(x)); 56 if (x < 0 || !isFinite(x)) 57 return R_D__0; 58 59 /* limiting case as size approaches zero is point mass at zero, 60 * even if mu is kept constant. limit distribution does not 61 * have mean mu, though. 62 */ 63 if (x == 0 && size == 0) 64 return R_D__1; 65 x = nearbyint(x); 66 if(!isFinite(size)) // limit case: Poisson 67 return(dpois_raw!T(x, mu, give_log)); 68 69 if(x == 0)/* be accurate, both for n << mu, and n >> mu :*/ 70 return R_D_exp!T(size * (size < mu ? log(size/(size + mu)) : log1p(-mu/(size + mu))), give_log); 71 if(x < 1e-10 * size) { /* don't use dbinom_raw() but MM's formula: */ 72 /* FIXME --- 1e-8 shows problem; rather use algdiv() from ./toms708.c */ 73 T p = (size < mu ? log(size/(1 + size/mu)) : log(mu / (1 + mu/size))); 74 return R_D_exp!T(x * p - mu - lgamma!T(x + 1) + 75 log1p(x*(x - 1)/(2*size)), give_log); 76 } else { 77 /* no unnecessary cancellation inside dbinom_raw, when 78 * x_ = size and n_ = x+size are so close that n_ - x_ loses accuracy */ 79 T p = (cast(T)size)/(size + x), 80 ans = dbinom_raw!T(size, x + size, size/(size + mu), mu/(size + mu), give_log); 81 return((give_log) ? log(p) + ans : p * ans); 82 } 83 } 84 85 86 T pnbinom(T: double)(T x, T size, T prob, int lower_tail, int log_p) 87 { 88 if (isNaN(x) || isNaN(size) || isNaN(prob)) 89 return x + size + prob; 90 if(!isFinite(size) || !isFinite(prob)) 91 return T.nan; 92 93 if (size < 0 || prob <= 0 || prob > 1) 94 return T.nan; 95 96 /* limiting case: point mass at zero */ 97 if (size == 0) 98 return (x >= 0) ? R_DT_1!T(lower_tail, log_p): R_DT_0!T(lower_tail, log_p); 99 100 if (x < 0) 101 return R_DT_0!T(lower_tail, log_p); 102 if (!isFinite(x)) 103 return R_DT_1!T(lower_tail, log_p); 104 x = floor(x + 1e-7); 105 return pbeta!T(prob, size, x + 1, lower_tail, log_p); 106 } 107 108 109 T pnbinom_mu(T: double)(T x, T size, T mu, int lower_tail, int log_p) 110 { 111 if (isNaN(x) || isNaN(size) || isNaN(mu)) 112 return x + size + mu; 113 if(!isFinite(mu)) 114 return T.nan; 115 116 if (size < 0 || mu < 0) 117 return T.nan; 118 119 /* limiting case: point mass at zero */ 120 if (size == 0) 121 return (x >= 0) ? R_DT_1!T(lower_tail, log_p) : R_DT_0!T(lower_tail, log_p); 122 123 if (x < 0) 124 return R_DT_0!T(lower_tail, log_p); 125 if (!isFinite(x)) 126 return R_DT_1!T(lower_tail, log_p); 127 if (!isFinite(size)) // limit case: Poisson 128 return(ppois!T(x, mu, lower_tail, log_p)); 129 130 x = floor(x + 1e-7); 131 /* return 132 * pbeta(pr, size, x + 1, lower_tail, log_p); pr = size/(size + mu), 1-pr = mu/(size+mu) 133 * 134 *= pbeta_raw(pr, size, x + 1, lower_tail, log_p) 135 * x. pin qin 136 *= bratio (pin, qin, x., 1-x., &w, &wc, &ierr, log_p), and return w or wc .. 137 *= bratio (size, x+1, pr, 1-pr, &w, &wc, &ierr, log_p) */ 138 { 139 int ierr; 140 T w, wc; 141 bratio!T(size, x + 1, size/(size + mu), mu/(size + mu), &w, &wc, &ierr, log_p); 142 if(ierr){ 143 //MATHLIB_WARNING(_("pnbinom_mu() -> bratio() gave error code %d"), ierr); 144 doNothing(); 145 } 146 return lower_tail ? w : wc; 147 } 148 } 149 150 151 static T do_search(T)(T y, T *z, T p, T n, T pr, T incr) 152 { 153 if(*z >= p) { /* search to the left */ 154 for(;;) { 155 if(y == 0 || (*z = pnbinom!T(y - incr, n, pr, /*l._t.*/1, /*log_p*/0)) < p) 156 return y; 157 y = fmax2!T(0, y - incr); 158 } 159 } else { /* search to the right */ 160 for(;;) { 161 y = y + incr; 162 if((*z = pnbinom!T(y, n, pr, /*l._t.*/1, /*log_p*/0)) >= p) 163 return y; 164 } 165 } 166 } 167 168 169 T qnbinom(T: double)(T p, T size, T prob, int lower_tail, int log_p) 170 { 171 T P, Q, mu, sigma, gamma, z, y; 172 173 if (isNaN(p) || isNaN(size) || isNaN(prob)) 174 return p + size + prob; 175 176 /* this happens if specified via mu, size, since 177 prob == size/(size+mu) 178 */ 179 if (prob == 0 && size == 0) 180 return 0; 181 182 if (prob <= 0 || prob > 1 || size < 0) 183 return T.nan; 184 185 if (prob == 1 || size == 0) 186 return 0; 187 188 immutable(T) INF = T.infinity; 189 mixin (R_Q_P01_boundaries!(p, 0, INF)); 190 191 Q = 1.0 / prob; 192 P = (1.0 - prob) * Q; 193 mu = size * P; 194 sigma = sqrt(size * P * Q); 195 gamma = (Q + P)/sigma; 196 197 mixin R_DT_qIv!p; 198 /* Note : "same" code in qpois.c, qbinom.c, qnbinom.c -- 199 * FIXME: This is far from optimal [cancellation for p ~= 1, etc]: */ 200 if(!lower_tail || log_p) { 201 p = R_DT_qIv; /* need check again (cancellation!): */ 202 if (p == R_DT_0!T(lower_tail, log_p)) 203 return 0; 204 if (p == R_DT_1!T(lower_tail, log_p)) 205 return T.infinity; 206 } 207 /* temporary hack --- FIXME --- */ 208 if (p + 1.01*DBL_EPSILON >= 1.) 209 return T.infinity; 210 211 /* y := approx.value (Cornish-Fisher expansion) : */ 212 z = qnorm!T(p, 0., 1., /*lower_tail*/1, /*log_p*/0); 213 y = nearbyint(mu + sigma * (z + gamma * (z*z - 1) / 6)); 214 215 z = pnbinom!T(y, size, prob, /*lower_tail*/1, /*log_p*/0); 216 217 /* fuzz to ensure left continuity: */ 218 p *= 1 - 64*DBL_EPSILON; 219 220 /* If the C-F value is not too large a simple search is OK */ 221 if(y < 1e5) 222 return do_search!T(y, &z, p, size, prob, 1); 223 /* Otherwise be a bit cleverer in the search */ 224 { 225 T incr = floor(y * 0.001), oldincr; 226 do { 227 oldincr = incr; 228 y = do_search!T(y, &z, p, size, prob, incr); 229 incr = fmax2!T(1, floor(incr/100)); 230 } while(oldincr > 1 && incr > y*1e-15); 231 return y; 232 } 233 } 234 235 T qnbinom_mu(T: double)(T p, T size, T mu, int lower_tail, int log_p) 236 { 237 if (size == T.infinity) // limit case: Poisson 238 return(qpois!T(p, mu, lower_tail, log_p)); 239 /* FIXME! Implement properly!! (not losing accuracy for very large size (prob ~= 1)*/ 240 return qnbinom!T(p, size, /* prob = */ size/(size + mu), lower_tail, log_p); 241 } 242 243 244 T rnbinom(T: double)(T size, T prob) 245 { 246 if(!isFinite(prob) || isNaN(size) || size <= 0 || prob <= 0 || prob > 1) 247 /* prob = 1 is ok, PR#1218 */ 248 return T.nan; 249 if(!isFinite(size)) size = DBL_MAX / 2.; // '/2' to prevent rgamma() returning Inf 250 return (prob == 1) ? 0 : rpois(rgamma!T(size, (1 - prob) / prob)); 251 } 252 253 T rnbinom_mu(T: double)(T size, T mu) 254 { 255 if(!isFinite(mu) || isNaN(size) || size <= 0 || mu < 0) 256 return T.nan; 257 if(!isFinite(size)) size = DBL_MAX / 2.; 258 return (mu == 0) ? 0 : rpois!T(rgamma!T(size, mu / size)); 259 } 260 261 262 void test_nbinomial() 263 { 264 import std.stdio: writeln; 265 writeln("dnbinom: ", dnbinom(2., 8., 0.7, 0)); 266 writeln("dnbinom_mu: ", dnbinom_mu(2., 8., 0.7, 0)); 267 writeln("pnbinom: ", pnbinom(2., 8., 0.7, 1, 0)); 268 writeln("pnbinom_mu: ", pnbinom_mu(2., 8., 0.7, 1, 0)); 269 writeln("qnbinom: ", qnbinom(.8, 8., 0.7, 1, 0)); 270 writeln("qnbinom_mu: ", qnbinom_mu(.8, 8., 0.7, 1, 0)); 271 writeln("rnbinom: ", rnbinom(8., 0.2), ", rnbinom: ", rnbinom(8., 0.2), ", rnbinom: ", rnbinom(8., 0.2)); 272 writeln("rnbinom_mu: ", rnbinom_mu(8., 0.7), ", rnbinom_mu: ", rnbinom_mu(8., 0.7), ", rnbinom_mu: ", rnbinom_mu(8., 0.7)); 273 } 274