Version: SMASH-3.4
interpolation.h
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2015-2018,2020,2022,2024,2026
4  * SMASH Team
5  *
6  * GNU General Public License (GPLv3 or later)
7  *
8  */
9 
10 #ifndef SRC_INCLUDE_SMASH_INTERPOLATION_H_
11 #define SRC_INCLUDE_SMASH_INTERPOLATION_H_
12 
13 #include <algorithm>
14 #include <cassert>
15 #include <cstddef>
16 #include <numeric>
17 #include <sstream>
18 #include <stdexcept>
19 #include <string>
20 #include <utility>
21 #include <vector>
22 
23 #include "gsl/gsl_errno.h"
24 #include "gsl/gsl_spline.h"
25 
26 #include "smash/constants.h"
28 
29 namespace smash {
30 
31 /**
32  * Represent a linear interpolation.
33  *
34  * \param T Type of interpolated values.
35  */
36 template <typename T>
38  public:
39  /**
40  * Linear interpolation given two points (x0, y0) and (x1, y1).
41  *
42  * \return The interpolation function.
43  */
44  InterpolateLinear(T x0, T y0, T x1, T y1);
45 
46  /**
47  * Calculate linear interpolation at x.
48  *
49  * \param x Interpolation argument.
50  * \return Interpolated value.
51  */
52  T operator()(T x) const;
53 
54  private:
55  /// Slope of the linear interpolation.
56  T slope_{};
57  /// y-axis intercept of the linear interpolation.
59 };
60 
61 /**
62  * Represent a piecewise linear interpolation.
63  *
64  * \param T Type of interpolated values.
65  */
66 template <typename T>
68  public:
69  /**
70  * Interpolate function f given discrete samples f(x_i) = y_i.
71  * Piecewise linear interpolation is used.
72  *
73  * \param x x-values.
74  * \param y y-values.
75  * \param extrapolation_type Type of extrapolation for requested x_i values
76  * that are out of bounds. Extrapolation is by
77  * default disabled. Possible types are
78  * <tt>None</tt>, <tt>Zero</tt>, <tt>Constant</tt>,
79  * and <tt>Linear</tt>.
80  *
81  * \return The interpolation function.
82  * \throw std::invalid_argument if vectors x and y have different length.
83  * \throw std::out_of_range if values outside of the boundaries of the
84  * underlying data are tried to be accessed and
85  * extrapolation is disabled.
86  * \throw std::invalid_argument if unsupported extrapolation type is
87  * requested.
88  */
90  const std::vector<T>& x, const std::vector<T>& y,
91  ExtrapolationType extrapolation_type = ExtrapolationType::None);
92 
93  /**
94  * Calculate linear interpolation at x.
95  *
96  * \param x Interpolation argument.
97  * \return Interpolated value.
98  *
99  * \throw std::out_of_range if values outside of the boundaries of the
100  * underlying data are tried to be accessed and
101  * extrapolation is disabled.
102  */
103  T operator()(T x) const;
104 
105  private:
106  /// x_i
107  std::vector<T> x_{};
108  /// Piecewise linear interpolation using f(x_i)
109  std::vector<InterpolateLinear<T>> f_{};
110  /// Extrapolation type
112 };
113 
114 template <typename T>
116  assert(x0 != x1);
117  slope_ = (y1 - y0) / (x1 - x0);
118  yintercept_ = y0 - slope_ * x0;
119 }
120 
121 template <typename T>
123  return slope_ * x + yintercept_;
124 }
125 
126 /// Represent a permutation.
127 using Permutation = std::vector<size_t>;
128 
129 /**
130  * Calculate the permutations necessary for sorting a vector.
131  *
132  * \tparam Cmp Type of comparison function.
133  * \param v Vector to be sorted.
134  * \param compare Comparison function (see `std::sort`).
135  * \return Vector of indices into the original vector.
136  */
137 template <typename T, typename Cmp>
138 Permutation generate_sort_permutation(std::vector<T> const& v, Cmp compare) {
139  Permutation p(v.size());
140  std::iota(p.begin(), p.end(), 0);
141  std::sort(p.begin(), p.end(),
142  [&](size_t i, size_t j) { return compare(v[i], v[j]); });
143  return p;
144 }
145 
146 /**
147  * Apply a permutation to a vector.
148  *
149  * \tparam T Type of values to be permuted.
150  * \param v Vector to be permuted.
151  * \param p Permutation to be applied.
152  * \return Permuted vector.
153  */
154 template <typename T>
155 std::vector<T> apply_permutation(const std::vector<T>& v,
156  const Permutation& p) {
157  std::vector<T> copied_v = v;
158  std::transform(p.begin(), p.end(), copied_v.begin(),
159  [&](size_t i) { return v[i]; });
160  return copied_v;
161 }
162 
163 /**
164  * Check whether two components have the same value in a sorted vector x.
165  *
166  * \tparam T Type of values to be checked for duplicates.
167  * \param x Vector to be checked for duplicates.
168  * \param error_position String used in the error message, indicating where the
169  * error originated.
170  *
171  * \throw std::runtime_error if duplicates are encountered.
172  */
173 template <typename T>
174 void check_duplicates(const std::vector<T>& x,
175  const std::string& error_position) {
176  auto it = std::adjacent_find(x.begin(), x.end());
177  if (it != x.end()) {
178  std::stringstream error_msg{};
179  error_msg << error_position << ": Each x value must be unique. \"" << *it
180  << "\" was found twice.";
181  throw std::runtime_error(error_msg.str());
182  }
183 }
184 
185 template <typename T>
187  const std::vector<T>& x, const std::vector<T>& y,
188  const ExtrapolationType extrapolation_type)
189  : extrapolation_type_{extrapolation_type} {
190  switch (extrapolation_type_) {
195  break;
196  default:
197  throw std::invalid_argument(
198  "The provided extrapolation type is not supported. Valid types are "
199  "'None', 'Zero', 'Constant', and 'Linear'.");
200  }
201  if (x.size() != y.size()) {
202  throw std::invalid_argument(
203  "The interpolation requires two vectors of equal length.");
204  }
205  const size_t n = x.size();
206  const auto p = generate_sort_permutation(
207  x, [&](T const& a, T const& b) { return a < b; });
208  x_ = apply_permutation(x, p);
209  check_duplicates(x_, "InterpolateDataLinear");
210  std::vector<T> y_sorted = apply_permutation(y, p);
211  f_.reserve(n - 1);
212  for (size_t i = 0; i < n - 1; i++) {
213  f_.emplace_back(
214  InterpolateLinear<T>(x_[i], y_sorted[i], x_[i + 1], y_sorted[i + 1]));
215  }
216 }
217 
218 /**
219  * Find the index in v that corresponds to the last value strictly smaller
220  * than x. If no such value exists, the first value is returned.
221  *
222  * This assumes v is sorted and uses a binary search.
223  *
224  * \tparam T Type of values to be compared to x.
225  * \param v Vector to be searched.
226  * \param x Upper bound for indexed value.
227  * \return Largest index corresponding to value below upper bound.
228  *
229  * Example:
230  * >>> std::vector<int> x = { 0, 2, 4, 6, 8, 10 };
231  * >>> find_index(x, 2)
232  * 0
233  * >>> find_index(x, 3)
234  * 1
235  */
236 template <typename T>
237 size_t find_index(const std::vector<T>& v, T x) {
238  const auto it = std::lower_bound(v.begin(), v.end(), x);
239  if (it == v.begin()) {
240  return 0;
241  } else {
242  return it - 1 - v.begin();
243  }
244 }
245 
246 template <typename T>
248  const double first_x = x_.front();
249  const double last_x = x_.back();
250  if (x0 < first_x || x0 > last_x) {
251  if (extrapolation_type_ == ExtrapolationType::None) {
252  std::ostringstream error_msg{
253  "InterpolateDataLinear only accepts x values within the range of the "
254  "underlying data\nwhen an extrapolation type is not specified. ",
255  std::ios::ate};
256  error_msg << "x value " << x0 << " is out of bounds.";
257  throw std::out_of_range(error_msg.str());
258  } else if (extrapolation_type_ == ExtrapolationType::Zero) {
259  return 0.;
260  } else if (extrapolation_type_ == ExtrapolationType::Constant) {
261  return (x0 < first_x) ? f_.front()(first_x) : f_.back()(last_x);
262  } else if (extrapolation_type_ == ExtrapolationType::Linear) {
263  return (x0 < first_x) ? f_.front()(x0) : f_.back()(x0);
264  }
265  }
266  // Find the piecewise linear interpolation corresponding to x0.
267  const size_t i = find_index(x_, x0);
268  return f_[i](x0);
269 }
270 
271 /// Represent a cubic spline interpolation.
273  public:
274  /**
275  * Interpolate function f given discrete samples f(x_i) = y_i.
276  * Cubic spline interpolation is used.
277  *
278  * \param x x-values.
279  * \param y y-values.
280  * \param extrapolation_type Type of extrapolation for requested x_i values
281  * that are out of bounds. Extrapolation is by
282  * default disabled. Possible types are
283  * <tt>None</tt>, <tt>Zero</tt>, and
284  * <tt>Constant</tt>.
285  *
286  * \return The interpolation function.
287  * \throw std::invalid_argument if vectors x and y have different length.
288  * \throw std::invalid_argument if less than 3 data points are provided.
289  * \throw std::out_of_range if values outside of the boundaries of the
290  * underlying data are tried to be accessed and
291  * extrapolation is disabled.
292  * \throw std::invalid_argument if unsupported extrapolation type is
293  * requested.
294  */
296  const std::vector<double>& x, const std::vector<double>& y,
297  ExtrapolationType extrapolation_type = ExtrapolationType::None);
298 
299  /// Destructor
301 
302  /**
303  * Calculate spline interpolation at x.
304  *
305  * \param x Interpolation argument.
306  * \return Interpolated value.
307  *
308  * \throw std::out_of_range if values outside of the boundaries of the
309  * underlying data are tried to be accessed and
310  * extrapolation is disabled.
311  */
312  double operator()(double x) const;
313 
314  private:
315  /// Extrapolation type.
317  /// First x value of underlying data.
318  double first_x_ = smash_NaN<double>;
319  /// Last x value of underlying data.
320  double last_x_ = smash_NaN<double>;
321  /// First y value of underlying data.
322  double first_y_ = smash_NaN<double>;
323  /// Last y value of underlying data.
324  double last_y_ = smash_NaN<double>;
325  /// GSL iterator for interpolation lookups.
326  gsl_interp_accel* acc_ = nullptr;
327  /// GSL spline.
328  gsl_spline* spline_ = nullptr;
329 };
330 
331 } // namespace smash
332 
333 #endif // SRC_INCLUDE_SMASH_INTERPOLATION_H_
Represent a piecewise linear interpolation.
Definition: interpolation.h:67
ExtrapolationType extrapolation_type_
Extrapolation type.
std::vector< InterpolateLinear< T > > f_
Piecewise linear interpolation using f(x_i)
std::vector< T > x_
x_i
T operator()(T x) const
Calculate linear interpolation at x.
InterpolateDataLinear(const std::vector< T > &x, const std::vector< T > &y, ExtrapolationType extrapolation_type=ExtrapolationType::None)
Interpolate function f given discrete samples f(x_i) = y_i.
Represent a cubic spline interpolation.
double first_x_
First x value of underlying data.
gsl_spline * spline_
GSL spline.
double last_y_
Last y value of underlying data.
double operator()(double x) const
Calculate spline interpolation at x.
double first_y_
First y value of underlying data.
ExtrapolationType extrapolation_type_
Extrapolation type.
InterpolateDataSpline(const std::vector< double > &x, const std::vector< double > &y, ExtrapolationType extrapolation_type=ExtrapolationType::None)
Interpolate function f given discrete samples f(x_i) = y_i.
gsl_interp_accel * acc_
GSL iterator for interpolation lookups.
double last_x_
Last x value of underlying data.
Represent a linear interpolation.
Definition: interpolation.h:37
T yintercept_
y-axis intercept of the linear interpolation.
Definition: interpolation.h:58
T operator()(T x) const
Calculate linear interpolation at x.
T slope_
Slope of the linear interpolation.
Definition: interpolation.h:56
InterpolateLinear(T x0, T y0, T x1, T y1)
Linear interpolation given two points (x0, y0) and (x1, y1).
Collection of useful constants that are known at compile time.
ExtrapolationType
Allows to specify the desired extrapolation type.
@ Linear
Extrapolate using a linear approach.
@ None
No extrapolation is done.
@ Constant
Extrapolate using a constant value.
@ Zero
Extrapolate with zero.
constexpr int p
Proton.
constexpr int n
Neutron.
Definition: action.h:24
size_t find_index(const std::vector< T > &v, T x)
Find the index in v that corresponds to the last value strictly smaller than x.
std::vector< T > apply_permutation(const std::vector< T > &v, const Permutation &p)
Apply a permutation to a vector.
void check_duplicates(const std::vector< T > &x, const std::string &error_position)
Check whether two components have the same value in a sorted vector x.
Permutation generate_sort_permutation(std::vector< T > const &v, Cmp compare)
Calculate the permutations necessary for sorting a vector.
std::vector< size_t > Permutation
Represent a permutation.