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: InterpPolyApproximation
10 //- Description: Implementation code for InterpPolyApproximation class
11 //-
12 //- Owner: Mike Eldred
13
14 #include "InterpPolyApproximation.hpp"
15 #include "SharedInterpPolyApproxData.hpp"
16
17 //#define DEBUG
18
19 namespace Pecos {
20
21
min_coefficients() const22 int InterpPolyApproximation::min_coefficients() const
23 {
24 // return the minimum number of data instances required to build the
25 // surface of multiple dimensions
26 return (expansionCoeffFlag || expansionCoeffGradFlag) ? 1 : 0;
27 }
28
29
allocate_arrays()30 void InterpPolyApproximation::allocate_arrays()
31 {
32 std::shared_ptr<SharedInterpPolyApproxData> data_rep =
33 std::static_pointer_cast<SharedInterpPolyApproxData>(sharedDataRep);
34 update_active_iterators(data_rep->activeKey);
35
36 allocate_total_sobol();
37 allocate_component_sobol();
38
39 size_t num_moments = (data_rep->nonRandomIndices.empty()) ? 4 : 2;
40 RealVector& num_int_mom = primaryMomIter->second;
41 if (num_int_mom.length() != num_moments)
42 num_int_mom.sizeUninitialized(num_moments);
43 }
44
45
test_interpolation()46 void InterpPolyApproximation::test_interpolation()
47 {
48 // SC should accurately interpolate the collocation data for TPQ and
49 // SSG with fully nested rules, but will exhibit interpolation error
50 // for SSG with other rules.
51 if (expansionCoeffFlag) {
52 std::shared_ptr<SharedPolyApproxData> data_rep =
53 std::static_pointer_cast<SharedPolyApproxData>(sharedDataRep);
54 bool use_derivs = data_rep->basisConfigOptions.useDerivs;
55
56 const SDVArray& sdv_array = surrData.variables_data();
57 const SDRArray& sdr_array = surrData.response_data();
58
59 size_t i, index = 0, num_colloc_pts = surrData.points(),
60 num_v = sharedDataRep->numVars, w7 = WRITE_PRECISION+7;
61 Real interp_val, err, val_max_err = 0., grad_max_err = 0.,
62 val_rmse = 0., grad_rmse = 0.;
63 PCout << std::scientific << std::setprecision(WRITE_PRECISION);
64 for (i=0; i<num_colloc_pts; ++i, ++index) {
65 const RealVector& c_vars = sdv_array[index].continuous_variables();
66 const SurrogateDataResp& sdr = sdr_array[index];
67 Real resp_fn = sdr.response_function();
68 interp_val = value(c_vars);
69 err = (std::abs(resp_fn) > DBL_MIN) ? std::abs(1. - interp_val/resp_fn) :
70 std::abs(resp_fn - interp_val);
71 PCout << "Colloc pt " << std::setw(3) << i+1
72 << ": truth value = " << std::setw(w7) << resp_fn
73 << " interpolant = " << std::setw(w7) << interp_val
74 << " relative error = " << std::setw(w7) << err <<'\n';
75 if (err > val_max_err) val_max_err = err;
76 val_rmse += err * err;
77 if (use_derivs) {
78 const RealVector& resp_grad = sdr.response_gradient();
79 const RealVector& interp_grad = gradient_basis_variables(c_vars);
80 for (size_t j=0; j<num_v; ++j) {
81 err = (std::abs(resp_grad[j]) > DBL_MIN) ?
82 std::abs(1. - interp_grad[j]/resp_grad[j]) :
83 std::abs(resp_grad[j] - interp_grad[j]);
84 PCout << " " << "truth grad_" << j+1 << " = "
85 << std::setw(w7) << resp_grad[j] << " interpolant = "
86 << std::setw(w7) << interp_grad[j] << " relative error = "
87 << std::setw(w7) << err << '\n';
88 if (err > grad_max_err) grad_max_err = err;
89 grad_rmse += err * err;
90 }
91 }
92 }
93 val_rmse = std::sqrt(val_rmse/num_colloc_pts);
94 PCout << "\nValue interpolation errors: " << std::setw(w7) << val_max_err
95 << " (max) " << std::setw(w7) << val_rmse << " (RMS)\n";
96 if (use_derivs) {
97 grad_rmse = std::sqrt(grad_rmse/num_colloc_pts/num_v);
98 PCout << "Gradient interpolation errors: " << std::setw(w7)
99 << grad_max_err << " (max) " << std::setw(w7) << grad_rmse
100 << " (RMS)\n";
101 }
102 }
103 }
104
105
compute_component_sobol()106 void InterpPolyApproximation::compute_component_sobol()
107 {
108 // initialize partialVariance
109 size_t sobol_len = sobolIndices.length();
110 if (partialVariance.length() != sobol_len) partialVariance.size(sobol_len);
111 else partialVariance = 0.;
112
113 // Compute the total expansion mean & variance. For standard mode, these are
114 // likely already available, as managed by computed{Mean,Variance} data reuse
115 // trackers. For all vars mode, they are computed without partial integration
116 // restricted to the random indices.
117 Real total_variance = variance();
118 if (total_variance > SMALL_NUMBER) { // Solve for partial variances
119 Real total_mean = mean();
120 // 0th term gets subtracted as child in compute_partial_variance()
121 partialVariance[0] = total_mean * total_mean;
122 // compute the partial variances corresponding to Sobol' indices
123 std::shared_ptr<SharedInterpPolyApproxData> data_rep =
124 std::static_pointer_cast<SharedInterpPolyApproxData>(sharedDataRep);
125 const BitArrayULongMap& index_map = data_rep->sobolIndexMap;
126 for (BAULMCIter cit=index_map.begin(); cit!=index_map.end(); ++cit) {
127 unsigned long index = cit->second;
128 if (index) { // partialVariance[0] stores mean^2 offset
129 compute_partial_variance(cit->first);
130 sobolIndices[index] = partialVariance[index] / total_variance;
131 }
132 }
133 }
134 else // don't perform variance attribution for zero/negligible variance
135 sobolIndices = 0.;
136 #ifdef DEBUG
137 PCout << "In InterpPolyApproximation::compute_component_sobol(), "
138 << "sobolIndices =\n" << sobolIndices;
139 #endif // DEBUG
140 }
141
142
compute_total_sobol()143 void InterpPolyApproximation::compute_total_sobol()
144 {
145 totalSobolIndices = 0.; // init total indices
146
147 std::shared_ptr<SharedInterpPolyApproxData> data_rep =
148 std::static_pointer_cast<SharedInterpPolyApproxData>(sharedDataRep);
149 if (data_rep->expConfigOptions.vbdOrderLimit)
150 // all component indices may not be available, so compute total indices
151 // independently. This approach parallels partial_variance_integral()
152 // where the algorithm is separated by integration approach.
153 compute_total_sobol_indices(); // virtual
154 else {
155 // all component indices are available, so add them up: totalSobolIndices
156 // simply parses the bit sets of each of the sobolIndices and adds them to
157 // each matching variable bin
158 size_t k, num_v = sharedDataRep->numVars;
159 const BitArrayULongMap& index_map = data_rep->sobolIndexMap;
160 for (BAULMCIter cit=index_map.begin(); cit!=index_map.end(); ++cit)
161 for (k=0; k<num_v; ++k)
162 if (cit->first[k]) // var k is present in this Sobol' index
163 totalSobolIndices[k] += sobolIndices[cit->second];
164 // ensure non-negativity of indices
165 //for (k=0; k<num_v; ++k)
166 // totalSobolIndices[k] = std::abs(totalSobolIndices[k]);
167 }
168
169 #ifdef DEBUG
170 PCout << "In InterpPolyApproximation::compute_total_sobol(), "
171 << "totalSobolIndices =\n" << totalSobolIndices;
172 #endif // DEBUG
173 }
174
175
176 /** Computes the variance of component functions. Assumes that partial
177 variances of all subsets of set_value have been computed in advance:
178 compute_component_sobol() calls compute_partial_variance() using
179 the ordered set_value's in sobolIndexMap. */
180 void InterpPolyApproximation::
compute_partial_variance(const BitArray & set_value)181 compute_partial_variance(const BitArray& set_value)
182 {
183 // derived classes override to compute partialVariance and then invoke
184 // base version for post-processing of proper subsets
185
186 // compute child subsets. An alternate approach would be to iterate
187 // over sobolIndexMap using it->is_proper_subset_of(set_value).
188 BitArraySet children;
189 proper_subsets(set_value, children);
190
191 std::shared_ptr<SharedInterpPolyApproxData> data_rep =
192 std::static_pointer_cast<SharedInterpPolyApproxData>(sharedDataRep);
193 BitArrayULongMap& index_map = data_rep->sobolIndexMap;
194
195 // index of parent set within sobolIndices and partialVariance
196 unsigned long set_index;
197 if (!children.empty())
198 set_index = index_map[set_value];
199
200 // subtract the contributions from child subsets. partialVariance
201 // calculations are computed by ordered traversal of sobolIndexMap, which
202 // uses BitArray keys that sort lexicographically (in ascending order of
203 // the corresponding unsigned integer) --> all proper subsets are available.
204 for (BASIter it=children.begin(); it!=children.end(); ++it) {
205 unsigned long subset_index = index_map[*it];
206 partialVariance[set_index] -= partialVariance[subset_index];
207 }
208 }
209
210
211 /** For input parent set, recursively finds constituent child subsets
212 with one fewer element */
213 void InterpPolyApproximation::
proper_subsets(const BitArray & parent_set,BitArraySet & children)214 proper_subsets(const BitArray& parent_set, BitArraySet& children)
215 {
216 size_t k, num_v = sharedDataRep->numVars;
217 for (k=0; k<num_v; ++k)
218 if (parent_set[k]) { // check for membership of variable k in parent set
219 // remove var k from parent set to create child set
220 BitArray child_set = parent_set; child_set.flip(k);
221 // if child set has not been stored previously, insert it and recurse
222 if (children.find(child_set) == children.end()) {
223 children.insert(child_set);
224 proper_subsets(child_set, children); // recurse until {0} is reached
225 }
226 }
227 }
228
229 } // namespace Pecos
230