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