1 /* _______________________________________________________________________
2
3 DAKOTA: Design Analysis Kit for Optimization and Terascale Applications
4 Copyright 2014-2020 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
5 This software is distributed under the GNU Lesser General Public License.
6 For more information, see the README file in the top Dakota directory.
7 _______________________________________________________________________ */
8
9 //- Class: NonDQuadrature
10 //- Description: Implementation code for NonDQuadrature class
11 //- Owner: Mike Eldred
12 //- Revised by:
13 //- Version:
14
15 #include "dakota_data_types.hpp"
16 #include "dakota_system_defs.hpp"
17 #include "NonDQuadrature.hpp"
18 #include "DakotaModel.hpp"
19 #include "DiscrepancyCorrection.hpp"
20 #include "ProblemDescDB.hpp"
21 #include "PolynomialApproximation.hpp"
22 #include "LHSDriver.hpp"
23 #include "dakota_stat_util.hpp"
24
25 static const char rcsId[]="@(#) $Id: NonDQuadrature.cpp,v 1.57 2004/06/21 19:57:32 mseldre Exp $";
26
27
28 namespace Dakota {
29
30 /** This constructor is called for a standard letter-envelope iterator
31 instantiation. In this case, set_db_list_nodes has been called
32 and probDescDB can be queried for settings from the method
33 specification. It is not currently used, as there is not yet a
34 separate nond_quadrature method specification. */
NonDQuadrature(ProblemDescDB & problem_db,Model & model)35 NonDQuadrature::NonDQuadrature(ProblemDescDB& problem_db, Model& model):
36 NonDIntegration(problem_db, model),
37 quadOrderSpec(probDescDB.get_ushort("method.nond.quadrature_order")),
38 numSamples(0), quadMode(FULL_TENSOR)
39 {
40 // initialize the numerical integration driver
41 numIntDriver = Pecos::IntegrationDriver(Pecos::QUADRATURE);
42 tpqDriver = std::static_pointer_cast<Pecos::TensorProductDriver>
43 (numIntDriver.driver_rep());
44
45 //check_variables(x_dist.random_variables());
46 // TO DO: create a ProbabilityTransformModel, if needed
47 const Pecos::MultivariateDistribution& u_dist
48 = model.multivariate_distribution();
49
50 short refine_type
51 = probDescDB.get_short("method.nond.expansion_refinement_type");
52 short refine_control
53 = probDescDB.get_short("method.nond.expansion_refinement_control");
54 short refine_metric = (refine_control) ? Pecos::COVARIANCE_METRIC :
55 Pecos::NO_METRIC;
56 short refine_stats = (refine_control) ? Pecos::ACTIVE_EXPANSION_STATS :
57 Pecos::NO_EXPANSION_STATS;
58 short nest_override = probDescDB.get_short("method.nond.nesting_override");
59 nestedRules = ( nest_override == Pecos::NESTED ||
60 ( refine_type && nest_override != Pecos::NON_NESTED ) );
61 Pecos::ExpansionConfigOptions ec_options(Pecos::QUADRATURE,
62 probDescDB.get_short("method.nond.expansion_basis_type"),
63 iteratedModel.correction_type(),
64 probDescDB.get_short("method.nond.multilevel_discrepancy_emulation"),
65 outputLevel, probDescDB.get_bool("method.variance_based_decomp"),
66 probDescDB.get_ushort("method.nond.vbd_interaction_order"),
67 refine_control, refine_metric, refine_stats,
68 probDescDB.get_int("method.nond.max_refinement_iterations"),
69 probDescDB.get_int("method.nond.max_solver_iterations"), convergenceTol,
70 probDescDB.get_ushort("method.soft_convergence_limit"));
71
72 bool piecewise_basis = (probDescDB.get_bool("method.nond.piecewise_basis") ||
73 refine_type == Pecos::H_REFINEMENT);
74 bool use_derivs = probDescDB.get_bool("method.derivative_usage");
75 bool equidist_rules = true; // NEWTON_COTES pts for piecewise interpolants
76 Pecos::BasisConfigOptions bc_options(nestedRules, piecewise_basis,
77 equidist_rules, use_derivs);
78
79 tpqDriver->initialize_grid(u_dist, ec_options, bc_options);
80 tpqDriver->initialize_grid_parameters(u_dist);
81
82 reset(); // init_dim_quad_order() uses integrationRules from initialize_grid()
83
84 maxEvalConcurrency *= tpqDriver->grid_size();
85 }
86
87
88 /** This alternate constructor is used for on-the-fly generation and
89 evaluation of numerical quadrature points. */
90 NonDQuadrature::
NonDQuadrature(Model & model,unsigned short quad_order,const RealVector & dim_pref,short driver_mode)91 NonDQuadrature(Model& model, unsigned short quad_order,
92 const RealVector& dim_pref, short driver_mode):
93 NonDIntegration(QUADRATURE_INTEGRATION, model, dim_pref), nestedRules(false),
94 quadOrderSpec(quad_order), numSamples(0), quadMode(FULL_TENSOR)
95 {
96 // initialize the numerical integration driver
97 numIntDriver = Pecos::IntegrationDriver(Pecos::QUADRATURE);
98 tpqDriver = std::static_pointer_cast<Pecos::TensorProductDriver>
99 (numIntDriver.driver_rep());
100
101 tpqDriver->mode(driver_mode);
102
103 // local natafTransform not yet updated: x_ran_vars would have to be passed in
104 // from NonDExpansion if check_variables() needed to be called here. Instead,
105 // it is deferred until run time in NonDIntegration::core_run().
106 //check_variables(x_ran_vars);
107 }
108
109
110 /** This alternate constructor is used for on-the-fly generation and
111 evaluation of filtered tensor quadrature points. */
112 NonDQuadrature::
NonDQuadrature(Model & model,unsigned short quad_order,const RealVector & dim_pref,short driver_mode,int num_filt_samples)113 NonDQuadrature(Model& model, unsigned short quad_order,
114 const RealVector& dim_pref, short driver_mode,
115 int num_filt_samples):
116 NonDIntegration(QUADRATURE_INTEGRATION, model, dim_pref),
117 nestedRules(false), // minimize zeros introduced into Vandermonde matrix
118 quadOrderSpec(quad_order), quadMode(FILTERED_TENSOR),
119 numSamples(num_filt_samples)
120 {
121 // initialize the numerical integration driver
122 numIntDriver = Pecos::IntegrationDriver(Pecos::QUADRATURE);
123 tpqDriver = std::static_pointer_cast<Pecos::TensorProductDriver>
124 (numIntDriver.driver_rep());
125
126 tpqDriver->mode(driver_mode);
127
128 // local natafTransform not yet updated: x_ran_vars would have to be passed in
129 // from NonDExpansion if check_variables() needed to be called here. Instead,
130 // it is deferred until run time in NonDIntegration::core_run().
131 //check_variables(x_ran_vars);
132 }
133
134
135 /** This alternate constructor is used for on-the-fly generation and
136 evaluation of random sampling from a tensor quadrature multi-index. */
137 NonDQuadrature::
NonDQuadrature(Model & model,unsigned short quad_order,const RealVector & dim_pref,short driver_mode,int num_sub_samples,int seed)138 NonDQuadrature(Model& model, unsigned short quad_order,
139 const RealVector& dim_pref, short driver_mode,
140 int num_sub_samples, int seed):
141 NonDIntegration(QUADRATURE_INTEGRATION, model, dim_pref),
142 nestedRules(false), // minimize zeros introduced into Vandermonde matrix
143 quadOrderSpec(quad_order), quadMode(RANDOM_TENSOR),
144 numSamples(num_sub_samples), randomSeed(seed)
145 {
146 // initialize the numerical integration driver
147 numIntDriver = Pecos::IntegrationDriver(Pecos::QUADRATURE);
148 tpqDriver = std::static_pointer_cast<Pecos::TensorProductDriver>
149 (numIntDriver.driver_rep());
150
151 tpqDriver->mode(driver_mode);
152
153 // local natafTransform not yet updated: x_ran_vars would have to be passed in
154 // from NonDExpansion if check_variables() needed to be called here. Instead,
155 // it is deferred until run time in NonDIntegration::core_run().
156 //check_variables(x_ran_vars);
157 }
158
159
~NonDQuadrature()160 NonDQuadrature::~NonDQuadrature()
161 { }
162
163
164 /** Used in combination with alternate NonDQuadrature constructor. */
165 void NonDQuadrature::
initialize_grid(const std::vector<Pecos::BasisPolynomial> & poly_basis)166 initialize_grid(const std::vector<Pecos::BasisPolynomial>& poly_basis)
167 {
168 tpqDriver->initialize_grid(poly_basis);
169 tpqDriver->
170 initialize_grid_parameters(iteratedModel.multivariate_distribution());
171
172 switch (quadMode) {
173 case FULL_TENSOR:
174 // infer nestedRules
175 for (size_t i=0; i<numContinuousVars; ++i) {
176 short rule = poly_basis[i].collocation_rule();
177 // Distinguish between rules that *support* vs. *require* nesting, since
178 // TPQ should allow unrestricted order specifications where supported.
179 // > GENZ_KEISTER and GAUSS_PATTERSON are only defined as nested rules
180 // > NEWTON_COTES, CLENSHAW_CURTIS, FEJER2 support nesting but also allow
181 // arbitrary order specification
182 if (rule == Pecos::GENZ_KEISTER || rule == Pecos::GAUSS_PATTERSON)// ||
183 //rule == Pecos::NEWTON_COTES || rule == Pecos::CLENSHAW_CURTIS ||
184 //rule == Pecos::FEJER2)
185 { nestedRules = true; break; }
186 }
187 reset();
188 maxEvalConcurrency *= tpqDriver->grid_size();
189 break;
190 case FILTERED_TENSOR:
191 // nested overrides not currently part of tensor regression spec
192 //for () if () { nestedRules = true; break; }
193 update(); // compute min quad order reqd for numSamples
194 maxEvalConcurrency *= numSamples;
195 break;
196 case RANDOM_TENSOR:
197 // nested overrides not currently part of tensor regression spec
198 //for () if () { nestedRules = true; break; }
199 reset(); // propagate updated settings to tpqDriver
200 update(); // enforce min quad order constraints
201 maxEvalConcurrency *= numSamples;
202 break;
203 }
204 }
205
206
207 void NonDQuadrature::
initialize_dimension_quadrature_order(unsigned short quad_order_spec,const RealVector & dim_pref_spec)208 initialize_dimension_quadrature_order(unsigned short quad_order_spec,
209 const RealVector& dim_pref_spec)
210 {
211 // Update ref_quad_order from quad_order_spec and dim_pref_spec
212 UShortArray ref_quad_order;
213 dimension_preference_to_anisotropic_order(quad_order_spec, dim_pref_spec,
214 numContinuousVars, ref_quad_order);
215
216 // Update Pecos::TensorProductDriver::quadOrder from ref_quad_order
217 tpqDriver->reference_quadrature_order(ref_quad_order, nestedRules);
218 }
219
220
221 void NonDQuadrature::
compute_minimum_quadrature_order(size_t min_samples,const RealVector & dim_pref)222 compute_minimum_quadrature_order(size_t min_samples, const RealVector& dim_pref)
223 {
224 UShortArray ref_quad_order(numContinuousVars, 1);
225 // compute minimal order tensor grid with at least min_samples points
226 if (dim_pref.empty()) // isotropic tensor grid
227 while (tpqDriver->grid_size() < min_samples)
228 increment_grid(ref_quad_order);
229 else // anisotropic tensor grid
230 while (tpqDriver->grid_size() < min_samples)
231 increment_grid_preference(dim_pref, ref_quad_order);
232 }
233
234
get_parameter_sets(Model & model)235 void NonDQuadrature::get_parameter_sets(Model& model)
236 {
237 // capture any distribution parameter insertions
238 if (subIteratorFlag)
239 tpqDriver->initialize_grid_parameters(model.multivariate_distribution());
240
241 // Precompute quadrature rules (e.g., by defining maximal order for
242 // NumGenOrthogPolynomial::solve_eigenproblem()):
243 tpqDriver->precompute_rules(); // efficiency optimization
244
245 size_t i, j, num_quad_points = tpqDriver->grid_size();
246 const Pecos::UShortArray& quad_order = tpqDriver->quadrature_order();
247 Cout << "\nNumber of Gauss points per variable: { ";
248 for (i=0; i<numContinuousVars; ++i)
249 Cout << quad_order[i] << ' ';
250 Cout << "}\n";
251
252 switch (quadMode) {
253 // --------------------------------
254 // Tensor quadrature (default mode)
255 // --------------------------------
256 case FULL_TENSOR:
257 Cout << "Total number of integration points: " << num_quad_points << '\n';
258 // Compute the tensor-product grid and store in allSamples
259 tpqDriver->compute_grid(allSamples);
260 if (outputLevel > NORMAL_OUTPUT)
261 print_points_weights("dakota_quadrature_tabular.dat");
262 break;
263 // ------------------------------------------------------------------------
264 // Probabilistic collocation from a tensor grid filtered by product weights
265 // ------------------------------------------------------------------------
266 case FILTERED_TENSOR:
267 Cout << "Filtered to " << numSamples
268 << " samples with max product weight.\n";
269 // Compute the tensor-product grid and store in allSamples
270 tpqDriver->compute_grid(allSamples);
271 // retain a subset of the minimal order tensor grid
272 filter_parameter_sets();
273 break;
274 // ----------------------------------------------------------------------
275 // Probabilistic collocation from random sampling of a tensor multi-index
276 // ----------------------------------------------------------------------
277 case RANDOM_TENSOR: {
278 Cout << numSamples << " samples drawn randomly from tensor grid.\n";
279 allSamples.shapeUninitialized(numContinuousVars, numSamples);
280
281 const Pecos::UShortArray& lev_index = tpqDriver->level_index();
282 tpqDriver->assign_1d_collocation_points_weights(quad_order, lev_index);
283 const Pecos::Real3DArray& colloc_pts_1d
284 = tpqDriver->collocation_points_1d();
285
286 // prevent case of all lhs_const variables, which is an lhs_prep error
287 bool lhs_error_trap = true;
288 for (i=0; i<numContinuousVars; ++i)
289 if (quad_order[i] > 1)
290 { lhs_error_trap = false; break; }
291 if (lhs_error_trap) { // only 1 point from which to draw samples
292 for (i=0; i<numContinuousVars; ++i) {
293 Real samp_i = colloc_pts_1d[0][i][0]; // all levels,indices = 0
294 for (j=0; j<numSamples; ++j)
295 allSamples(i,j) = samp_i;
296 }
297 }
298 else { // sample randomly from the tensor multi-index
299 IntVector index_l_bnds(numContinuousVars), // init to 0
300 index_u_bnds(numContinuousVars, false);
301 for (j=0; j<numContinuousVars; ++j)
302 index_u_bnds[j] = quad_order[j] - 1;
303 IntMatrix sorted_samples;
304 // generate unique samples since redundancy degrades the conditioning
305 Pecos::LHSDriver lhs("lhs", IGNORE_RANKS, false);
306 if (!randomSeed) randomSeed = generate_system_seed();
307 lhs.seed(randomSeed);
308 lhs.generate_uniform_index_samples(index_l_bnds, index_u_bnds, numSamples,
309 sorted_samples, true); // backfill
310
311 // convert multi-index samples into allSamples
312 for (i=0; i<numSamples; ++i){
313 Real* all_samp_i = allSamples[i];
314 int* sorted_samples_i = sorted_samples[i];
315 for (j=0; j<numContinuousVars; ++j)
316 all_samp_i[j] = colloc_pts_1d[lev_index[j]][j][sorted_samples_i[j]];
317 }
318 }
319 break;
320 }
321 }
322 }
323
324
filter_parameter_sets()325 void NonDQuadrature::filter_parameter_sets()
326 {
327 size_t i, num_tensor_pts = allSamples.numCols();
328 const Pecos::RealVector& wts = tpqDriver->type1_weight_sets();
329 #ifdef DEBUG
330 Cout << "allSamples pre-filter:" << allSamples
331 << "weights pre-filter:\n" << wts;
332 #endif // DEBUG
333 // sort TPQ points in order of descending weight
334 std::multimap<Real, RealVector> ordered_pts;
335 for (i=0; i<num_tensor_pts; ++i) {
336 RealVector col_i(Teuchos::Copy, allSamples[i], numContinuousVars);
337 ordered_pts.insert(std::pair<Real, RealVector>(-std::abs(wts[i]), col_i));
338 }
339 // truncate allSamples to the first numSamples with largest weight
340 allSamples.reshape(numContinuousVars, numSamples);
341 std::multimap<Real, RealVector>::iterator it;
342 for (i=0, it=ordered_pts.begin(); i<numSamples; ++i, ++it)
343 Teuchos::setCol(it->second, (int)i, allSamples);
344 #ifdef DEBUG
345 Cout << "allSamples post-filter:" << allSamples;
346 #endif // DEBUG
347 }
348
349
350 /** used by DataFitSurrModel::build_global() to publish the minimum
351 number of points needed from the quadrature routine in order to
352 build a particular global approximation. */
353 void NonDQuadrature::
sampling_reset(int min_samples,bool all_data_flag,bool stats_flag)354 sampling_reset(int min_samples, bool all_data_flag, bool stats_flag)
355 {
356 // tpqDriver tracks the active model key
357 UShortArray rqo = tpqDriver->reference_quadrature_order();
358 while (tpqDriver->grid_size() < min_samples) {
359 if (dimPrefSpec.empty()) increment_grid(rqo);
360 else increment_grid_preference(dimPrefSpec, rqo);
361 }
362
363 if (min_samples > numSamples)
364 numSamples = min_samples;
365
366 /* Old:
367 if (tpqDriver->grid_size() < min_samples) {
368 UShortArray dqo_l_bnd; // isotropic or anisotropic based on dimPrefSpec
369 compute_minimum_quadrature_order(min_samples, dimPrefSpec, dqo_l_bnd);
370 // enforce lower bound
371 UShortArray new_dqo(numContinuousVars);
372 for (size_t i=0; i<numContinuousVars; ++i)
373 new_dqo[i] = std::max(dqo_l_bnd[i], dimQuadOrderRef[i]);
374 // update tpqDriver
375 tpqDriver->reference_quadrature_order(new_dqo, nestedRules);
376 }
377 */
378
379 // not currently used by this class:
380 //allDataFlag = all_data_flag;
381 //statsFlag = stats_flag;
382 }
383
384
increment_grid(UShortArray & ref_quad_order)385 void NonDQuadrature::increment_grid(UShortArray& ref_quad_order)
386 {
387 // Used for uniform refinement: all quad orders are incremented by 1
388 if (nestedRules) {
389 // define reference point
390 size_t orig_size = tpqDriver->grid_size();
391 // initial increment and nestedness enforcement
392 increment_reference_quadrature_order(ref_quad_order);
393 // ensure change in presence of restricted growth
394 while (tpqDriver->grid_size() == orig_size)
395 increment_reference_quadrature_order(ref_quad_order);
396 }
397 else
398 increment_reference_quadrature_order(ref_quad_order);
399
400 if (outputLevel >= DEBUG_OUTPUT)
401 Cout << "Incremented quadrature order:\n" << tpqDriver->quadrature_order();
402 }
403
404
405 void NonDQuadrature::
increment_grid_preference(const RealVector & dim_pref,UShortArray & ref_quad_order)406 increment_grid_preference(const RealVector& dim_pref,
407 UShortArray& ref_quad_order)
408 {
409 // Used for dimension-adaptive refinement: order lower bounds are enforced
410 // using ref_quad_order such that anisotropy may not reduce dimension
411 // resolution once grids have been resolved (as with SparseGridDriver::
412 // axisLowerBounds in NonDSparseGrid).
413 if (nestedRules) {
414 // define reference point
415 size_t orig_size = tpqDriver->grid_size();
416 // initial increment, anisotropy update, and nestedness enforcement
417 increment_reference_quadrature_order(dim_pref, ref_quad_order);
418 // ensure change in presence of restricted growth
419 while (tpqDriver->grid_size() == orig_size)
420 increment_reference_quadrature_order(dim_pref, ref_quad_order);
421 }
422 else
423 increment_reference_quadrature_order(dim_pref, ref_quad_order);
424
425 if (outputLevel >= DEBUG_OUTPUT)
426 Cout << "Incremented quadrature order:\n" << tpqDriver->quadrature_order();
427 }
428
429
430 void NonDQuadrature::
increment_reference_quadrature_order(UShortArray & ref_quad_order)431 increment_reference_quadrature_order(UShortArray& ref_quad_order)
432 {
433 // increment uniformly by 1 (no growth rule is currently enforced,
434 // but could be desirable for weak nesting of symmetric rules)
435 for (size_t i=0; i<numContinuousVars; ++i)
436 ref_quad_order[i] += 1;
437
438 tpqDriver->reference_quadrature_order(ref_quad_order, nestedRules);
439 }
440
441
442 void NonDQuadrature::
increment_reference_quadrature_order(const RealVector & dim_pref,UShortArray & ref_quad_order)443 increment_reference_quadrature_order(const RealVector& dim_pref,
444 UShortArray& ref_quad_order)
445 {
446 // determine the dimension with max preference
447 Real max_dim_pref = dim_pref[0]; size_t max_dim_pref_index = 0;
448 for (size_t i=1; i<numContinuousVars; ++i)
449 if (dim_pref[i] > max_dim_pref)
450 { max_dim_pref = dim_pref[i]; max_dim_pref_index = i; }
451 // increment only the dimension with max preference by 1
452 ref_quad_order[max_dim_pref_index] += 1;
453 // now balance the other dims relative to this new increment, preserving
454 // previous resolution
455 update_anisotropic_order(dim_pref, ref_quad_order);
456
457 tpqDriver->reference_quadrature_order(ref_quad_order, nestedRules);
458 }
459
460
461 void NonDQuadrature::
update_anisotropic_order(const RealVector & dim_pref,UShortArray & ref_quad_order)462 update_anisotropic_order(const RealVector& dim_pref,
463 UShortArray& ref_quad_order)
464 {
465 // Logic is loosely patterned after anisotropic SSG in which the dominant
466 // dimension is constrained by the current ssgLevel and all nondominant
467 // dimensions are scaled back.
468 unsigned short max_quad_ref = ref_quad_order[0];
469 Real max_dim_pref = dim_pref[0]; size_t max_dim_pref_index = 0;
470 for (size_t i=1; i<numContinuousVars; ++i) {
471 if (ref_quad_order[i] > max_quad_ref)
472 max_quad_ref = ref_quad_order[i];
473 if (dim_pref[i] > max_dim_pref)
474 { max_dim_pref = dim_pref[i]; max_dim_pref_index = i; }
475 }
476 // There are several different options for the following operation. Initial
477 // choice is to preserve the current (recently incremented) ref_quad_order
478 // entry in the dimension with max_dim_pref and to compute the others relative
479 // to max_quad_ref (whose dimension may differ from max_dim_pref). Could also
480 // decide to set max_quad_ref in the max_dim_pref dimension, but this could be
481 // a much larger increment. In all cases, the current ref_quad_order is used
482 // as a lower bound to prevent reducing previous resolution.
483 for (size_t i=0; i<numContinuousVars; ++i)
484 if (i != max_dim_pref_index)
485 ref_quad_order[i] = std::max(ref_quad_order[i],
486 (unsigned short)(max_quad_ref*dim_pref[i]/max_dim_pref)); // truncate
487
488 // When used as a stand-alone update:
489 //tpqDriver->reference_quadrature_order(ref_quad_order, nestedRules);
490 }
491
492 } // namespace Dakota
493