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