View Javadoc

1   package org.textensor.stochdiff.numeric.stochastic;
2   
3   import org.textensor.report.E;
4   import org.textensor.stochdiff.numeric.BaseCalc.distribution_t;
5   import static org.textensor.stochdiff.numeric.BaseCalc.distribution_t.*;
6   
7   
8   public final class NGoTable {
9   
10      double lnp;
11      int nparticle;
12  
13      double[] cprob; // cumulative probabilities
14      int ncprob;
15  
16      distribution_t mode;
17  
18      public NGoTable(int n, double lnp0, distribution_t mode) {
19          lnp = lnp0;
20          nparticle = n;
21          this.mode = mode;
22  
23          double p = Math.exp(lnp);
24          double q = 1. - p;
25          double lnq = Math.log(q);
26  
27  
28  
29  
30          switch (mode) {
31          case BINOMIAL: {
32              BinomialTable btab = BinomialTable.getTable();
33              /*
34               * accumulate c in reverse order: we add the small quantities first
35               * so they don't get lost in rounding errors
36               * (which is what would happen if we add them to
37               * something of order 1)
38               */
39              double[] wk = new double[n+1];
40  
41              double c = 0.;
42              ncprob = -1;
43              for (int i = n; i >= 0; i--) {
44                  double pn = Math.exp(i * lnp + (n-i) * lnq) * btab.ncm(n, i);
45                  c += pn;
46                  wk[i] = c;
47                  if (ncprob < 0 && c > 1.e-11) {
48                      ncprob = i;
49                  }
50              }
51  
52  
53              /* at this stage, wk[i] contains the probability that
54               * i or more particles move in the step. wk[0] must be 1
55               * unless something has gone wrong.
56               */
57              // RCC, condition was 1.e-14: should check why it doesn't always make that
58              if (Math.abs(1. - c) > 1.e-11) {
59                  E.error("cumulative probability miscount? "+ c +
60                          " for n, p, nkept " + n + " " + p + " " + ncprob);
61              }
62  
63              /*
64               * What we actually want is the cumulative probability of n or fewer
65               * particles moving: cprob[0] is the probabiulioty of 0 moving
66               * cprob[1] is the probability of either 0 or 1 moving etc
67               */
68              cprob = new double[ncprob+1];
69              for (int i = 0; i < ncprob; i++) {
70                  cprob[i] = 1. - wk[i+1];
71              }
72              cprob[ncprob] = 2.;
73              // one extra element on the end to save testing for the end condition;
74  
75          }; break;
76  
77          case POISSON: {
78  
79              double lambda = n * Math.exp(lnp0);
80              double emlambda = Math.exp(-1. * lambda);
81  
82  
83              ncprob = -1;
84              int nmax = 4 * n + 20;  // sure
85              double[] wk = new double[nmax];
86  
87              double lampnbynfac = 1.; // lambda to the power n over n factorial;
88  
89              for (int i = 0; i < nmax; i++) {
90                  double pn = emlambda * lampnbynfac;
91                  lampnbynfac *= (lambda/ (i+1));
92  
93                  wk[i] = pn;
94                  if (i > lambda && ncprob < 0 && pn < 1.e-11) {
95                      ncprob = i;
96                  }
97  
98              }
99  
100             if (ncprob < 0) {
101                 E.error("never terminated tabled? " + n + " " + lambda + " " +  emlambda * lampnbynfac);
102             }
103 
104 
105             /*
106              * What we actually want is the cumulative probability of n or fewer
107              * particles moving: cprob[0] is the probabiulioty of 0 moving
108              * cprob[1] is the probability of either 0 or 1 moving etc
109              */
110             // POSERR - just accumulating forwards here - should still be ok with doubles;
111             cprob = new double[ncprob+1];
112             cprob[0] = wk[0];
113             for (int i = 1; i < ncprob; i++) {
114                 cprob[i] = cprob[i-1] + wk[i];
115             }
116             cprob[ncprob] = 2.;
117 
118         }; break;
119 
120         default:
121 
122             E.warning("unrecognized distribution " + mode);
123         }
124 
125 
126 
127     }
128 
129 
130     /*
131      * Step generation
132      *
133      * There are three methods here - no one method is good for all cases
134      *
135      * First is a simple-minded walk through the table until we overstep the
136      * right box.
137      *
138      * The second is a binary search, which is suboptimal becasue the
139      * cumulative distribution has a very long tail as it approaches 1 contianing
140      * elements that will almost never be used.
141      *
142      * one could switch between the two according to p and the number of
143      * particles, but on the assumption that p will be small if n is large
144      * (otherwise, the timestep should be shorter...) the default is to
145      * use the sequential lookup.
146      *
147      *
148      * TODO The best thing is probaby to precompute an optimal search strategy
149      * given the actual data (and maybe generate code for it)
150      */
151 
152 
153 
154     public int nGoSeq(double r) {
155         int ret = 0;
156         while (cprob[ret] < r) {
157             ret += 1;
158         }
159         return ret;
160     }
161 
162 
163     public int nGoBS(double r) {
164         int bot = 0;
165         int top = ncprob;
166 
167         while (top - bot > 1) {
168             int ic = (bot + top) / 2;
169             if (r < cprob[ic]) {
170                 top = ic;
171             } else {
172                 bot = ic;
173             }
174         }
175         return bot;
176     }
177 
178 
179 
180     public int nGo(double r) {
181         int ret = 0;
182         while (cprob[ret] < r) {
183             ret++;
184         }
185         return ret;
186     }
187 
188 
189     /*
190      * rather than returning just the interval that the random number
191      * lies in, we return a real from within the interval according to
192      * the position of r across its domain.
193      * this contains the nGo information since nGo(r) = (int)(rnGo(r))
194      * and can also be used for interpolating between different
195      * probabilities.
196      */
197 
198 
199     public double rnGo(double r) {
200         double ret = 0.;
201         if (r < cprob[0]) {
202             ret = r / cprob[0];
203 
204         } else {
205             int ia = 1;
206             while (cprob[ia] < r) {
207                 ia++;
208             }
209             ret = ia + (r - cprob[ia-1]) / (cprob[ia] - cprob[ia-1]);
210         }
211 
212         return ret;
213     }
214 
215 
216 
217 
218 
219 
220 
221     /*
222      * The rest is just for testing that the results are sensible
223      */
224 
225     public void print() {
226         StringBuffer sb = new StringBuffer();
227         sb.append("n=" + nparticle + " p="+ Math.exp(lnp) +
228                   " njmax=" + ncprob + " mode=" + mode + "\n");
229         sb.append("p(n < i): ");
230         for (int i = 0; i < ncprob; i++) {
231             sb.append(cprob[i] + ", ");
232         }
233         sb.append("\n");
234 
235         sb.append("ngo, rngo for ten equally spaced randoms starting at 0.001 ");
236         for (int i = 0; i < 10; i++) {
237             double r = 0.001 + 0.99 * (i / 9.);
238             sb.append("(" + nGo(r) + ",  " + rnGo(r) + ") ");
239         }
240         sb.append("\n");
241         System.out.println(sb.toString());
242     }
243 
244 
245 
246 
247 
248     private void lookupCheck() {
249         for (int i = 0; i <= 50; i++) {
250             double r = (1. * i) / 50.;
251             int n1 = nGoSeq(r);
252             int n2 = nGoBS(r);
253             if (n1 != n2) {
254                 E.error("different results in lookup check " + nparticle + " " +
255                         Math.exp(lnp) + " " + n1 + " " + n2);
256             }
257         }
258     }
259 
260 
261 
262 
263     private long lookupTimeSeq() {
264         long t0 = System.currentTimeMillis();
265         double rnx = 1.e7;
266         int njt = 0;
267         for (int i = 0; i < rnx; i++) {
268             njt += nGoSeq(i / rnx);
269         }
270         long t1 = System.currentTimeMillis();
271         return (t1 - t0);
272     }
273 
274     private long lookupTimeBS() {
275         long t0 = System.currentTimeMillis();
276         double rnx = 1.e7;
277         int njt = 0;
278         for (int i = 0; i < rnx; i++) {
279             njt += nGoBS(i / rnx);
280         }
281         long t1 = System.currentTimeMillis();
282         return (t1 - t0);
283     }
284 
285     private long lookupTimeSwitch() {
286         long t0 = System.currentTimeMillis();
287         double rnx = 1.e7;
288         int njt = 0;
289         for (int i = 0; i < rnx; i++) {
290             njt += nGo(i / rnx);
291         }
292         long t1 = System.currentTimeMillis();
293         return (t1 - t0);
294     }
295 
296 
297 
298     private static void lookupTest() {
299         long tstot = 0;
300         long tbtot = 0;
301         long tctot = 0;
302 
303         distribution_t m = BINOMIAL;
304 
305         for (int i = 10; i <= 90; i+= 10) {
306             for (int ip = -6; ip < -1; ip++) {
307                 double lp = 2. * ip;
308                 NGoTable jc = new NGoTable(i, lp, m);
309 
310                 jc.lookupCheck();
311 
312                 long ts = jc.lookupTimeSeq();
313                 long tb = jc.lookupTimeBS();
314                 long tc = jc.lookupTimeSwitch();
315 
316                 tstot += ts;
317                 tbtot += tb;
318                 tctot += tc;
319                 double p = Math.exp(lp);
320                 System.out.println("timings " + i + " " + p + " " +
321                                    ts + " " + tb + " " + tc);
322 
323             }
324         }
325 
326         E.info("seq " + tstot + "   bs " + tbtot + " " + tctot);
327     }
328 
329 
330     private static void dumpExamples() {
331         new NGoTable(10, Math.log(0.1), BINOMIAL).print();
332         new NGoTable(10, Math.log(0.1), POISSON).print();
333 
334         new NGoTable(90, Math.log(0.1), BINOMIAL).print();
335         new NGoTable(90, Math.log(0.1), POISSON).print();
336 
337         new NGoTable(10, Math.log(0.01), BINOMIAL).print();
338         new NGoTable(10, Math.log(0.01), POISSON).print();
339 
340         new NGoTable(90, Math.log(0.01), BINOMIAL).print();
341         new NGoTable(90, Math.log(0.01), POISSON).print();
342 
343         new NGoTable(10, Math.log(0.001), BINOMIAL).print();
344         new NGoTable(10, Math.log(0.001), POISSON).print();
345 
346         new NGoTable(90, Math.log(0.001), BINOMIAL).print();
347         new NGoTable(90, Math.log(0.001), POISSON).print();
348 
349         new NGoTable(10, Math.log(0.0001), BINOMIAL).print();
350         new NGoTable(10, Math.log(0.0001), POISSON).print();
351 
352         new NGoTable(90, Math.log(0.0001), BINOMIAL).print();
353         new NGoTable(90, Math.log(0.0001), POISSON).print();
354     }
355 
356 
357     public static void main(String[] argv) {
358         dumpExamples();
359         //   lookupTest();
360     }
361 
362 
363 }