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 #ifndef LHS_DRIVER_H
10 #define LHS_DRIVER_H
11
12 #include "pecos_stat_util.hpp"
13 #include "RandomVariable.hpp"
14
15
16 namespace Pecos {
17
18 // LHS rank array processing modes:
19 enum { IGNORE_RANKS, SET_RANKS, GET_RANKS, SET_GET_RANKS };
20
21
22 /// Driver class for Latin Hypercube Sampling (LHS)
23
24 /** This class provides common code for sampling methods which
25 employ the Latin Hypercube Sampling (LHS) package from Sandia
26 Albuquerque's Risk and Reliability organization. */
27
28 class LHSDriver
29 {
30 public:
31
32 //
33 //- Heading: Constructors and destructor
34 //
35
36 LHSDriver(); ///< default constructor
37 LHSDriver(const String& sample_type, short sample_ranks_mode = IGNORE_RANKS,
38 bool reports = true); ///< constructor
39 ~LHSDriver(); ///< destructor
40
41 //
42 //- Heading: Member functions
43 //
44
45 /// populate data when not passed through ctor
46 void initialize(const String& sample_type,
47 short sample_ranks_mode = IGNORE_RANKS, bool reports = true);
48
49 /// set randomSeed
50 void seed(int seed);
51 /// return randomSeed
52 int seed() const;
53
54 /// set random number generator, passing the name of the uniform
55 /// generator: rnum2 or mt19937 (default). Passed value is
56 /// superceded by environment variable DAKOTA_LHS_UNIFGEN, if
57 /// present
58 void rng(String unif_gen);
59 // return name of uniform generator
60 //String rng();
61
62 /// reseed using a deterministic sequence
63 void advance_seed_sequence();
64
65 /// generates a set of parameter samples from RandomVariable distributions
66 void generate_samples(const std::vector<RandomVariable>& random_vars,
67 const RealSymMatrix& corr, int num_samples,
68 RealMatrix& samples, RealMatrix& sample_ranks,
69 const BitArray& active_vars = BitArray(),
70 const BitArray& active_corr = BitArray());
71 /// Similar to generate_samples, but this function seeks uniqueness in
72 /// discrete samples through iterative replacement of duplicates
73 void generate_unique_samples(const std::vector<RandomVariable>& random_vars,
74 const RealSymMatrix& corr, int num_samples,
75 RealMatrix& samples, RealMatrix& sample_ranks,
76 const BitArray& active_vars = BitArray(),
77 const BitArray& active_corr = BitArray());
78
79 /*
80 /// generates a set of parameter samples from RandomVariable distributions
81 /// for the uncertain variable subset
82 void generate_uncertain_samples(
83 const std::vector<RandomVariable>& random_vars, const RealSymMatrix& corr,
84 int num_samples, RealMatrix& samples_array, bool backfill_flag = false);
85 /// generates a set of parameter samples from RandomVariable distributions
86 /// for the aleatory uncertain variable subset
87 void generate_aleatory_samples(
88 const std::vector<RandomVariable>& random_vars,
89 const RealSymMatrix& corr, int num_samples, RealMatrix& samples_array,
90 bool backfill_flag = false);
91 /// generates a set of parameter samples from RandomVariable distributions
92 /// for the epistemic uncertain variable subset
93 void generate_epistemic_samples(
94 const std::vector<RandomVariable>& random_vars, const RealSymMatrix& corr,
95 int num_samples, RealMatrix& samples_array, bool backfill_flag = false);
96 */
97
98 /// generates a set of parameter samples for a set of (correlated)
99 /// normal distributions
100 void generate_normal_samples(const RealVector& n_means,
101 const RealVector& n_std_devs, const RealVector& n_l_bnds,
102 const RealVector& n_u_bnds, RealSymMatrix& corr, int num_samples,
103 RealMatrix& samples_array);
104 /// generates a set of parameter samples for a set of (correlated)
105 /// uniform distributions
106 void generate_uniform_samples(const RealVector& u_l_bnds,
107 const RealVector& u_u_bnds, RealSymMatrix& corr, int num_samples,
108 RealMatrix& samples_array);
109
110 /// generates integer index samples from within uncorrelated uniform
111 /// discrete range distributions
112 void generate_uniform_index_samples(const IntVector& index_l_bnds,
113 const IntVector& index_u_bnds, int num_samples, IntMatrix& index_samples,
114 bool backfill_flag = false);
115
116 private:
117
118 //
119 //- Heading: Convenience functions
120 //
121
122 /// checks whether LHS is enabled in the build and aborts if not
123 static void abort_if_no_lhs();
124
125 /// check for valid range for type T
126 template <typename T>
127 void check_range(T l_bnd, T u_bnd, bool allow_equal) const;
128 /// check for finite bounds for type T
129 template <typename T>
130 void check_finite(T l_bnd, T u_bnd) const;
131 /// checks the return codes from LHS routines and aborts with an
132 /// error message if an error was returned
133 void check_error(int err_code, const char* err_source = NULL,
134 const char* err_case = NULL) const;
135
136 /// register a random variable with LHS using the DIST format
137 void lhs_dist_register(const char* var_name, const char* dist_name,
138 size_t rv, const RealArray& dist_params);
139 /// register a random variable with LHS using the UDIST format
140 void lhs_udist_register(const char* var_name, const char* dist_name,
141 size_t rv, const RealArray& x_val,
142 const RealArray& y_val);
143 /// register a random variable with LHS using the CONST format
144 void lhs_const_register(const char* var_name, size_t rv, Real val);
145
146 /// create F77 string label from base name + tag + padding (field = 16 chars)
147 void f77name16(const char* name, size_t index, String& label);
148 /// create F77 string label from base name + padding (field = 32 chars)
149 void f77name32(const char* name, String& label);
150
151 bool test_unique(const std::vector<RandomVariable>& random_vars,
152 const BitArray& active_vars, const Real* new_samp,
153 std::set<RealArray>& unique_samples);
154
155 //
156 //- Heading: Data
157 //
158
159 /// type of sampling: random, lhs, incremental_lhs, or incremental_random
160 String sampleType;
161
162 /// variable labels passed to LHS; required for specification of correlations
163 StringArray lhsNames;
164
165 /// mode of sample ranks I/O: IGNORE_RANKS, SET_RANKS, GET_RANKS, or
166 /// SET_GET_RANKS
167 short sampleRanksMode;
168
169 /// flag for generating LHS report output
170 bool reportFlag;
171
172 /// the current random number seed
173 int randomSeed;
174 /// for honoring advance_seed_sequence() calls
175 short allowSeedAdvance; // bit 1 = first-time flag
176 // bit 2 = allow repeated seed update
177 };
178
179
180 inline void LHSDriver::
initialize(const String & sample_type,short sample_ranks_mode,bool reports)181 initialize(const String& sample_type, short sample_ranks_mode, bool reports)
182 {
183 sampleType = sample_type;
184 sampleRanksMode = sample_ranks_mode;
185 reportFlag = reports;
186 }
187
188
LHSDriver()189 inline LHSDriver::LHSDriver():
190 sampleType("lhs"), sampleRanksMode(IGNORE_RANKS), reportFlag(true),
191 randomSeed(0), allowSeedAdvance(1)
192 { abort_if_no_lhs(); }
193
194
LHSDriver(const String & sample_type,short sample_ranks_mode,bool reports)195 inline LHSDriver::LHSDriver(const String& sample_type,
196 short sample_ranks_mode, bool reports) :
197 randomSeed(0), allowSeedAdvance(1)
198 {
199 abort_if_no_lhs();
200 initialize(sample_type, sample_ranks_mode, reports);
201 }
202
203
~LHSDriver()204 inline LHSDriver::~LHSDriver()
205 { }
206
207
seed() const208 inline int LHSDriver::seed() const
209 { return randomSeed; }
210
211
212 /** It would be preferable to call srand() only once and then call rand()
213 for each LHS execution (the intended usage model), but possible
214 interaction with other uses of rand() in other contexts is a concern.
215 E.g., an srand(clock()) executed elsewhere would ruin the
216 repeatability of the LHSDriver seed sequence. The only way to insure
217 isolation is to reseed each time. Any induced correlation should be
218 inconsequential for the intended use. */
advance_seed_sequence()219 inline void LHSDriver::advance_seed_sequence()
220 {
221 if (allowSeedAdvance & 2) { // repeated seed updates allowed
222 std::srand(randomSeed);
223 seed(1 + std::rand()); // from 1 to RANDMAX+1
224 }
225 }
226
227
228 /*
229 inline void LHSDriver::
230 generate_uncertain_samples(const std::vector<RandomVariable>& random_vars,
231 const RealSymMatrix& corr, int num_samples,
232 RealMatrix& samples_array, bool backfill_flag)
233 {
234 if (sampleRanksMode) {
235 PCerr << "Error: LHSDriver::generate_uncertain_samples() does not support "
236 << "sample rank input/output." << std::endl;
237 abort_handler(-1);
238 }
239 RealMatrix ranks;
240 // Note: insufficient to identify source x type if in transformed u space
241 BitArray active_rv; uncertain_subset(random_vars, active_rv);
242 BitArray active_corr; aleatory_uncertain_subset(random_vars, active_corr);
243 if (backfill_flag)
244 generate_unique_samples(random_vars, corr, num_samples, samples_array,
245 ranks, active_rv, active_corr);
246 else
247 generate_samples(random_vars, corr, num_samples, samples_array, ranks,
248 active_rv, active_corr);
249 }
250
251
252 inline void LHSDriver::
253 generate_aleatory_samples(const std::vector<RandomVariable>& random_vars,
254 const RealSymMatrix& corr, int num_samples,
255 RealMatrix& samples_array, bool backfill_flag)
256 {
257 if (sampleRanksMode) {
258 PCerr << "Error: generate_aleatory_samples() does not support sample rank "
259 << "input/output." << std::endl;
260 abort_handler(-1);
261 }
262 RealMatrix ranks;
263 // Note: insufficient to identify source x type if in transformed u space
264 BitArray active_rv; aleatory_uncertain_subset(random_vars, active_rv);
265 if (backfill_flag)
266 generate_unique_samples(random_vars, corr, num_samples, samples_array,
267 ranks, active_rv, active_rv);
268 else
269 generate_samples(random_vars, corr, num_samples, samples_array, ranks,
270 active_rv, active_rv);
271 }
272
273
274 inline void LHSDriver::
275 generate_epistemic_samples(const std::vector<RandomVariable>& random_vars,
276 const RealSymMatrix& corr, int num_samples,
277 RealMatrix& samples_array, bool backfill_flag)
278 {
279 if (sampleRanksMode) {
280 PCerr << "Error: generate_epistemic_samples() does not support sample rank "
281 << "input/output." << std::endl;
282 abort_handler(-1);
283 }
284 RealMatrix ranks;
285 // Note: insufficient to identify source x type if in transformed u space
286 BitArray active_rv; epistemic_uncertain_subset(random_vars, active_rv);
287 BitArray active_corr; aleatory_uncertain_subset(random_vars, active_corr);
288 if (backfill_flag)
289 generate_unique_samples(random_vars, corr, num_samples, samples_array,
290 ranks, active_rv, active_corr);
291 else
292 generate_samples(random_vars, corr, num_samples,
293 samples_array, ranks, active_rv, active_corr);
294 }
295 */
296
297
298 inline void LHSDriver::
generate_normal_samples(const RealVector & n_means,const RealVector & n_std_devs,const RealVector & n_l_bnds,const RealVector & n_u_bnds,RealSymMatrix & corr,int num_samples,RealMatrix & samples_array)299 generate_normal_samples(const RealVector& n_means, const RealVector& n_std_devs,
300 const RealVector& n_l_bnds, const RealVector& n_u_bnds,
301 RealSymMatrix& corr, int num_samples,
302 RealMatrix& samples_array)//, bool backfill_flag)
303 {
304 if (sampleRanksMode) {
305 PCerr << "Error: generate_normal_samples() does not support sample rank "
306 << "input/output." << std::endl;
307 abort_handler(-1);
308 }
309 size_t i, num_rv = n_means.length();
310 std::vector<RandomVariable> random_vars(num_rv);
311 bool l_bnds = !n_l_bnds.empty(), u_bnds = !n_u_bnds.empty(), l_bnd, u_bnd;
312 Real dbl_inf = std::numeric_limits<Real>::infinity();
313 for (i=0; i<num_rv; ++i) {
314 l_bnd = (l_bnds && n_l_bnds[i] > -dbl_inf);
315 u_bnd = (u_bnds && n_u_bnds[i] < dbl_inf);
316 RandomVariable& rv_i = random_vars[i];
317 rv_i = (l_bnd || u_bnd) ? RandomVariable(BOUNDED_NORMAL)
318 : RandomVariable(NORMAL);
319 rv_i.push_parameter(N_MEAN, n_means[i]);
320 rv_i.push_parameter(N_STD_DEV, n_std_devs[i]);
321 if (l_bnd) rv_i.push_parameter(N_LWR_BND, n_l_bnds[i]);
322 if (u_bnd) rv_i.push_parameter(N_UPR_BND, n_u_bnds[i]);
323 }
324 RealMatrix ranks;
325 // All variables are continuous normal --> no need for backfill
326 //if (backfill_flag)
327 // generate_unique_samples(random_vars,corr,num_samples,samples_array,ranks);
328 //else
329 generate_samples(random_vars, corr, num_samples, samples_array, ranks);
330 }
331
332
333 inline void LHSDriver::
generate_uniform_samples(const RealVector & u_l_bnds,const RealVector & u_u_bnds,RealSymMatrix & corr,int num_samples,RealMatrix & samples_array)334 generate_uniform_samples(const RealVector& u_l_bnds, const RealVector& u_u_bnds,
335 RealSymMatrix& corr, int num_samples,
336 RealMatrix& samples_array)//, bool backfill_flag)
337 {
338 if (sampleRanksMode) {
339 PCerr << "Error: generate_uniform_samples() does not support sample rank "
340 << "input/output." << std::endl;
341 abort_handler(-1);
342 }
343 size_t i, num_rv = u_l_bnds.length();
344 std::vector<RandomVariable> random_vars(num_rv);
345 for (i=0; i<num_rv; ++i) {
346 RandomVariable& rv_i = random_vars[i];
347 rv_i = RandomVariable(UNIFORM);
348 rv_i.push_parameter(U_LWR_BND, u_l_bnds[i]);
349 rv_i.push_parameter(U_UPR_BND, u_u_bnds[i]);
350 }
351 RealMatrix ranks;
352 // All variables are continuous uniform --> no need for backfill
353 //if (backfill_flag)
354 // generate_unique_samples(random_vars,corr,num_samples,samples_array,ranks);
355 //else
356 generate_samples(random_vars, corr, num_samples, samples_array, ranks);
357 }
358
359
360 inline void LHSDriver::
generate_uniform_index_samples(const IntVector & index_l_bnds,const IntVector & index_u_bnds,int num_samples,IntMatrix & index_samples,bool backfill_flag)361 generate_uniform_index_samples(const IntVector& index_l_bnds,
362 const IntVector& index_u_bnds, int num_samples,
363 IntMatrix& index_samples, bool backfill_flag)
364 {
365 if (sampleRanksMode) {
366 PCerr << "Error: generate_uniform_index_samples() does not support sample "
367 << "rank input/output." << std::endl;
368 abort_handler(-1);
369 }
370 // For uniform probability, model as discrete range (this fn).
371 // For nonuniform probability, model as discrete uncertain set integer.
372 size_t i, num_rv = index_l_bnds.length();
373 std::vector<RandomVariable> random_vars(num_rv);
374 for (i=0; i<num_rv; ++i) {
375 RandomVariable& rv_i = random_vars[i];
376 rv_i = RandomVariable(DISCRETE_RANGE);
377 rv_i.push_parameter(DR_LWR_BND, index_l_bnds[i]);
378 rv_i.push_parameter(DR_UPR_BND, index_u_bnds[i]);
379 }
380 RealMatrix ranks, samples_rm; RealSymMatrix corr; // assume uncorrelated
381 if (backfill_flag)
382 generate_unique_samples(random_vars, corr, num_samples, samples_rm, ranks);
383 else
384 generate_samples(random_vars, corr, num_samples, samples_rm, ranks);
385 copy_data(samples_rm, index_samples);
386 }
387
388
389 /** Helper function to create labels with appended tags that Fortran
390 will see as character*16 values which are NOT null-terminated. */
f77name16(const char * name,size_t index,String & label)391 inline void LHSDriver::f77name16(const char* name, size_t index, String& label)
392 {
393 label = name + std::to_string(index+1);
394 label.resize(16, ' '); // NOTE: no NULL terminator
395 }
396
397
398 /** Helper function to create labels that Fortran will see as character*32
399 values which are NOT null-terminated. */
f77name32(const char * name,String & label)400 inline void LHSDriver::f77name32(const char* name, String& label)
401 {
402 label = name;
403 label.resize(32, ' '); // NOTE: no NULL terminator
404 }
405
406
407 template <typename T>
check_range(T l_bnd,T u_bnd,bool allow_equal) const408 void LHSDriver::check_range(T l_bnd, T u_bnd, bool allow_equal) const
409 {
410 if (l_bnd > u_bnd) {
411 PCerr << "\nError: lower bound exceeds upper bound in Pecos::LHSDriver."
412 << std::endl;
413 abort_handler(-1);
414 }
415 else if (!allow_equal && l_bnd == u_bnd) {
416 PCerr << "\nError: Pecos::LHSDriver requires non-zero range between lower "
417 << "and upper bounds." << std::endl;
418 abort_handler(-1);
419 }
420 }
421
422
423 template <typename T>
check_finite(T l_bnd,T u_bnd) const424 void LHSDriver::check_finite(T l_bnd, T u_bnd) const
425 {
426 if (std::numeric_limits<T>::has_infinity) { // floating point types
427 T T_inf = std::numeric_limits<T>::infinity();
428 if (l_bnd <= -T_inf || u_bnd >= T_inf) {
429 PCerr << "\nError: Pecos::LHSDriver requires finite bounds to sample a "
430 << "continuous range." << std::endl;
431 abort_handler(-1);
432 }
433 }
434 else if (l_bnd <= std::numeric_limits<T>::min() ||
435 u_bnd >= std::numeric_limits<T>::max()) { // INT_{MIN,MAX}
436 PCerr << "\nError: Pecos::LHSDriver requires finite bounds to sample a "
437 << "discrete range." << std::endl;
438 abort_handler(-1);
439 }
440 }
441
442
443 inline void LHSDriver::
check_error(int err_code,const char * err_source,const char * err_case) const444 check_error(int err_code, const char* err_source, const char* err_case) const
445 {
446 if (err_code) {
447 PCerr << "Error: code " << err_code << " in LHSDriver";
448 if (err_source != NULL) PCerr << " returned from " << err_source;
449 if (err_case != NULL) PCerr << " for case " << err_case;
450 PCerr << "." << std::endl;
451 abort_handler(-1);
452 }
453 }
454
455 } // namespace Pecos
456
457 #endif
458