Doxygen 1.9.1
Toolkit for Adaptive Stochastic Modeling and Non-Intrusive ApproximatioN: Tasmanian v8.2 (development)
tsgExoticQuadrature.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2021, Miroslav Stoyanov & William Kong
3  *
4  * This file is part of
5  * Toolkit for Adaptive Stochastic Modeling And Non-Intrusive ApproximatioN: TASMANIAN
6  *
7  * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8  * conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
13  * and the following disclaimer in the documentation and/or other materials provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse
16  * or promote products derived from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
19  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
21  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
22  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24  * POSSIBILITY OF SUCH DAMAGE.
25  *
26  * UT-BATTELLE, LLC AND THE UNITED STATES GOVERNMENT MAKE NO REPRESENTATIONS AND DISCLAIM ALL WARRANTIES, BOTH EXPRESSED AND
27  * IMPLIED. THERE ARE NO EXPRESS OR IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, OR THAT THE USE
28  * OF THE SOFTWARE WILL NOT INFRINGE ANY PATENT, COPYRIGHT, TRADEMARK, OR OTHER PROPRIETARY RIGHTS, OR THAT THE SOFTWARE WILL
29  * ACCOMPLISH THE INTENDED RESULTS OR THAT THE SOFTWARE OR ITS USE WILL NOT RESULT IN INJURY OR DAMAGE. THE USER ASSUMES
30  * RESPONSIBILITY FOR ALL LIABILITIES, PENALTIES, FINES, CLAIMS, CAUSES OF ACTION, AND COSTS AND EXPENSES, CAUSED BY, RESULTING
31  * FROM OR ARISING OUT OF, IN WHOLE OR IN PART THE USE, STORAGE OR DISPOSAL OF THE SOFTWARE.
32  */
33 
34 #ifndef __TASMANIAN_ADDONS_EXOTICQUADRATURE_HPP
35 #define __TASMANIAN_ADDONS_EXOTICQUADRATURE_HPP
36 
47 #include "tsgAddonsCommon.hpp"
48 
81 namespace TasGrid {
82 
90 static int gauss_quadrature_version = 1;
91 
100 inline double poly_eval(const std::vector<double> &roots, double x) {
101  double eval_value = 1.0;
102  for (auto r : roots) eval_value *= (x - r);
103  return eval_value;
104 }
105 
117 inline double lagrange_eval(size_t idx, const std::vector<double> &roots, double x) {
118  double eval_value = 1.0;
119  for (size_t i=0; i<idx; i++) {
120  eval_value *= (x - roots[i]) / (roots[idx] - roots[i]);
121  }
122  for (size_t i=idx+1; i<roots.size(); i++) {
123  eval_value *= (x - roots[i]) / (roots[idx] - roots[i]);
124  }
125  return eval_value;
126 }
127 
148 template <bool is_symmetric>
149 inline void getGaussNodesAndWeights(const int n, const std::vector<double> &ref_points, const std::vector<double> &ref_weights,
150  std::vector<std::vector<double>> &points_cache, std::vector<std::vector<double>> &weights_cache) {
151  #ifndef Tasmanian_ENABLE_BLAS
152  // If BLAS is not enabled, we must use an implementation that does not require it.
153  if (gauss_quadrature_version == 1) {
154  gauss_quadrature_version = 2;
155  }
156  #endif
157  // Compute the roots incrementally.
158  assert(ref_points.size() == ref_weights.size());
159  assert(ref_points.size() > 0);
160  double mu0 = 0.0;
161  for (auto &w : ref_weights) mu0 += w;
162  std::vector<double> poly_m1_vals(ref_points.size(), 0.0), poly_vals(ref_points.size(), 1.0);
163  std::vector<double> diag, offdiag;
164  points_cache.resize(n);
165  weights_cache.resize(n);
166  for (int i=0; i<n; i++) {
167  // Form the diagonal and offdiagonal vectors of the Jacobi matrix.
168  if (is_symmetric) {
169  diag.push_back(0.0);
170  } else {
171  double diag_numr = 0.0;
172  double diag_denm = 0.0;
173  for (size_t j=0; j<ref_points.size(); j++) {
174  diag_numr += ref_points[j] * (poly_vals[j] * poly_vals[j]) * ref_weights[j];
175  diag_denm += (poly_vals[j] * poly_vals[j]) * ref_weights[j];
176  }
177  diag.push_back(diag_numr / diag_denm);
178  }
179  if (i >= 1) {
180  double sqr_offdiag_numr = 0.0;
181  double sqr_offdiag_denm = 0.0;
182  for (size_t j=0; j<ref_points.size(); j++) {
183  sqr_offdiag_numr += (poly_vals[j] * poly_vals[j]) * ref_weights[j];
184  sqr_offdiag_denm += (poly_m1_vals[j] * poly_m1_vals[j]) * ref_weights[j];
185  }
186  offdiag.push_back(std::sqrt(sqr_offdiag_numr / sqr_offdiag_denm));
187  }
188  // Compute the Gauss points and weights.
189  if (gauss_quadrature_version == 1) {
190  // Use LAPACK to obtain the Gauss points, and use the reference points and weights to obtain the Gauss weights.
191  // Note that the matrix is always symmetric even if the weights function is not.
192  points_cache[i] = TasmanianTridiagonalSolver::getSymmetricEigenvalues(i+1, diag, offdiag);
193  if (points_cache[i].size() % 2 == 1 && is_symmetric) {
194  points_cache[i][(points_cache[i].size() - 1) / 2] = 0.0;
195  }
196  weights_cache[i] = std::vector<double>(points_cache[i].size(), 0.0);
197  for (size_t j=0; j<points_cache[i].size(); j++) {
198  // Integrate the function l_j(x) according to the measure induced by the function [weight_fn(x) + shift].
199  for (size_t k=0; k<ref_points.size(); k++) {
200  weights_cache[i][j] += lagrange_eval(j, points_cache[i], ref_points[k]) * ref_weights[k];
201  }
202  }
203  } else if (gauss_quadrature_version == 2) {
204  // Use TasmanianTridiagonalSolver::decompose().
205  std::vector<double> dummy_diag = diag;
206  std::vector<double> dummy_offdiag = offdiag;
207  TasmanianTridiagonalSolver::decompose(dummy_diag, dummy_offdiag, mu0, points_cache[i], weights_cache[i]);
208  } else {
209  throw std::invalid_argument("ERROR: gauss_quadrature_version must be a valid number!");
210  }
211  // Update the values of the polynomials at ref_points.
212  std::swap(poly_m1_vals, poly_vals);
213  std::transform(ref_points.begin(), ref_points.end(), poly_vals.begin(),
214  [&points_cache, i](double x){return poly_eval(points_cache[i], x);});
215  }
216 }
217 
233 inline TasGrid::CustomTabulated getShiftedExoticQuadrature(const int n, const double shift, const std::vector<double> &shifted_weights,
234  const std::vector<double> &ref_points, const char* description,
235  const bool is_symmetric = false) {
236  // Create the set of points (roots) and weights for the first term.
237  assert(shifted_weights.size() == ref_points.size());
238  std::vector<std::vector<double>> points_cache, weights_cache;
239  if (is_symmetric) {
240  getGaussNodesAndWeights<true>(n, ref_points, shifted_weights, points_cache, weights_cache);
241  } else {
242  getGaussNodesAndWeights<false>(n, ref_points, shifted_weights, points_cache, weights_cache);
243  }
244 
245  // Create and append the set of correction points and weights for the second term only if the shift is nonzero.
246  if (shift != 0.0) {
247  for (int i=0; i<n; i++) {
248  const size_t init_size = points_cache[i].size();
249  std::vector<double> correction_points, correction_weights;
250  TasGrid::OneDimensionalNodes::getGaussLegendre(static_cast<int>(init_size), correction_weights, correction_points);
251  for (auto &w : correction_weights) w *= -shift;
252  if (correction_points.size() % 2 == 1 && is_symmetric) {
253  // Zero out for stability.
254  correction_points[(correction_points.size() - 1) / 2] = 0.0;
255  }
256  // Account for duplicates up to a certain tolerance.
257  for (size_t j=0; j<correction_points.size(); j++) {
258  long long nonunique_idx = -1;
259  for (size_t k=0; k<init_size; k++) {
260  if (std::abs(correction_points[j] - points_cache[i][k]) <= Maths::num_tol) {
261  nonunique_idx = static_cast<long long>(k);
262  break;
263  }
264  }
265  if (nonunique_idx == -1) {
266  points_cache[i].push_back(correction_points[j]);
267  weights_cache[i].push_back(correction_weights[j]);
268  } else {
269  weights_cache[i][nonunique_idx] += correction_weights[j];
270  }
271  }
272  }
273  }
274 
275  // Output to a CustomTabulated instance.
276  std::vector<int> num_nodes(n), precision(n);
277  for (int i=0; i<n; i++) {
278  num_nodes[i] = static_cast<int>(points_cache[i].size());
279  precision[i] = 2 * (i + 1) - 1;
280  }
281  return TasGrid::CustomTabulated(std::move(num_nodes), std::move(precision), std::move(points_cache), std::move(weights_cache),
282  description);
283 }
284 
298 inline void shiftReferenceWeights(std::vector<double> const &vals, double shift, std::vector<double> &ref_weights){
299  assert(ref_weights.size() == vals.size());
300  if (not std::all_of(vals.begin(), vals.end(), [shift](double x){return (x+shift)>=0.0;}))
301  throw std::invalid_argument("ERROR: shift needs to be chosen so that weight_fn(x)+shift is nonnegative!");
302  for (size_t k=0; k<ref_weights.size(); k++)
303  ref_weights[k] *= (vals[k] + shift);
304 }
305 
335 inline TasGrid::CustomTabulated getExoticQuadrature(const int num_levels, const double shift, const TasGrid::TasmanianSparseGrid &grid,
336  const char* description, const bool is_symmetric = false) {
337  if (grid.getNumLoaded() == 0) {
338  throw std::invalid_argument("ERROR: grid needs to contain a nonzero number of loaded points returned by grid.getLoadedPoints()!");
339  }
340  if (grid.getNumDimensions() != 1) {
341  throw std::invalid_argument("ERROR: grid needs to be one dimensional!");
342  }
343  if (grid.getNumOutputs() != 1) {
344  throw std::invalid_argument("ERROR: grid needs to have scalar outputs!");
345  }
346  std::vector<double> ref_points = grid.getLoadedPoints();
347  std::vector<double> shifted_weights = grid.getQuadratureWeights();
348  std::vector<double> weight_fn_vals = std::vector<double>(grid.getLoadedValues(), grid.getLoadedValues() + grid.getNumLoaded());
349  shiftReferenceWeights(weight_fn_vals, shift, shifted_weights);
350  return getShiftedExoticQuadrature(num_levels, shift, shifted_weights, ref_points, description, is_symmetric);
351 }
352 
362 inline TasGrid::CustomTabulated getExoticQuadrature(const int num_levels, const double shift, std::function<double(double)> weight_fn,
363  const int num_ref_points, const char* description, const bool is_symmetric = false) {
364  std::vector<double> shifted_weights, ref_points, weight_fn_vals(num_ref_points);
365  TasGrid::OneDimensionalNodes::getGaussLegendre(num_ref_points, shifted_weights, ref_points);
366  std::transform(ref_points.begin(), ref_points.end(), weight_fn_vals.begin(), weight_fn);
367  shiftReferenceWeights(weight_fn_vals, shift, shifted_weights);
368  return getShiftedExoticQuadrature(num_levels, shift, shifted_weights, ref_points, description, is_symmetric);
369 }
370 
371 } // namespace(TasGrid)
372 #endif
Class providing manipulation of custom tabulated rules, file I/O and structured access to the points,...
Definition: tsgCoreOneDimensional.hpp:66
The master-class that represents an instance of a Tasmanian sparse grid.
Definition: TasmanianSparseGrid.hpp:293
int getNumOutputs() const
Return the outputs of the grid, i.e., number of model outputs.
Definition: TasmanianSparseGrid.hpp:644
const double * getLoadedValues() const
Returns the model values that have been loaded in the gird.
Definition: TasmanianSparseGrid.hpp:899
int getNumLoaded() const
Return the number of points already associated with model values via loadNeededValues().
Definition: TasmanianSparseGrid.hpp:657
std::vector< double > getLoadedPoints() const
Return the points already associated with model values.
Definition: TasmanianSparseGrid.hpp:671
int getNumDimensions() const
Return the dimensions of the grid, i.e., number of model inputs.
Definition: TasmanianSparseGrid.hpp:642
std::vector< double > getQuadratureWeights() const
Returns a vector of size getNumPoints() of the quadrature weights of the grid.
Definition: TasmanianSparseGrid.hpp:747
TasGrid::CustomTabulated getExoticQuadrature(const int num_levels, const double shift, const TasGrid::TasmanianSparseGrid &grid, const char *description, const bool is_symmetric=false)
Constructs an exotic quadrature from a sparse grid object.
Definition: tsgExoticQuadrature.hpp:335
double lagrange_eval(size_t idx, const std::vector< double > &roots, double x)
Evaluates a Lagrange basis polynomial at a point x.
Definition: tsgExoticQuadrature.hpp:117
void getGaussNodesAndWeights(const int n, const std::vector< double > &ref_points, const std::vector< double > &ref_weights, std::vector< std::vector< double >> &points_cache, std::vector< std::vector< double >> &weights_cache)
Generates n levels of points and weights using orthogonal polynomials with respect to ref_points and ...
Definition: tsgExoticQuadrature.hpp:149
void shiftReferenceWeights(std::vector< double > const &vals, double shift, std::vector< double > &ref_weights)
Multiplies the reference quadrature weights by the shifted values of the weight function.
Definition: tsgExoticQuadrature.hpp:298
TasGrid::CustomTabulated getShiftedExoticQuadrature(const int n, const double shift, const std::vector< double > &shifted_weights, const std::vector< double > &ref_points, const char *description, const bool is_symmetric=false)
Generate the exotic quadrature points and weights and load them into a TasGrid::CustomTabulated objec...
Definition: tsgExoticQuadrature.hpp:233
double poly_eval(const std::vector< double > &roots, double x)
Evaluates a polynomial with roots given by roots at the point x.
Definition: tsgExoticQuadrature.hpp:100
constexpr double num_tol
Numerical tolerance for various algorithms.
Definition: tsgMathUtils.hpp:132
void getGaussLegendre(int m, std::vector< double > &w, std::vector< double > &x)
Generate Gauss-Legendre weights w and points x for (input) number of points m.
void decompose(std::vector< double > &diag, std::vector< double > &off_diag, const double mu0, std::vector< double > &nodes, std::vector< double > &weights)
Method for tridiagonal eigenvalue decomposition, used to compute nodes and weights for Gaussian rules...
std::vector< double > getSymmetricEigenvalues(int n, std::vector< double > const &diag, std::vector< double > const &offdiag)
Method for computing the eigenvalues of a symmetric matrix in place, using an LAPACK wrapper.
Encapsulates the Tasmanian Sparse Grid module.
Definition: TasmanianSparseGrid.hpp:68
Common includes and methods for all addons.