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: LagrangeInterpPolynomial
10 //- Description: Implementation code for LagrangeInterpPolynomial class
11 //-
12 //- Owner: Mike Eldred, Sandia National Laboratories
13
14 #include "LagrangeInterpPolynomial.hpp"
15
16
17 namespace Pecos {
18
19 /** Pre-compute denominator products that are only a function of the
20 interpolation points. */
precompute_data()21 void LagrangeInterpPolynomial::precompute_data()
22 {
23 // precompute w_j for all forms of Lagrange interpolation
24 size_t i, j, num_interp_pts = interpPts.size(); Real prod;
25 if (bcWeights.empty())
26 bcWeights.sizeUninitialized(num_interp_pts);
27 for (i=0; i<num_interp_pts; ++i) {
28 prod = 1.;
29 Real interp_pt_i = interpPts[i];
30 for (j=0; j<num_interp_pts; ++j)
31 if (i != j)
32 prod *= interp_pt_i - interpPts[j];
33 bcWeights[i] = 1. / prod; // prefer repeated multiplication to division
34 }
35 }
36
37
38 /** Shared initialization code. */
39 void LagrangeInterpPolynomial::
init_new_point(Real x,short request_order,short & compute_order)40 init_new_point(Real x, short request_order, short& compute_order)
41 {
42 // define compute_order from requested and available data. If grad factors
43 // are requested, value factors will also be needed (multidimensional grad
44 // components involve values in non-differentiated dimensions).
45 if (x == newPoint) {
46 compute_order = request_order - (newPtOrder & request_order);
47 if (request_order == 2 && !(newPtOrder & 1))
48 compute_order |= 1; // augment request
49 if (compute_order) newPtOrder |= compute_order; // augment total available
50 else return;
51 }
52 else {
53 newPtOrder = compute_order = (request_order & 2) ? 3 : request_order;
54 newPoint = x; exactIndex = exactDeltaIndex = _NPOS;
55 }
56 }
57
58
59 /** Define the bcValueFactors (and exactIndex if needed) corresponding to x. */
set_new_point(Real x,short request_order)60 void LagrangeInterpPolynomial::set_new_point(Real x, short request_order)
61 {
62 short compute_order;
63 init_new_point(x, request_order, compute_order);
64 allocate_factors(compute_order);
65
66 // second form of barycentric interpolation: precompute value factors and
67 // grad factor terms or identify exactIndex
68 size_t j, num_interp_pts = interpPts.size();
69 Real diff_inv_sum; RealVector diffs;
70 if (exactIndex == _NPOS) { // exact match may have been previously detected
71 // first, compute diffs vector or exactIndex
72 diffs.sizeUninitialized(num_interp_pts);
73 for (j=0; j<num_interp_pts; ++j) {
74 diffs[j] = newPoint - interpPts[j];
75 if (diffs[j] == 0.) // no tolerance needed due to favorable stability
76 { exactIndex = exactDeltaIndex = j; break; }
77 }
78
79 // now compute value factors and grad factor terms based on exactIndex
80 if (exactIndex == _NPOS) {
81 if (compute_order & 1) bcValueFactorSum = 0.;
82 if (compute_order & 2) { diffProduct = 1.; diff_inv_sum = 0.; }
83 for (j=0; j<num_interp_pts; ++j) {
84 if (compute_order & 1)
85 bcValueFactorSum += bcValueFactors[j] = bcWeights[j] / diffs[j];
86 if (compute_order & 2)
87 { diffProduct *= diffs[j]; diff_inv_sum += 1. / diffs[j]; }
88 }
89 }
90 }
91 // catch previous or current identification of exactIndex
92 if (exactIndex != _NPOS && (compute_order & 1)) {
93 bcValueFactors = 0.; bcValueFactors[exactIndex] = 1.;
94 //bcValueFactorSum = 1.; // not currently used if exactIndex
95 }
96
97 // second form of barycentric interpolation: precompute gradient factors
98 if (compute_order & 2) {
99 // bcGradFactors (like bcValueFactors) differ from the actual gradient
100 // values by diffProduct, which gets applied after a tensor summation
101 // using the barycentric gradient scaling.
102 if (exactIndex == _NPOS)
103 for (j=0; j<num_interp_pts; ++j) // bcValueFactors must be available
104 bcGradFactors[j] = bcValueFactors[j] // * diffProduct
105 * (diff_inv_sum - 1. / diffs[j]); // back out jth inverse diff
106 else { // Berrut and Trefethen, 2004
107 // for this case, bcGradFactors are the actual gradient values
108 // and no diffProduct scaling needs to be subsequently applied
109 bcGradFactors[exactIndex] = 0.;
110 for (j=0; j<num_interp_pts; ++j)
111 if (j != exactIndex)
112 bcGradFactors[exactIndex] -= bcGradFactors[j] = bcWeights[j]
113 / bcWeights[exactIndex] / (interpPts[exactIndex] - interpPts[j]);
114 }
115 }
116 }
117
118
119 /** Define the bcValueFactors (and exactIndex if needed) corresponding to x. */
120 void LagrangeInterpPolynomial::
set_new_point(Real x,short request_order,const UShortArray & delta_key)121 set_new_point(Real x, short request_order, const UShortArray& delta_key)
122 {
123 short compute_order;
124 // TO DO: capture change in delta_key...
125 // Note: no longer any reuse --> just move code cases here and encapsulate
126 // any lower level shared logic
127 init_new_point(x, request_order, compute_order);//, delta_key);
128 allocate_factors(compute_order);
129
130 // second form of barycentric interpolation: precompute value factors and
131 // grad factor terms or identify exactIndex
132 size_t j, vj, num_interp_pts = interpPts.size(),
133 num_delta_pts = delta_key.size();
134 Real diff_inv_sum; RealVector diffs;
135
136 if (exactIndex == _NPOS) { // exact match may have been previously detected
137 // compute diffs vector and detect exactIndex within all of interpPts
138 diffs.sizeUninitialized(num_interp_pts);
139 for (j=0; j<num_interp_pts; ++j) {
140 diffs[j] = newPoint - interpPts[j];
141 if (diffs[j] == 0.) // no tol reqd due to favorable stability
142 { exactIndex = j; break; }
143 }
144 // detect exactDeltaIndex within delta points only
145 // (see, for example, InterpPolyApproximation::barycentric_exact_index())
146 exactDeltaIndex = (exactIndex == _NPOS) ? _NPOS :
147 find_index(delta_key, exactIndex);
148
149 // now compute value factors and grad factor terms based on exactIndex
150 if (exactIndex == _NPOS) {
151 if (compute_order & 1) bcValueFactorSum = 0.;
152 if (compute_order & 2) { diffProduct = 1.; diff_inv_sum = 0.; }
153 for (j=0; j<num_interp_pts; ++j) {
154 if (compute_order & 1)
155 bcValueFactorSum += bcValueFactors[j] = bcWeights[j] / diffs[j];
156 if (compute_order & 2)
157 { diffProduct *= diffs[j]; diff_inv_sum += 1. / diffs[j]; }
158 }
159 }
160 }
161 // catch previous or current identification of exactIndex
162 if (exactIndex != _NPOS && (compute_order & 1)) {
163 bcValueFactors = 0.; bcValueFactors[exactIndex] = 1.;
164 //bcValueFactorSum = 1.; // not currently used if exactIndex
165 }
166
167 // second form of barycentric interpolation: precompute gradient factors
168 if (compute_order & 2) {
169 // bcGradFactors (like bcValueFactors) differ from the actual gradient
170 // values by diffProduct, which gets applied after a tensor summation
171 // using the barycentric gradient scaling.
172 if (exactIndex == _NPOS)
173 for (j=0; j<num_delta_pts; ++j) { // bcValueFactors must be available
174 vj = delta_key[j];
175 bcGradFactors[vj] = bcValueFactors[vj] // * diffProduct
176 * (diff_inv_sum - 1. / diffs[vj]); // back out vj-th inverse diff
177 }
178 else { // Berrut and Trefethen, 2004
179 // for this case, bcGradFactors are the actual gradient values
180 // and no diffProduct scaling needs to be subsequently applied
181 bcGradFactors[exactIndex] = 0.;
182 for (j=0; j<num_interp_pts; ++j)
183 if (j != exactIndex)
184 bcGradFactors[exactIndex] -= bcGradFactors[j] = bcWeights[j]
185 / bcWeights[exactIndex] / (interpPts[exactIndex] - interpPts[j]);
186 }
187 }
188 }
189
190
191 /** Compute value of the Lagrange polynomial (1st barycentric form)
192 corresponding to interpolation point i using data from previous
193 call to set_new_point(). */
type1_value(unsigned short i)194 Real LagrangeInterpPolynomial::type1_value(unsigned short i)
195 {
196 // second form of the barycentric interpolation formula
197 if (exactIndex == _NPOS) return bcValueFactors[i] * diffProduct;
198 else return (exactIndex == i) ? 1. : 0.;
199
200 /*
201 // first form of the barycentric interpolation formula
202 if (exactIndex == _NPOS)
203 return bcWeights[i] * diffProduct / (newPoint - interpPts[i]);
204 else
205 return (exactIndex == i) ? 1. : 0.;
206 */
207 }
208
209
210 /** Compute derivative with respect to x of the Lagrange polynomial
211 (1st barycentric form) corresponding to interpolation point i
212 using data from previous call to set_new_point(). */
type1_gradient(unsigned short i)213 Real LagrangeInterpPolynomial::type1_gradient(unsigned short i)
214 {
215 // second form of the barycentric interpolation formula
216 return (exactIndex == _NPOS) ?
217 bcGradFactors[i] * diffProduct : bcGradFactors[i];
218
219 /*
220 // first form of the barycentric interpolation formula
221 if (exactIndex == _NPOS) {
222 Real sum = 0.,
223 t1_i = bcWeights[i] * diffProduct / (newPoint - interpPts[i]);
224 size_t j, num_interp_pts = interpPts.size();
225 for (j=0; j<num_interp_pts; ++j)
226 if (j != i)
227 sum += t1_i / (newPoint - interpPts[j]);
228 return sum;
229 }
230 else // double loop fallback
231 return type1_gradient(newPoint, i);
232 */
233 }
234
235
236 /** Compute value of the Lagrange polynomial (traditional characteristic
237 polynomial form) corresponding to interpolation point i. */
type1_value(Real x,unsigned short i)238 Real LagrangeInterpPolynomial::type1_value(Real x, unsigned short i)
239 {
240 size_t j, num_interp_pts = interpPts.size();
241 Real t1_val = bcWeights[i];
242 for (j=0; j<num_interp_pts; ++j)
243 if (i != j)
244 t1_val *= x - interpPts[j];
245 return t1_val;
246 }
247
248
249 /** Compute derivative with respect to x of the Lagrange polynomial (traditional
250 characteristic polynomial form) corresponding to interpolation point i. */
type1_gradient(Real x,unsigned short i)251 Real LagrangeInterpPolynomial::type1_gradient(Real x, unsigned short i)
252 {
253 size_t j, k, num_interp_pts = interpPts.size();
254 Real sum = 0., prod;
255 for (j=0; j<num_interp_pts; ++j) {
256 if (j != i) {
257 prod = 1.;
258 for (k=0; k<num_interp_pts; ++k)
259 if (k != j && k != i)
260 prod *= x - interpPts[k];
261 sum += prod;
262 }
263 }
264 return sum * bcWeights[i];
265 }
266
267 } // namespace Pecos
268