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