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