casadi_finite_diff.hpp
1 //
2 // MIT No Attribution
3 //
4 // Copyright (C) 2010-2023 Joel Andersson, Joris Gillis, Moritz Diehl, KU Leuven.
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a copy of this
7 // software and associated documentation files (the "Software"), to deal in the Software
8 // without restriction, including without limitation the rights to use, copy, modify,
9 // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
10 // permit persons to whom the Software is furnished to do so.
11 //
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13 // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
14 // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
15 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
16 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
17 // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18 //
19 
20 // C-REPLACE "std::numeric_limits<T1>::quiet_NaN()" "NAN"
21 // C-REPLACE "fmax" "casadi_fmax"
22 
23 // SYMBOL "forward_diff"
24 template<typename T1>
25 void casadi_forward_diff(const T1** yk, T1* J, T1 h, casadi_int n_y) {
26  // Local variables
27  casadi_int i;
28  const double *yf, *yc;
29  T1 hinv;
30  // Inverse of step size
31  hinv = 1. / h;
32  // Get stencil
33  yc = yk[0];
34  yf = yk[1];
35  // Calculate FD approximation
36  for (i = 0; i < n_y; ++i) J[i] = hinv * (yf[i] - yc[i]);
37 }
38 
39 // SYMBOL "central_diff"
40 template<typename T1>
41 void casadi_central_diff(const T1** yk, T1* J, T1 h, casadi_int n_y) {
42  // Local variables
43  casadi_int i;
44  const T1 *yf, *yc, *yb;
45  T1 hinv;
46  // Inverse of step size
47  hinv = 1. / h;
48  // Get stencil
49  yb = yk[0];
50  yc = yk[1];
51  yf = yk[2];
52  // Set u and stencils to zero (also supresses warnings)
53  for (i = 0; i < n_y; ++i) {
54  if (isfinite(yb[i])) {
55  if (isfinite(yf[i])) {
56  // Both forward and backward allowed
57  J[i] = 0.5 * hinv * (yf[i] - yb[i]);
58  } else {
59  // Backward but not forward allowed
60  J[i] = hinv * (yc[i] - yb[i]);
61  }
62  } else {
63  if (isfinite(yf[i])) {
64  // Forward but not backward allowed
65  J[i] = hinv * (yf[i] - yc[i]);
66  } else {
67  // Neither forward nor backward possible
68  J[i] = std::numeric_limits<T1>::quiet_NaN();
69  }
70  }
71  }
72 }
73 
74 // SYMBOL "central_diff_err"
75 template<typename T1>
76 T1 casadi_central_diff_err(const T1** yk, T1 h, casadi_int n_y, casadi_int i,
77  T1 abstol, T1 reltol) {
78  // Local variables
79  const T1 *yf, *yc, *yb;
80  T1 err_trunc, err_round;
81  // Get stencil
82  yb = yk[0];
83  yc = yk[1];
84  yf = yk[2];
85  // Only consider points where both forward and backward allowed
86  if (isfinite(yb[i]) && isfinite(yf[i])) {
87  // Truncation error
88  err_trunc = yf[i] - 2*yc[i] + yb[i];
89  // Roundoff error
90  err_round = reltol / h * fmax(fabs(yf[i] - yc[i]), fabs(yc[i] - yb[i])) + abstol;
91  // Error quotient estimate
92  return err_trunc / err_round;
93  } else {
94  // Cannot be calculated
95  return std::numeric_limits<T1>::quiet_NaN();;
96  }
97 }
98 
99 // SYMBOL "smoothing_diff_weights"
100 template<typename T1>
101 T1 casadi_smoothing_diff_weights(casadi_int k, T1 yb, T1 yc, T1 yf, T1 *J) {
102  // Calculate shifted finite difference approximation, weights
103  if (k == 0) {
104  // Backward shifted
105  // 7.10 in Conte & Carl de Boor: Elementary Numerical Analysis (1972)
106  // and 25.3.4 in Abramowitz and Stegun, Handbook of Mathematical Functions (1964)
107  if (J) *J = 3 * yf - 4 * yc + yb;
108  // Relative weight is 1
109  return 1;
110  } else if (k == 1) {
111  // Central
112  // We give this the relative weight 4 since if all weights are equal,
113  // this would amount to a five point formula for the derivative
114  // (yb2 - 8*yb + 8*yf - y_f2)/(12*h)
115  // cf. 25.3.6 in Abramowitz and Stegun, Handbook of Mathematical Functions (1964)
116  if (J) *J = yf - yb;
117  // Relative weight is 4
118  return 4;
119  } else {
120  // Forward shifted, cf. backward shifted above
121  if (J) *J = -3 * yb + 4 * yc - yf;
122  // Relative weight is 1
123  return 1;
124  }
125 }
126 
127 // SYMBOL "smoothing_diff"
128 template<typename T1>
129 void casadi_smoothing_diff(const T1** yk, T1* J, T1 h, casadi_int n_y, T1 smoothing) {
130  // Stencil
131  T1 yb, yc, yf;
132  // Local variables
133  T1 Jk, wk, sw, ui, sm;
134  casadi_int i, k;
135  // Set stencils to zero (also supresses warnings)
136  yf = yc = yb = 0;
137  for (i = 0; i < n_y; ++i) {
138  // Reset derivative estimate, sum of weights, error estimate
139  J[i] = sw = ui = 0;
140  // For backward shifted, central and forward shifted
141  for (k = 0; k < 3; ++k) {
142  // Get stencil
143  yb = yk[k][i];
144  yc = yk[k + 1][i];
145  yf = yk[k + 2][i];
146  // No contribuation if any value is infinite
147  if (!isfinite(yb) || !isfinite(yc) || !isfinite(yf)) continue;
148  // Calculate weights
149  wk = casadi_smoothing_diff_weights(k, yb, yc, yf, &Jk);
150  // Smoothness measure (second order derivative)
151  sm = yf - 2*yc + yb;
152  sm /= h*h;
153  // Modify the weight according to smoothness
154  wk /= sm*sm + smoothing;
155  sw += wk;
156  // Added weighted contribution to weight and error
157  J[i] += wk * Jk;
158  }
159  // If sw is 0, no stencil worked
160  if (sw == 0) {
161  // Set component to 0, return -1
162  J[i] = std::numeric_limits<T1>::quiet_NaN();
163  } else {
164  // Finalize estimate using the sum of weights and the step length
165  J[i] /= 2*h*sw;
166  }
167  }
168 }
169 
170 // SYMBOL "smoothing_diff_err"
171 template<typename T1>
172 T1 casadi_smoothing_diff_err(const T1** yk, T1 h, casadi_int n_y, casadi_int i,
173  T1 abstol, T1 reltol, T1 smoothing) {
174  // Stencil
175  T1 yb, yc, yf;
176  // Local variables
177  T1 wk, sw, ui, err_trunc, err_round, sm;
178  casadi_int k;
179  // Set stencils to zero (also supresses warnings)
180  yf = yc = yb = 0;
181  // Reset derivative estimate, sum of weights, error estimate
182  sw = ui = 0;
183  // For backward shifted, central and forward shifted
184  for (k = 0; k < 3; ++k) {
185  // Get stencil
186  yb = yk[k][i];
187  yc = yk[k + 1][i];
188  yf = yk[k + 2][i];
189  // No contribuation if any value is infinite
190  if (!isfinite(yb) || !isfinite(yc) || !isfinite(yf)) continue;
191  // Calculate weights
192  wk = casadi_smoothing_diff_weights(k, yb, yc, yf, static_cast<T1*>(0));
193  // Truncation error
194  err_trunc = yf - 2*yc + yb;
195  // Roundoff error
196  err_round = reltol/h*fmax(fabs(yf - yc), fabs(yc - yb)) + abstol;
197  // We use the second order derivative as a smoothness measure
198  sm = err_trunc/(h*h);
199  // Modify the weight according to smoothness
200  wk /= sm*sm + smoothing;
201  sw += wk;
202  // Added weighted contribution to weight and error
203  ui += wk * fabs(err_trunc / err_round);
204  }
205  // If sw is 0, no stencil worked
206  if (sw == 0) {
207  // Cannot be calculated
208  return std::numeric_limits<T1>::quiet_NaN();;
209  } else {
210  // Finalize estimate using the sum of weights and the step length
211  return ui / sw;
212  }
213 }
214 
215 // SYMBOL "finite_diff_mem"
216 template<typename T1>
218  // Input precision
219  T1 reltol;
220  // Output precision
221  T1 abstol;
222  // Smoothness parameter
223  // Smaller epsilon: More discontinuity rejecting
224  // Larger epsilon: More accurate (higher order) if smooth
226 };
227 
228 // C-REPLACE "casadi_finite_diff_mem<T1>" "struct casadi_finite_diff_mem"
229 
230 // SYMBOL "forward_diff_old"
231 template<typename T1>
232 T1 casadi_forward_diff_old(T1** yk, T1* y0, T1* J,
233  T1 h, casadi_int n_y, const casadi_finite_diff_mem<T1>* m) {
234  casadi_int i;
235  for (i=0; i<n_y; ++i) {
236  J[i] = (yk[0][i]-y0[i])/h;
237  }
238  return -1;
239 }
240 
241 // SYMBOL "central_diff_old"
242 template<typename T1>
243 T1 casadi_central_diff_old(T1** yk, T1* y0, T1* J,
244  T1 h, casadi_int n_y, const casadi_finite_diff_mem<T1>* m) {
245  // Return value
246  T1 u;
247  // Stencil
248  T1 yf, yc, yb;
249  // Local variables
250  T1 err_trunc, err_round;
251  casadi_int i;
252  // Set u and stencils to zero (also supresses warnings)
253  yf = yc = yb = u = 0;
254  for (i=0; i<n_y; ++i) {
255  // Copy to local variables, return -1 if invalid entry
256  if (!isfinite((yf=yk[1][i])) || !isfinite((yc=y0[i])) || !isfinite((yb=yk[0][i]))) {
257  J[i] = std::numeric_limits<T1>::quiet_NaN();
258  u = -1;
259  continue;
260  }
261  // Central difference approximation
262  J[i] = (yf - yb)/(2*h);
263  // Truncation error
264  err_trunc = yf - 2*yc + yb;
265  // Roundoff error
266  err_round = m->reltol/h*fmax(fabs(yf-yc), fabs(yc-yb)) + m->abstol;
267  // Update error estimate
268  if (u>=0) u = fmax(u, fabs(err_trunc/err_round));
269  }
270  return u;
271 }
272 
273 // SYMBOL "smoothing_diff_old"
274 template<typename T1>
275 T1 casadi_smoothing_diff_old(T1** yk, T1* y0, T1* J,
276  T1 h, casadi_int n_y, const casadi_finite_diff_mem<T1>* m) {
277  // Return value
278  T1 u;
279  // Stencil
280  T1 yb, yc, yf;
281  // Local variables
282  T1 Jk, wk, sw, ui, err_trunc, err_round, sm;
283  casadi_int i, k;
284  // Set u and stencils to zero (also supresses warnings)
285  yf = yc = yb = u = 0;
286  for (i=0; i<n_y; ++i) {
287  // Reset derivative estimate, sum of weights, error estimate
288  J[i] = sw = ui = 0;
289  // For backward shifted, central and forward shifted
290  for (k=0; k<3; ++k) {
291  // Calculate shifted finite difference approximation
292  if (k==0) {
293  // Backward shifted
294  // 7.10 in Conte & Carl de Boor: Elementary Numerical Analysis (1972)
295  // and 25.3.4 in Abramowitz and Stegun, Handbook of Mathematical Functions (1964)
296  if (!isfinite((yc=yk[0][i]))) continue;
297  if (!isfinite((yb=yk[1][i]))) continue;
298  yf = y0[i];
299  Jk = 3*yf - 4*yc + yb;
300  wk = 1;
301  } else if (k==1) {
302  // Central
303  // We give this the "nominal weight" 4 since if all weights are equal,
304  // this would amount to a five point formula for the derivative
305  // (yb2 - 8*yb + 8*yf - y_f2)/(12*h)
306  // cf. 25.3.6 in Abramowitz and Stegun, Handbook of Mathematical Functions (1964)
307  if (!isfinite((yf=yk[2][i]))) continue;
308  if (!isfinite((yb=yc))) continue;
309  yc = y0[i];
310  Jk = yf-yb;
311  wk = 4;
312  } else {
313  // Forward shifted
314  if (!isfinite((yc=yf))) continue;
315  if (!isfinite((yf=yk[3][i]))) continue;
316  yb = y0[i];
317  Jk = -3*yb + 4*yc - yf;
318  wk = 1;
319  }
320  // Truncation error
321  err_trunc = yf - 2*yc + yb;
322  // Roundoff error
323  err_round = m->reltol/h*fmax(fabs(yf-yc), fabs(yc-yb)) + m->abstol;
324  // We use the second order derivative as a smoothness measure
325  sm = err_trunc/(h*h);
326  // Modify the weight according to smoothness
327  wk /= sm*sm + m->smoothing;
328  sw += wk;
329  // Added weighted contribution to weight and error
330  J[i] += wk * Jk;
331  ui += wk * fabs(err_trunc/err_round);
332  }
333  // If sw is 0, no stencil worked
334  if (sw==0) {
335  // Set component to 0, return -1
336  J[i] = std::numeric_limits<T1>::quiet_NaN();
337  u = -1;
338  } else {
339  // Finalize estimate using the sum of weights and the step length
340  J[i] /= 2*h*sw;
341  if (u>=0) u = fmax(u, ui/sw);
342  }
343  }
344  return u;
345 }