Geant4  10.03.p01
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
G4PolynomialPDF.cc
Go to the documentation of this file.
1 //
2 // ********************************************************************
3 // * License and Disclaimer *
4 // * *
5 // * The Geant4 software is copyright of the Copyright Holders of *
6 // * the Geant4 Collaboration. It is provided under the terms and *
7 // * conditions of the Geant4 Software License, included in the file *
8 // * LICENSE and available at http://cern.ch/geant4/license . These *
9 // * include a list of copyright holders. *
10 // * *
11 // * Neither the authors of this software system, nor their employing *
12 // * institutes,nor the agencies providing financial support for this *
13 // * work make any representation or warranty, express or implied, *
14 // * regarding this software system or assume any liability for its *
15 // * use. Please see the license in the file LICENSE and URL above *
16 // * for the full disclaimer and the limitation of liability. *
17 // * *
18 // * This code implementation is the result of the scientific and *
19 // * technical work of the GEANT4 collaboration. *
20 // * By using, copying, modifying or distributing the software (or *
21 // * any work based on the software) you agree to acknowledge its *
22 // * use in resulting scientific publications, and indicate your *
23 // * acceptance of all terms of the Geant4 Software license. *
24 // ********************************************************************
25 //
26 //
27 // -------------------------------------------------------------------
28 // GEANT4 Class file
29 //
30 //
31 // File name: G4PolynomialPDF
32 //
33 // Author: Jason Detwiler (jasondet@gmail.com)
34 //
35 // Creation date: Aug 2012
36 // -------------------------------------------------------------------
37 
38 #include "G4PolynomialPDF.hh"
39 #include "Randomize.hh"
40 
41 using namespace std;
42 
44  G4double x1, G4double x2) :
45  fX1(x1), fX2(x2), fChanged(true), fTolerance(1.e-8), fVerbose(0)
46 {
47  if(coeffs != nullptr) SetCoefficients(n, coeffs);
48  else if(n > 0) SetNCoefficients(n);
49 }
50 
52 {}
53 
55 {
56  while(i >= fCoefficients.size()) fCoefficients.push_back(0);
57  /* Loop checking, 30-Oct-2015, G.Folger */
58  fCoefficients[i] = value;
59  fChanged = true;
60 }
61 
62 void G4PolynomialPDF::SetCoefficients(size_t nCoeffs,
63  const G4double* coefficients)
64 {
65  SetNCoefficients(nCoeffs);
66  for(size_t i=0; i<GetNCoefficients(); ++i) {
67  SetCoefficient(i, coefficients[i]);
68  }
69  fChanged = true;
70 }
71 
73 {
74  if(x2 <= x1) {
75  if(fVerbose > 0) {
76  G4cout << "G4PolynomialPDF::SetDomain() WARNING: Invalide domain! "
77  << "(x1 = " << x1 << ", x2 = " << x2 << ")." << G4endl;
78  }
79  return;
80  }
81  fX1 = x1;
82  fX2 = x2;
83  fChanged = true;
84 }
85 
87 {
90  while(fCoefficients.size()) { /* Loop checking, 30-Oct-2015, G.Folger */
91  if(fCoefficients[fCoefficients.size()-1] == 0.0) fCoefficients.pop_back();
92  else break;
93  }
94 
95  G4double x1N = fX1, x2N = fX2;
96  G4double sum = 0;
97  for(size_t i=0; i<GetNCoefficients(); ++i) {
98  sum += GetCoefficient(i)*(x2N - x1N)/G4double(i+1);
99  x1N*=fX1;
100  x2N*=fX2;
101  }
102  if(sum <= 0) {
103  if(fVerbose > 0) {
104  G4cout << "G4PolynomialPDF::Normalize() WARNING: PDF has non-positive area: "
105  << sum << G4endl;
106  Dump();
107  }
108  return;
109  }
110 
111  for(size_t i=0; i<GetNCoefficients(); ++i) {
112  SetCoefficient(i, GetCoefficient(i)/sum);
113  }
114 }
115 
117 {
123  if(ddxPower < -1 || ddxPower > 2) {
124  if(fVerbose > 0) {
125  G4cout << "G4PolynomialPDF::GetX() WARNING: ddxPower " << ddxPower
126  << " not implemented" << G4endl;
127  }
128  return 0.0;
129  }
130 
131  double f = 0.; // return value
132  double xN = 1.; // x to the power N
133  double x1N = 1.; // endpoint x1 to the power N; only used by CDF
134  for(size_t i=0; i<=GetNCoefficients(); ++i) {
135  if(ddxPower == -1) { // CDF
136  if(i>0) f += GetCoefficient(i-1)*(xN - x1N)/i;
137  x1N *= fX1;
138  }
139  else if(ddxPower == 0 && i<GetNCoefficients()) f += GetCoefficient(i)*xN; // PDF
140  else if(ddxPower == 1) { // (d/dx) PDF
141  if(i<GetNCoefficients()-1) f += GetCoefficient(i+1)*xN*(i+1);
142  }
143  else if(ddxPower == 2) { // (d2/dx2) PDF
144  if(i<GetNCoefficients()-2) f += GetCoefficient(i+2)*xN*((i+2)*(i+1));
145  }
146  xN *= x;
147  }
148  return f;
149 }
150 
152 {
153  // ax2 + bx + c = 0
154  // p': 2ax + b = 0 -> = 0 at min: x_extreme = -b/2a
155 
156  if(x1 < fX1 || x2 > fX2 || x2 < x1) {
157  if(fVerbose > 0) {
158  G4cout << "G4PolynomialPDF::HasNegativeMinimum() WARNING: Invalid range "
159  << x1 << " - " << x2 << G4endl;
160  }
161  return false;
162  }
163 
164  // If flat, then check anywhere.
165  if(GetNCoefficients() == 1) return (Evaluate(x1) < -fTolerance);
166 
167  // If linear, or if quadratic with negative second derivative,
168  // just check the endpoints
169  if(GetNCoefficients() == 2 ||
170  (GetNCoefficients() == 3 && GetCoefficient(2) < 0)) {
171  return (Evaluate(x1) < -fTolerance) || (Evaluate(x2) < -fTolerance);
172  }
173 
174  // If quadratic and second dervative is positive, check at the mininum
175  if(GetNCoefficients() == 3) {
176  G4double xMin = -GetCoefficient(1)*0.5/GetCoefficient(2);
177  if(xMin < x1) xMin = x1;
178  if(xMin > x2) xMin = x2;
179  return Evaluate(xMin) < -fTolerance;
180  }
181 
182  // Higher-order polynomials: consider any extremum between x1 and x2. If none
183  // are found, check the endpoints.
184  G4double extremum = GetX(0, x1, x2, 1);
185  if(Evaluate(extremum) < -fTolerance) return true;
186  else if(extremum <= x1+(x2-x1)*fTolerance ||
187  extremum >= x2-(x2-x1)*fTolerance) return false;
188  else return
189  HasNegativeMinimum(x1, extremum) || HasNegativeMinimum(extremum, x2);
190 }
191 
193 {
194  if(fChanged) {
195  Normalize();
196  if(HasNegativeMinimum(fX1, fX2)) {
197  if(fVerbose > 0) {
198  G4cout << "G4PolynomialPDF::GetRandomX() WARNING: PDF has negative values, returning 0..."
199  << G4endl;
200  }
201  return 0.0;
202  }
203  fChanged = false;
204  }
205  return EvalInverseCDF(G4UniformRand());
206 }
207 
209  G4int ddxPower, G4double guess, G4bool bisect)
210 {
217 
218  // input range checking
219  if(GetNCoefficients() == 0) {
220  if(fVerbose > 0) {
221  G4cout << "G4PolynomialPDF::GetX() WARNING: no PDF defined!" << G4endl;
222  }
223  return x2;
224  }
225  if(ddxPower < -1 || ddxPower > 1) {
226  if(fVerbose > 0) {
227  G4cout << "G4PolynomialPDF::GetX() WARNING: ddxPower " << ddxPower
228  << " not implemented" << G4endl;
229  }
230  return x2;
231  }
232  if(ddxPower == -1 && (p<0 || p>1)) {
233  if(fVerbose > 0) {
234  G4cout << "G4PolynomialPDF::GetX() WARNING: p is out of range" << G4endl;
235  }
236  return fX2;
237  }
238 
239  // check limits
240  if(x2 <= x1 || x1 < fX1 || x2 > fX2) {
241  if(fVerbose > 0) {
242  G4cout << "G4PolynomialPDF::GetX() WARNING: domain must have fX1 <= x1 < x2 <= fX2. "
243  << "You sent x1 = " << x1 << ", x2 = " << x2 << "." << G4endl;
244  }
245  return x2;
246  }
247 
248  // Return x2 for flat lines
249  if((ddxPower == 0 && GetNCoefficients() == 1) ||
250  (ddxPower == 1 && GetNCoefficients() == 2)) return x2;
251 
252  // Solve p = mx + b -> x = (p-b)/m for linear functions
253  if((ddxPower == -1 && GetNCoefficients() == 1) ||
254  (ddxPower == 0 && GetNCoefficients() == 2) ||
255  (ddxPower == 1 && GetNCoefficients() == 3)) {
256  G4double b = (ddxPower > -1) ? GetCoefficient(ddxPower) : -GetCoefficient(0)*fX1;
257  G4double slope = GetCoefficient(ddxPower+1);
258  if(ddxPower == 1) slope *= 2.;
259  G4double value = (p-b)/slope;
260  if(value < x1) {
261  return x1;
262  }
263  else if(value > x2) {
264  return x2;
265  }
266  else {
267  return value;
268  }
269  }
270 
271  // Solve quadratic equation for f-p=0 when f is quadratic
272  if((ddxPower == -1 && GetNCoefficients() == 2) ||
273  (ddxPower == 0 && GetNCoefficients() == 3) ||
274  (ddxPower == 1 && GetNCoefficients() == 4)) {
275  G4double c = -p + ((ddxPower > -1) ? GetCoefficient(ddxPower) : 0);
276  if(ddxPower == -1) c -= (GetCoefficient(0) + GetCoefficient(1)/2.*fX1)*fX1;
277  G4double b = GetCoefficient(ddxPower+1);
278  if(ddxPower == 1) b *= 2.;
279  G4double a = GetCoefficient(ddxPower+2);
280  if(ddxPower == 1) a *= 3;
281  else if(ddxPower == -1) a *= 0.5;
282  double sqrtFactor = b*b - 4.*a*c;
283  if(sqrtFactor < 0) return x2; // quadratic equation has no solution (p not in range of f)
284  sqrtFactor = sqrt(sqrtFactor)/2./fabs(a);
285  G4double valueMinus = -b/2./a - sqrtFactor;
286  if(valueMinus >= x1 && valueMinus <= x2) return valueMinus;
287  else if(valueMinus > x2) return x2;
288  G4double valuePlus = -b/2./a + sqrtFactor;
289  if(valuePlus >= x1 && valuePlus <= x2) return valuePlus;
290  else if(valuePlus < x1) return x2;
291  return (x1-valueMinus <= valuePlus-x2) ? x1 : x2;
292  }
293 
294  // f is non-trivial, so use Newton-Raphson
295  // start in the middle if no good guess is provided
296  if(guess < x1 || guess > x2) guess = (x2+x1)*0.5;
297  G4double lastChange = 1;
298  size_t iterations = 0;
299  while(fabs(lastChange) > fTolerance) { /* Loop checking, 02.11.2015, A.Ribon */
300  // calculate f and f' simultaneously
301  G4double f = -p;
302  G4double dfdx = 0;
303  G4double xN = 1;
304  G4double x1N = 1; // only used by CDF
305  for(size_t i=0; i<=GetNCoefficients(); ++i) {
306  if(ddxPower == -1) { // CDF
307  if(i>0) f += GetCoefficient(i-1)*(xN - x1N)/G4double(i);
308  if(i<GetNCoefficients()) dfdx += GetCoefficient(i)*xN;
309  x1N *= fX1;
310  }
311  else if(ddxPower == 0) { // PDF
312  if(i<GetNCoefficients()) f += GetCoefficient(i)*xN;
313  if(i+1<GetNCoefficients()) dfdx += GetCoefficient(i+1)*xN*G4double(i+1);
314  }
315  else { // ddxPower == 1: (d/dx) PDF
316  if(i+1<GetNCoefficients()) f += GetCoefficient(i+1)*xN*G4double(i+1);
317  if(i+2<GetNCoefficients()) dfdx += GetCoefficient(i+2)*xN*G4double(i+2);
318  }
319  xN *= guess;
320  }
321  if(f == 0) return guess;
322  if(dfdx == 0) {
323  if(fVerbose > 0) {
324  G4cout << "G4PolynomialPDF::GetX() WARNING: got f != 0 but slope = 0 for ddxPower = "
325  << ddxPower << G4endl;
326  }
327  return x2;
328  }
329  lastChange = - f/dfdx;
330 
331  if(guess + lastChange < x1) {
332  lastChange = x1 - guess;
333  } else if(guess + lastChange > x2) {
334  lastChange = x2 - guess;
335  }
336 
337  guess += lastChange;
338  lastChange /= (fX2-fX1);
339 
340  ++iterations;
341  if(iterations > 50) {
342  if(p!=0) {
343  if(fVerbose > 0) {
344  G4cout << "G4PolynomialPDF::GetX() WARNING: got stuck searching for " << p
345  << " between " << x1 << " and " << x2 << " with ddxPower = "
346  << ddxPower
347  << ". Last guess was " << guess << "." << G4endl;
348  }
349  }
350  if(ddxPower==-1 && bisect) {
351  if(fVerbose > 0) {
352  G4cout << "G4PolynomialPDF::GetX() WARNING: Bisceting and trying again..."
353  << G4endl;
354  }
355  return Bisect(p, x1, x2);
356  }
357  else return guess;
358  }
359  }
360  return guess;
361 }
362 
364  // Bisect to get 1% precision, then use Newton-Raphson
365  G4double z = (x2 + x1)/2.0; // [x1 z x2]
366  if((x2 - x1)/(fX2 - fX1) < 0.01) return GetX(p, fX1, fX2, -1, z, false);
367  G4double fz = Evaluate(z, -1) - p;
368  if(fz < 0) return Bisect(p, z, x2); // [z x2]
369  return Bisect(p, x1, z); // [x1 z]
370 }
371 
373 {
374  G4cout << "G4PolynomialPDF::Dump() - PDF(x) = ";
375  for(size_t i=0; i<GetNCoefficients(); i++) {
376  if(i>0) G4cout << " + ";
377  G4cout << GetCoefficient(i);
378  if(i>0) G4cout << "*x";
379  if(i>1) G4cout << "^" << i;
380  }
381  G4cout << G4endl;
382  G4cout << "G4PolynomialPDF::Dump() - Interval: " << fX1 << " <= x < "
383  << fX2 << G4endl;
384 }
385 
386 
std::vector< G4double > fCoefficients
void SetCoefficients(const std::vector< G4double > &v)
G4double GetRandomX()
const char * p
Definition: xmltok.h:285
void SetNCoefficients(size_t n)
G4PolynomialPDF(size_t n=0, const double *coeffs=nullptr, G4double x1=0, G4double x2=1)
G4double GetX(G4double p, G4double x1, G4double x2, G4int ddxPower=0, G4double guess=1.e99, G4bool bisect=true)
int G4int
Definition: G4Types.hh:78
G4double Bisect(G4double p, G4double x1, G4double x2)
#define G4UniformRand()
Definition: Randomize.hh:97
G4GLOB_DLL std::ostream G4cout
G4double GetCoefficient(size_t i) const
const XML_Char int const XML_Char * value
Definition: expat.h:331
void SetDomain(G4double x1, G4double x2)
bool G4bool
Definition: G4Types.hh:79
void SetCoefficient(size_t i, G4double value)
G4bool HasNegativeMinimum(G4double x1, G4double x2)
size_t GetNCoefficients() const
#define G4endl
Definition: G4ios.hh:61
G4double EvalInverseCDF(G4double p)
G4double Evaluate(G4double x, G4int ddxPower=0)
double G4double
Definition: G4Types.hh:76