EGSnrc C++ class library  Report PIRS-898 (2021)
Iwan Kawrakow, Ernesto Mainegra-Hing, Frederic Tessier, Reid Townson and Blake Walters
egs_alias_table.cpp
Go to the documentation of this file.
1 /*
2 ###############################################################################
3 #
4 # EGSnrc egs++ alias table
5 # Copyright (C) 2015 National Research Council Canada
6 #
7 # This file is part of EGSnrc.
8 #
9 # EGSnrc is free software: you can redistribute it and/or modify it under
10 # the terms of the GNU Affero General Public License as published by the
11 # Free Software Foundation, either version 3 of the License, or (at your
12 # option) any later version.
13 #
14 # EGSnrc is distributed in the hope that it will be useful, but WITHOUT ANY
15 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
17 # more details.
18 #
19 # You should have received a copy of the GNU Affero General Public License
20 # along with EGSnrc. If not, see <http://www.gnu.org/licenses/>.
21 #
22 ###############################################################################
23 #
24 # Author: Iwan Kawrakow, 2005
25 #
26 # Contributors: Ernesto Mainegra-Hing
27 # Frederic Tessier
28 # Blake Walters
29 # Reid Townson
30 #
31 ###############################################################################
32 */
33 
34 
40 #include "egs_alias_table.h"
41 #include "egs_functions.h"
42 
43 void EGS_AliasTable::clear() {
44  if (n > 0) {
45  delete [] fi;
46  delete [] xi;
47  delete [] wi;
48  delete [] bin;
49  n = 0;
50  }
51 }
52 
53 void EGS_AliasTable::allocate(int N, int Type) {
54  clear();
55  n = N;
56  type = Type;
57  xi = new EGS_Float [n];
58  if (type == 0) {
59  np = n;
60  fi = new EGS_Float [n];
61  wi = new EGS_Float [n];
62  bin = new int [n];
63  }
64  else {
65  np = n-1;
66  wi = new EGS_Float [n-1];
67  bin = new int [n-1];
68  if (type == 1) {
69  fi = new EGS_Float [n-1];
70  }
71  else {
72  fi = new EGS_Float [n];
73  }
74  }
75 }
76 
77 
78 void EGS_AliasTable::copy(const EGS_AliasTable &t) {
79  clear();
80  if (t.n > 0) {
81  allocate(t.n,t.type);
82  for (int j=0; j<np; j++) {
83  xi[j] = t.xi[j];
84  fi[j] = t.fi[j];
85  wi[j] = t.wi[j];
86  bin[j] = t.bin[j];
87  }
88  if (type) {
89  xi[np] = t.xi[np];
90  if (type == 2 || type == 3) {
91  fi[np] = t.fi[np];
92  }
93  }
94  }
95 }
96 
97 /**************************************************
98  Initializes alias-table:
99  N = number of abscissa points
100 **************************************************/
101 void EGS_AliasTable::initialize(int N, const EGS_Float *x,
102  const EGS_Float *f, int Type) {
103  allocate(N,Type);
104  for (int i=0; i<np; i++) {
105  xi[i] = x[i];
106  fi[i] = f[i];
107  }
108  if (Type) {
109  xi[np] = x[np];
110  }
111  if (Type == 2 || Type == 3) {
112  fi[np] = f[np];
113  }
114  make();
115 }
116 
117 void EGS_AliasTable::make() {
118  EGS_Float *fcum = new EGS_Float[np];
119  bool *not_done = new bool[np];
120  EGS_Float sum = 0, sum1 = 0;
121  int i;
122  for (i=0; i<np; i++) {
123  if (type == 0) {
124  fcum[i] = fi[i];
125  }
126  else if (type == 1) {
127  fcum[i] = fi[i]*(xi[i+1]-xi[i]);
128  }
129  else {
130  fcum[i] = 0.5*(fi[i]+fi[i+1])*(xi[i+1]-xi[i]);
131  }
132  sum += fcum[i];
133  wi[i] = 1;
134  bin[i] = 0;
135  not_done[i] = true;
136  if (type == 0) {
137  sum1 += fcum[i]*xi[i];
138  }
139  else if (type == 1) {
140  sum1 += 0.5*fcum[i]*(xi[i+1]+xi[i]);
141  }
142  else sum1 += fcum[i]*(fi[i]*(2*xi[i]+xi[i+1])+
143  fi[i+1]*(xi[i]+2*xi[i+1]))/(3*(fi[i]+fi[i+1]));
144  }
145  average = sum1/sum;
146 
147  for (i=0; i<np; i++) {
148  fi[i] /= sum;
149  }
150  if (type == 2 || type == 3) {
151  fi[np] /= sum;
152  }
153  sum /= np;
154 
155  int jh, jl;
156  for (i=0; i<np-1; i++) {
157 
158  // find the next "high" bin (above average)
159  int high_bin = -1;
160  for (jh=0; jh<np; jh++) {
161  if (not_done[jh] && fcum[jh] > sum) {
162  high_bin = jh;
163  break;
164  }
165  }
166  if (high_bin < 0) {
167  break;
168  }
169 
170  // find the next "low" bin (below average)
171  int low_bin = -1;
172  for (jl=0; jl<np; jl++) {
173  if (not_done[jl] && fcum[jl] < sum) {
174  low_bin = jl;
175  break;
176  }
177  }
178  if (low_bin < 0) {
179  egsWarning("EGS_AliasTable::make(): found a high bin, but no low bin; this is abnormal.");
180  break;
181  }
182 
183  // alias the high bin from the low bin
184  EGS_Float aux = sum - fcum[low_bin];
185  fcum[high_bin] -= aux;
186  not_done[jl] = false;
187  wi[low_bin] = fcum[low_bin]/sum;
188  bin[low_bin] = high_bin;
189  }
190  delete [] fcum;
191  delete [] not_done;
192 }
193 
194 #define AT_NCHECK 3
195 
196 int EGS_AliasTable::initialize(EGS_Float xmin, EGS_Float xmax, EGS_Float accu,
197  int nmax, EGS_AtFunction f, void *data) {
198  allocate(2,2);
199  xi[0] = xmin;
200  xi[1] = xmax;
201  fi[0] = f(xi[0],data);
202  fi[1] = f(xi[1],data);
203  EGS_Float *xtemp, *ftemp;
204  int error = 0;
205  for (EGS_I64 loopCount=0; loopCount<=loopMax; ++loopCount) {
206  if (loopCount == loopMax) {
207  egsFatal("EGS_AliasTable::initialize: Too many iterations were required! Input may be invalid, or consider increasing loopMax.");
208  return 1;
209  }
210  int nnn = (n-1)*AT_NCHECK + n;
211  xtemp = new EGS_Float[nnn];
212  ftemp = new EGS_Float[nnn];
213  if (!xtemp || !ftemp) egsFatal("EGS_AliasTable::initialize: "
214  "not enough memory!\n");
215  bool is_ok = true;
216  int i,j=0;
217  for (i=0; i<n-1; i++) {
218  xtemp[j] = xi[i];
219  ftemp[j] = fi[i];
220  EGS_Float dx = (xi[i+1]-xi[i])/(AT_NCHECK+1);
221  for (int l=0; l<AT_NCHECK; l++) {
222  EGS_Float x = xi[i]+dx*(l+1);
223  EGS_Float fe = f(x,data);
224  EGS_Float fa = fi[i]+(fi[i+1]-fi[i])/(xi[i+1]-xi[i])*(x-xi[i]);
225  EGS_Float test = fabs(fa/fe-1);
226  if (test > accu) {
227  is_ok = false;
228  xtemp[++j] = x;
229  ftemp[j] = fe;
230  }
231  }
232  j++;
233  }
234  if (is_ok) {
235  break;
236  }
237  xtemp[j] = xi[n-1];
238  ftemp[j++] = fi[n-1];
239  allocate(j,2);
240  for (i=0; i<j; i++) {
241  xi[i] = xtemp[i];
242  fi[i] = ftemp[i];
243  }
244  if (n >= nmax) {
245  error = 1;
246  break;
247  }
248  delete [] xtemp;
249  delete [] ftemp;
250  }
251  delete [] xtemp;
252  delete [] ftemp;
253  make();
254  return error;
255 }
256 
258  EGS_Float r1 = rndm->getUniform();
259  EGS_Float aj = r1*np;
260  int j = (int) aj;
261  aj -= j;
262  if (aj > wi[j]) {
263  j = bin[j];
264  }
265  return j;
266 }
267 
269  EGS_Float r1 = rndm->getUniform();
270  EGS_Float aj = r1*np;
271  int j = (int) aj;
272  aj -= j;
273  if (aj > wi[j]) {
274  j = bin[j];
275  }
276  if (!type) {
277  return xi[j];
278  }
279  EGS_Float x = xi[j];
280  EGS_Float dx = xi[j+1] - x;
281  EGS_Float r2 = rndm->getUniform();
282  if (type == 1) {
283  return x + dx*r2;
284  }
285  EGS_Float res;
286  if (fi[j] > 0) {
287  EGS_Float a = fi[j+1]/fi[j]-1;
288  if (fabs(a) < 0.2) {
289  EGS_Float rnno1 = 0.5*(1-r2)*a;
290  res = x + r2*dx*(1+rnno1*(1-r2*a));
291  }
292  else {
293  res = x - dx/a*(1-sqrt(1+r2*a*(2+a)));
294  }
295  }
296  else {
297  res = x + dx*sqrt(r2);
298  }
299  return res;
300 }
301 
302 
303 EGS_SimpleAliasTable::EGS_SimpleAliasTable(int N, const EGS_Float *f) : n(0) {
304 
305  if (N < 1) {
306  return;
307  }
308 
309  // initialize data members
310  n = N;
311  wi = new EGS_Float [n];
312  bins = new int [n];
313 
314  // local variables
315  int i;
316  double sum = 0;
317  double *p = new EGS_Float [n];
318 
319  // initialize distribution and bin aliases, and compute histogram sum
320  for (i=0; i<n; i++) {
321  bins[i] = i;
322  p[i] = f[i];
323  sum += p[i];
324  }
325 
326  // normalize distribution
327  if (sum <= 0) {
328  egsFatal("Error: %s, line %d: degenerate distribution, histogram sum <= 0", __FILE__, __LINE__);
329  }
330  else {
331  for (i=0; i<n; i++) {
332  p[i] /= sum;
333  }
334  }
335 
336  // sort bins into "big" and "small" lists
337  vector<int> big_list; // bins above average
338  vector<int> small_list; // bins below average
339  for (i=0; i<n; i++) {
340  wi[i] = p[i]*n;
341  if (wi[i] <= 1.0) {
342  small_list.push_back(i);
343  }
344  else {
345  big_list.push_back(i);
346  }
347  }
348 
349  // alias
350  int loopCount=0;
351  while (big_list.size() > 0 && small_list.size() > 0 && loopCount++ <= loopMax) {
352 
353  // get a pair of big and small bins
354  int big = big_list.back();
355  int small = small_list.back();
356 
357  // alias: fill small bin
358  bins[small] = big; // alias small to big
359  wi[big] -= (1.0 - wi[small]); // remove aliased portion from big bin
360  small_list.pop_back(); // small bin is now filled
361 
362  // check if big bin is now small
363  if (wi[big] < 1.0 + epsilon) {
364  big_list.pop_back();
365  small_list.push_back(big);
366  }
367  }
368  if (big_list.size() > 0) {
369  egsWarning("Warning: %s, line %d: table aliasing may be incomplete", __FILE__, __LINE__);
370  }
371 
372  // delete local arrays
373  delete [] p;
374 }
375 
376 
378  if (n > 0) {
379  delete [] wi;
380  delete [] bins;
381  }
382 }
A class for sampling random values from a given probability distribution using the alias table techni...
int sampleBin(EGS_RandomGenerator *rndm) const
Get a random bin from this table.
void initialize(int N, const EGS_Float *x, const EGS_Float *f, int Type=1)
Initialize the alias table.
EGS_Float sample(EGS_RandomGenerator *rndm) const
Get a random point from this table using the RNG rndm.
Base random number generator class. All random number generators should be derived from this class.
Definition: egs_rndm.h:67
EGS_Float getUniform()
Returns a random number uniformly distributed between zero (inclusive) and 1 (exclusive).
Definition: egs_rndm.h:103
EGS_SimpleAliasTable(int N, const EGS_Float *f)
Constructor.
~EGS_SimpleAliasTable()
Destructor.
EGS_AliasTable class header file.
Global egspp functions header file.
EGS_InfoFunction EGS_EXPORT egsFatal
Always use this function for reporting fatal errors.
const EGS_I64 loopMax
The maximum number of iterations for near-infinite loops.
Definition: egs_functions.h:96
const EGS_Float epsilon
The epsilon constant for floating point comparisons.
Definition: egs_functions.h:62
EGS_InfoFunction EGS_EXPORT egsWarning
Always use this function for reporting warnings.