1 /*  _______________________________________________________________________
2 
3     PECOS: Parallel Environment for Creation Of Stochastics
4     Copyright (c) 2011, Sandia National Laboratories.
5     This software is distributed under the GNU Lesser General Public License.
6     For more information, see the README file in the top Pecos directory.
7     _______________________________________________________________________ */
8 
9 //- Class:	 TriangularRandomVariable
10 //- Description: Encapsulates random variable data and utilities
11 //- Owner:       Mike Eldred
12 //- Revised by:
13 //- Version:
14 
15 #ifndef TRIANGULAR_RANDOM_VARIABLE_HPP
16 #define TRIANGULAR_RANDOM_VARIABLE_HPP
17 
18 #include "UniformRandomVariable.hpp"
19 
20 namespace Pecos {
21 
22 
23 /// Derived random variable class for triangular random variables.
24 
25 /** Manages mode and inherits bounds.  See Haldar and Mahadevan, p. 99. */
26 
27 class TriangularRandomVariable: public UniformRandomVariable
28 {
29 public:
30 
31   //
32   //- Heading: Constructors and destructor
33   //
34 
35   /// default constructor
36   TriangularRandomVariable();
37   /// alternate constructor
38   TriangularRandomVariable(Real lwr, Real mode, Real upr);
39   /// destructor
40   ~TriangularRandomVariable();
41 
42   //
43   //- Heading: Virtual function redefinitions
44   //
45 
46   Real cdf(Real x) const;
47   Real ccdf(Real x) const;
48   Real inverse_cdf(Real p_cdf) const;
49   Real inverse_ccdf(Real p_ccdf) const;
50 
51   Real pdf(Real x) const;
52   Real pdf_gradient(Real x) const;
53   Real pdf_hessian(Real x) const;
54 
55   void pull_parameter(short dist_param, Real& val) const;
56   void push_parameter(short dist_param, Real  val);
57 
58   void copy_parameters(const RandomVariable& rv);
59 
60   Real mean() const;
61   Real median() const;
62   Real mode() const;
63   Real standard_deviation() const;
64   Real variance() const;
65 
66   Real dx_ds(short dist_param, short u_type, Real x, Real z) const;
67   Real dz_ds_factor(short u_type, Real x, Real z) const;
68 
69   //
70   //- Heading: Member functions
71   //
72 
73   void update(Real lwr, Real mode, Real upr);
74 
75   //
76   //- Heading: Static member functions (global utilities)
77   //
78 
79   static Real pdf(Real x, Real lwr, Real mode, Real upr);
80   static Real cdf(Real x, Real lwr, Real mode, Real upr);
81 
82   static void moments_from_params(Real lwr, Real mode, Real upr,
83 				  Real& mean, Real& std_dev);
84 
85 protected:
86 
87   //
88   //- Heading: Member functions
89   //
90 
91   /// create a new triangDist instance
92   void update_boost();
93   /// create a new triangDist instance if parameter set is valid
94   void update_boost_conditionally();
95 
96   //
97   //- Heading: Data
98   //
99 
100   /// mode of triangular random variable
101   Real triangularMode;
102 
103   /// pointer to the Boost trainagulardistribution instance
104   std::unique_ptr<triangular_dist> triangDist;
105 };
106 
107 
TriangularRandomVariable()108 inline TriangularRandomVariable::TriangularRandomVariable():
109   UniformRandomVariable(), triangularMode(0.),
110   triangDist(new triangular_dist(lowerBnd, triangularMode, upperBnd))
111 { ranVarType = TRIANGULAR; }
112 
113 
114 inline TriangularRandomVariable::
TriangularRandomVariable(Real lwr,Real mode,Real upr)115 TriangularRandomVariable(Real lwr, Real mode, Real upr):
116   UniformRandomVariable(lwr, upr), triangularMode(mode),
117   triangDist(new triangular_dist(lwr, mode, upr))
118 { ranVarType = TRIANGULAR; }
119 
120 
~TriangularRandomVariable()121 inline TriangularRandomVariable::~TriangularRandomVariable()
122 { }
123 
124 
pdf(Real x,Real lwr,Real mode,Real upr)125 inline Real TriangularRandomVariable::pdf(Real x, Real lwr, Real mode, Real upr)
126 {
127   triangular_dist tri1(lwr, mode, upr);
128   return bmth::pdf(tri1, x);
129 
130   //return (x < mode) ? 2.*(x-lwr)/(upr-lwr)/(mode-lwr) :
131   //                    2.*(upr-x)/(upr-lwr)/(upr-mode);
132 }
133 
134 
cdf(Real x,Real lwr,Real mode,Real upr)135 inline Real TriangularRandomVariable::cdf(Real x, Real lwr, Real mode, Real upr)
136 {
137   triangular_dist tri1(lwr, mode, upr);
138   return bmth::cdf(tri1, x);
139 
140   //return (x < mode) ? std::pow(x-lwr,2.)/(upr-lwr)/(mode-lwr) :
141   //  ((mode-lwr) - (x+mode-2*upr)*(x-mode)/(upr-mode))/(upr-lwr);
142 }
143 
144 
cdf(Real x) const145 inline Real TriangularRandomVariable::cdf(Real x) const
146 { return bmth::cdf(*triangDist, x); }
147 
148 
ccdf(Real x) const149 inline Real TriangularRandomVariable::ccdf(Real x) const
150 { return bmth::cdf(complement(*triangDist, x)); }
151 
152 
inverse_cdf(Real p_cdf) const153 inline Real TriangularRandomVariable::inverse_cdf(Real p_cdf) const
154 {
155   return bmth::quantile(*triangDist, p_cdf);
156 
157   /*
158   // assume x < mode and then check
159   Real range = upperBnd - lowerBnd,
160        x = lowerBnd + std::sqrt(p*range*(triangularMode-lowerBnd));
161   Real x_pdf = 2.*(x-lowerBnd)/range/(triangularMode-lowerBnd),
162        m_pdf = 2./range;
163   // check pdf value to ensure that proper equation used
164   if ( x_pdf > m_pdf )
165     x = upperBnd - std::sqrt((1.-p)*range*(upperBnd-triangularMode));
166   return x;
167   */
168 }
169 
170 
inverse_ccdf(Real p_ccdf) const171 inline Real TriangularRandomVariable::inverse_ccdf(Real p_ccdf) const
172 { return bmth::quantile(complement(*triangDist, p_ccdf)); }
173 
174 
175 //             x < M                        x > M
176 //  F(x): (x-L)^2/(U-L)/(M-L)    (M-L)/(U-L) - (x+M-2U)(x-M)/(U-L)/(U-M)
177 //  f(x): 2(x-L)/(U-L)/(M-L)     2(U-x)/(U-L)/(U-M)
178 // f'(x): 2/(U-L)/(M-L)          -2/(U-L)/(U-M)
179 // Note: at x=M, F(x) and f(x) are continuous but f'(x) is not
pdf(Real x) const180 inline Real TriangularRandomVariable::pdf(Real x) const
181 { return bmth::pdf(*triangDist, x); }
182 
183 
pdf_gradient(Real x) const184 inline Real TriangularRandomVariable::pdf_gradient(Real x) const
185 {
186   Real range = upperBnd - lowerBnd;
187   if (x < triangularMode)
188     return  2. / ( range * (triangularMode - lowerBnd) );
189   else if (x > triangularMode)
190     return -2. / ( range * (upperBnd - triangularMode) );
191   else // x == triangularMode
192     return  0.; // f'(x) is undefined: use 0.
193 }
194 
195 
pdf_hessian(Real x) const196 inline Real TriangularRandomVariable::pdf_hessian(Real x) const
197 { return 0.; }
198 
199 
200 inline void TriangularRandomVariable::
pull_parameter(short dist_param,Real & val) const201 pull_parameter(short dist_param, Real& val) const
202 {
203   switch (dist_param) {
204   case T_MODE:    val = triangularMode; break;
205   case T_LWR_BND: val = lowerBnd;       break;
206   case T_UPR_BND: val = upperBnd;       break;
207   default:
208     PCerr << "Error: update failure for distribution parameter " << dist_param
209 	  << " in TriangularRandomVariable::pull_parameter(Real)." << std::endl;
210     abort_handler(-1); break;
211   }
212 }
213 
214 
push_parameter(short dist_param,Real val)215 inline void TriangularRandomVariable::push_parameter(short dist_param, Real val)
216 {
217   switch (dist_param) {
218   case T_MODE:    triangularMode = val; break;
219   case T_LWR_BND: lowerBnd       = val; break;
220   case T_UPR_BND: upperBnd       = val; break;
221   default:
222     PCerr << "Error: update failure for distribution parameter " << dist_param
223 	  << " in TriangularRandomVariable::push_parameter(Real)." << std::endl;
224     abort_handler(-1); break;
225   }
226   update_boost_conditionally();// create new triangDist instance if valid params
227 }
228 
229 
copy_parameters(const RandomVariable & rv)230 inline void TriangularRandomVariable::copy_parameters(const RandomVariable& rv)
231 {
232   rv.pull_parameter(T_MODE,    triangularMode);
233   //UniformRandomVariable::copy_parameters(rv); // different enums used
234   rv.pull_parameter(T_LWR_BND, lowerBnd);
235   rv.pull_parameter(T_UPR_BND, upperBnd);
236   update_boost(); // create a new triangDist instance
237 }
238 
239 
mean() const240 inline Real TriangularRandomVariable::mean() const
241 { return bmth::mean(*triangDist); }
242 
243 
median() const244 inline Real TriangularRandomVariable::median() const
245 { return bmth::median(*triangDist); }
246 
247 
mode() const248 inline Real TriangularRandomVariable::mode() const
249 { return bmth::mode(*triangDist); }
250 
251 
standard_deviation() const252 inline Real TriangularRandomVariable::standard_deviation() const
253 { return bmth::standard_deviation(*triangDist); }
254 
255 
variance() const256 inline Real TriangularRandomVariable::variance() const
257 { return bmth::variance(*triangDist); }
258 
259 
260 /** dx/ds is derived by differentiating NatafTransformation::trans_Z_to_X()
261     with respect to distribution parameter s.  dz/ds is zero if uncorrelated,
262     while dz_ds_factor() manages contributions in the correlated case. */
263 inline Real TriangularRandomVariable::
dx_ds(short dist_param,short u_type,Real x,Real z) const264 dx_ds(short dist_param, short u_type, Real x, Real z) const
265 {
266   bool u_type_err = false, dist_error = false; Real sdf;
267   if (x < triangularMode)
268     switch (u_type) {
269     case STD_NORMAL:  sdf =  NormalRandomVariable::std_cdf(z);  break;
270     case STD_UNIFORM: sdf = UniformRandomVariable::std_cdf(z);  break;
271     //case TRIANGULAR: break;
272     default: u_type_err = true; break;
273     }
274   else
275     switch (u_type) {
276     case STD_NORMAL:  sdf =  NormalRandomVariable::std_ccdf(z); break;
277     case STD_UNIFORM: sdf = UniformRandomVariable::std_ccdf(z); break;
278     //case TRIANGULAR: break;
279     default: u_type_err = true; break;
280     }
281   if (u_type_err) {
282     PCerr << "Error: unsupported u-space type " << u_type
283 	  << " in TriangularRandomVariable::dx_ds()." << std::endl;
284     abort_handler(-1);
285   }
286 
287   if (x < triangularMode) {
288     Real denom = 2.*(x-lowerBnd);
289     switch (dist_param) {
290     case T_MODE:    return sdf*(upperBnd-lowerBnd)/denom;        break;
291     case T_LWR_BND:
292       return 1.+sdf*(2.*lowerBnd-upperBnd-triangularMode)/denom; break;
293     case T_UPR_BND: return sdf*(triangularMode-lowerBnd)/denom;  break;
294     // Triangular Mean          - TO DO
295     // Triangular Std Deviation - TO DO
296     default:        dist_error = true;                           break;
297     }
298   }
299   else {
300     Real denom = 2.*(upperBnd-x);
301     switch (dist_param) {
302     case T_MODE:    return sdf*(upperBnd-lowerBnd)/denom;        break;
303     case T_LWR_BND: return (upperBnd-triangularMode)*sdf/denom;  break;
304     case T_UPR_BND:
305       return 1.-sdf*(2.*upperBnd-lowerBnd-triangularMode)/denom; break;
306     // Triangular Mean          - TO DO
307     // Triangular Std Deviation - TO DO
308     default:        dist_error = true;                           break;
309     }
310   }
311   if (dist_error) {
312     PCerr << "Error: mapping failure for distribution parameter " << dist_param
313 	  << " in TriangularRandomVariable::dx_ds()." << std::endl;
314     abort_handler(-1); return 0.;
315   }
316 }
317 
318 
319 /** dx/ds is derived by differentiating NatafTransformation::trans_Z_to_X()
320     with respect to distribution parameter s.  For the uncorrelated case,
321     u and z are constants.  For the correlated case, u is a constant, but
322     z(s) = L(s) u due to Nataf dependence on s and dz/ds = dL/ds u. */
323 inline Real TriangularRandomVariable::
dz_ds_factor(short u_type,Real x,Real z) const324 dz_ds_factor(short u_type, Real x, Real z) const
325 {
326   Real pdf;
327   switch (u_type) {
328   case STD_NORMAL:  pdf =  NormalRandomVariable::std_pdf(z); break;
329   case STD_UNIFORM: pdf = UniformRandomVariable::std_pdf(z); break;
330   //case TRIANGULAR: break;
331   default:
332     PCerr << "Error: unsupported u-space type " << u_type
333 	  << " in TriangularRandomVariable::dz_ds_factor()." << std::endl;
334     abort_handler(-1); break;
335   }
336 
337   return (x < triangularMode) ?
338     (upperBnd-lowerBnd)*(triangularMode-lowerBnd)*pdf/(2.*(x-lowerBnd)) :
339     (upperBnd-triangularMode)*(upperBnd-lowerBnd)*pdf/(2.*(upperBnd-x));
340 }
341 
342 
update_boost()343 inline void TriangularRandomVariable::update_boost()
344 {
345   triangDist.reset(new triangular_dist(lowerBnd, triangularMode, upperBnd));
346 }
347 
348 
update_boost_conditionally()349 inline void TriangularRandomVariable::update_boost_conditionally()
350 {
351   // old is now invalid
352   if (triangDist) triangDist.reset();
353   // new may not be valid as of yet
354   if (lowerBnd <= triangularMode && triangularMode <= upperBnd)
355     triangDist.reset(new triangular_dist(lowerBnd, triangularMode, upperBnd));
356   // else wait for pending param updates
357 }
358 
359 
update(Real lwr,Real mode,Real upr)360 inline void TriangularRandomVariable::update(Real lwr, Real mode, Real upr)
361 {
362   if (!triangDist ||
363       lowerBnd != lwr || triangularMode != mode || upperBnd != upr)
364     { lowerBnd = lwr; triangularMode = mode; upperBnd = upr; update_boost(); }
365 }
366 
367 
368 inline void TriangularRandomVariable::
moments_from_params(Real lwr,Real mode,Real upr,Real & mean,Real & std_dev)369 moments_from_params(Real lwr, Real mode, Real upr, Real& mean, Real& std_dev)
370 {
371   mean = (lwr + mode + upr)/3.;
372   std_dev
373     = std::sqrt((lwr*(lwr - mode) + mode*(mode - upr) + upr*(upr - lwr))/18.);
374 }
375 
376 } // namespace Pecos
377 
378 #endif
379