Optimist  0.0.0
A C++ library for optimization
Loading...
Searching...
No Matches
FiniteDifference.hh
Go to the documentation of this file.
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
2 * Copyright (c) 2025, Davide Stocco, Mattia Piazza and Enrico Bertolazzi. *
3 * *
4 * The Optimist project is distributed under the BSD 2-Clause License. *
5 * *
6 * Davide Stocco Mattia Piazza Enrico Bertolazzi *
7 * University of Trento University of Trento University of Trento *
8 * davide.stocco@unitn.it mattia.piazza@unitn.it enrico.bertolazzi@unitn.it *
9\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
10
11#pragma once
12
13#ifndef OPTIMIST_FINITE_DIFFERENCE_HH
14#define OPTIMIST_FINITE_DIFFERENCE_HH
15
16namespace Optimist
17{
18
20 {
21
22 class Epsilon
23 {
24 Real const m_epsilon{std::numeric_limits<Real>::epsilon()};
25 Real const m_epsilon1{std::sqrt(std::numeric_limits<Real>::epsilon())};
26 Real const m_epsilon2{std::pow(std::numeric_limits<Real>::epsilon(), 0.75)};
27 Real const m_epsilon3{std::pow(std::numeric_limits<Real>::epsilon(), 0.25)};
28 public:
29 Real epsilon1( Real v ) const { return (abs(v)+1)*m_epsilon1; }
30 Real epsilon2( Real v ) const { return (abs(v)+1)*m_epsilon2; }
31 Real epsilon3( Real v ) const { return (abs(v)+1)*m_epsilon3; }
32 };
33
34 /*\
35 | ____ _ _
36 | / ___|(_) __| | ___
37 | \___ \| |/ _` |/ _ \
38 | ___) | | (_| | __/
39 | |____/|_|\__,_|\___|
40 |
41 \*/
42
43 static
44 Real
46 Real f0,
47 Real f1,
48 Real f2,
49 Real h1,
50 Real h2
51 ) {
52 Real df1{f1 - f0};
53 Real df2{f2 - f0};
54 Real dH{h1 - h2};
55 return ((h1/h2)*df2 - (h2/h1)*df1) / dH;
56 }
57
58 template <Integer N>
59 static
60 bool
62 const Vector<N> & f0,
63 const Vector<N> & f1,
64 const Vector<N> & f2,
65 const Real h1,
66 const Real h2,
67 Vector<N> & diff
68 ) {
69 Real dH{h1 - h2};
70 Real t1{-(h2/h1) / dH};
71 Real t2{(h1/h2) / dH};
72 diff = t1 * (f1 - f0) + t2 * (f2 - f0);
73 return diff.allFinite();
74 }
75
76 /*\
77 | ____ _ _
78 | / ___|___ _ __ | |_ ___ _ __ ___ __| |
79 | | | / _ \ '_ \| __/ _ \ '__/ _ \/ _` |
80 | | |__| __/ | | | || __/ | | __/ (_| |
81 | \____\___|_| |_|\__\___|_| \___|\__,_|
82 |
83 \*/
84
85 static
86 Real
88 const Real fun_l,
89 const Real fun_c,
90 const Real fun_r,
91 const Real h
92 ) {
93 Real diff_r{(fun_r-fun_c)/h};
94 Real diff_l{(fun_c-fun_l)/h};
95 Real weight_r{std::abs(diff_r) + EPSILON};
96 Real weight_l{std::abs(diff_l) + EPSILON};
97 Real weight_max{std::max(weight_r, weight_l)};
98 weight_r = std::sqrt(weight_r/weight_max);
99 weight_l = std::sqrt(weight_l/weight_max);
100 return (diff_r*weight_l + diff_l*weight_r) / (weight_r+weight_l);
101 }
102
103 static
104 Integer
106 const Real fun_l, const bool is_finite_l,
107 const Real fun_c, const bool is_finite_c,
108 const Real fun_r, const bool is_finite_r,
109 const Real h,
110 Real & diff
111 ) {
112
113 if (is_finite_r && is_finite_l && is_finite_c) {
114 diff = finite_difference_centered(fun_l, fun_c, fun_r, h);
115 return 0;
116 } else if (is_finite_c && is_finite_r) {
117 diff = (fun_r-fun_c)/h;
118 return 1;
119 } else if (is_finite_c && is_finite_l) {
120 diff = (fun_c-fun_l)/h;
121 return -1;
122 }
123 diff = 0;
124 return -2;
125 }
126
127 template <Integer N>
128 static
129 Integer
131 const Vector<N> & fun_l, bool is_finite_l,
132 const Vector<N> & fun_c, bool is_finite_c,
133 const Vector<N> & fun_r, bool is_finite_r,
134 Real h,
135 Real diff
136 ) {
137 if ( is_finite_r && is_finite_l && is_finite_c ) {
138 Vector<N> diff_r((fun_r - fun_c) / h);
139 Vector<N> diff_l((fun_c - fun_l) / h);
140 Vector<N> weight_r{diff_r.array().abs() + EPSILON};
141 Vector<N> weight_l{diff_l.array().abs() + EPSILON};
142 Vector<N> weight_max = weight_r.array().max(weight_l.array());
143 weight_r = (weight_r/weight_max).array().sqrt();
144 weight_l = (weight_l/weight_max).array().sqrt();
145 diff = (diff_r*weight_l + diff_l*weight_r) / (weight_r+weight_l);
146 return 0;
147 } else if ( is_finite_c && is_finite_r ) {
148 diff = (fun_r-fun_c)/h;
149 return 1;
150 } else if ( is_finite_c && is_finite_l ) {
151 diff = (fun_c-fun_l)/h;
152 return -1;
153 }
154 diff.setZero();
155 return -2;
156 }
157
158 /*\
159 | ____ _ _ _
160 | / ___|_ __ __ _ __| (_) ___ _ __ | |_
161 | | | _| '__/ _` |/ _` | |/ _ \ '_ \| __|
162 | | |_| | | | (_| | (_| | | __/ | | | |_
163 | \____|_| \__,_|\__,_|_|\___|_| |_|\__|
164 |
165 \*/
166
167 template <typename FUNCTION, typename Real>
168 static
169 bool
171 Real const x[],
172 Integer dim_x,
173 FUNCTION fun,
174 Real grad[]
175 ) {
176
177 static FiniteDifferenceEpsilon<Real> EPS;
178
179 auto FUN = [&fun]( Real const x[], Real & f ) -> bool {
180 bool ok{fun( x, f )};
181 if ( ok ) ok = std::is_finite(f);
182 return ok;
183 };
184
185 Real vC{0}, vR{0}, vL{0}; // only to stop warning
186 bool is_finite_c = FUN( x, vC );
187 if ( !is_finite_c ) return false;
188
189 Real * X = const_cast<Real*>(x);
190
191 for ( Integer i{0}; i < dim_x; ++i ) {
192 Real temp{x[i]};
193 Real h1{EPS.epsilon1(temp)};
194 Real h2{EPS.epsilon2(temp)};
195 X[i] = temp+h1; bool is_finite_r{FUN( X, vR )};
196 X[i] = temp-h1; bool is_finite_l{FUN( X, vL )};
197 Integer ic = finite_difference_centered( vL, is_finite_l, vC, is_finite_c, vR, is_finite_r, h1, grad[i] );
198 switch ( ic ) {
199 case 0:
200 break;
201 case 1:
202 {
203 Real & vRR = vL;
204 X[i] = temp+h2; // modify the vector only at i position
205 bool is_finite_rr{FUN( X, vRR )};
206 if ( is_finite_rr ) grad[i] = finite_difference_side( vC, vR, vRR, h1, h2 );
207 if ( ! (is_finite_rr&&std::is_finite(grad[i])) ) grad[i] = (vR-vC)/h1; // low precision FD
208 }
209 break;
210 case -1:
211 {
212 Real & vLL = vR;
213 X[i] = temp-h2; // modify the vector only at i position
214 bool is_finite_ll{FUN( X, vLL )};
215 if ( is_finite_ll ) grad[i] = finite_difference_side( vC, vL, vLL, -h1, -h2 );
216 if ( ! (is_finite_ll&&std::is_finite(grad[i])) ) grad[i] = (vC-vL)/h1; // low precision FD
217 }
218 break;
219 case -2:
220 return false;
221 break;
222 }
223 X[i] = temp; // restore i position
224 if ( !std::is_finite(grad[i]) ) return false;
225 }
226 return true;
227 }
228
229 /*\
230 | _ _ _
231 | | | __ _ ___ ___ | |__ (_) __ _ _ __
232 | _ | |/ _` |/ __/ _ \| '_ \| |/ _` | '_ \
233 | | |_| | (_| | (_| (_) | |_) | | (_| | | | |
234 | \___/ \__,_|\___\___/|_.__/|_|\__,_|_| |_|
235 |
236 \*/
237
238 template <typename FUNCTION, typename Real>
239 static
240 bool
242 Real const x[],
243 Integer dim_x,
244 FUNCTION fun,
245 Integer dim_f,
246 Real Jac[],
247 Integer ldJ,
248 Real * work,
249 Integer lwork
250 ) {
251
252 static FiniteDifferenceEpsilon<Real> EPS;
253
254 UTILS_ASSERT(
255 lwork >= 3*dim_f,
256 "finite_difference_jacobian(...,dim_f={},...,lwork={}), lwork must be >= 3*dim_f\n",
257 dim_f, lwork
258 );
259
260 Real * vC{work};
261 Real * vR{work+dim_f};
262 Real * vL{work+2*dim_f};
263
264 auto FUN = [&fun,dim_f]( Real const x[], Real f[] ) -> bool {
265 bool ok{fun( x, f )};
266 for ( Integer i{0}; ok && i < dim_f; ++i ) ok = std::is_finite(f[i]);
267 return ok;
268 };
269
270 bool is_finite_c{FUN( x, vC )};
271 if ( !is_finite_c ) return false;
272
273 Real * X{const_cast<Real*>(x)};
274 Real * pjac{Jac};
275
276 for ( Integer j{0}; j < dim_x; ++j ) {
277 Real temp{x[j]};
278 Real h1{EPS.epsilon1(temp)};
279 Real h2{EPS.epsilon2(temp)};
280
281 X[j] = temp+h1; bool is_finite_r{FUN( X, vR )};
282 X[j] = temp-h1; bool is_finite_l{FUN( X, vL )};
283
284 Integer ic = finite_difference_centered( vL, is_finite_l, vC, is_finite_c, vR, is_finite_r, h1, pjac, dim_f );
285
286 switch ( ic ) {
287 case 0:
288 break;
289 case 1:
290 {
291 Real * vRR{vL};
292 X[j] = temp+h2; // modify the vector only at i position
293 bool is_finite_rr{FUN( X, vRR )};
294 if ( is_finite_rr ) is_finite_rr = finite_difference_side( vC, vR, vRR, h1, h2, pjac, dim_f );
295 if ( !is_finite_rr ) {
296 for ( Integer i{0}; i < dim_f; ++i ) {
297 pjac[i] = (vR[i]-vC[i])/h1; // low precision FD
298 if ( !std::is_finite( pjac[i] ) ) return false;
299 }
300 }
301 }
302 break;
303 case -1:
304 {
305 Real * vLL{vR};
306 X[j] = temp-h2; // modify the vector only at i position
307 bool is_finite_ll{FUN( X, vLL )};
308 if ( is_finite_ll ) is_finite_ll = finite_difference_side( vC, vL, vLL, -h1, -h2, pjac, dim_f );
309 if ( !is_finite_ll ) {
310 for ( Integer i{0}; i < dim_f; ++i ) {
311 pjac[i] = (vC[i]-vL[i])/h1; // low precision FD
312 if ( !std::is_finite( pjac[i] ) ) return false;
313 }
314 }
315 }
316 break;
317 case -2:
318 return false;
319 break;
320 }
321 X[j] = temp; // restore i position
322 pjac += ldJ;
323 }
324 return true;
325 }
326
327 /*\
328 | _ _ _
329 | | | | | ___ ___ ___(_) __ _ _ __
330 | | |_| |/ _ \/ __/ __| |/ _` | '_ \
331 | | _ | __/\__ \__ \ | (_| | | | |
332 | |_| |_|\___||___/___/_|\__,_|_| |_|
333 |
334 \*/
335
336 template <typename FUNCTION, typename Real>
337 static
338 bool
340 Real const x[],
341 Integer dim_x,
342 FUNCTION fun,
343 Real Hess[],
344 Integer ldH
345 ) {
346 static FiniteDifferenceEpsilon<Real> EPS;
347
348 auto FUN = [&fun]( Real const x[], Real & f ) -> bool {
349 bool ok{fun( x, f )};
350 if ( ok ) ok = std::is_finite(f);
351 return ok;
352 };
353
354 Real fp{0}, fm{0}, fc{0}, hij{0}, tempi{0}, tempj{0}, hi{0}, hj{0},
355 fpp{0}, fpm{0}, fmp{0}, fmm{0};
356 bool ok{true};
357
358 Real * X{const_cast<Real*>(x)};
359 for ( Integer j{0}; j < dim_x && ok; ++j ) {
360 tempj = x[j];
361 hj = EPS.epsilon3(tempj);
362 ok = FUN( X, fc );
363 if ( !ok ) goto skip;
364 X[j] = tempj+hj;
365 ok = FUN( X, fp );
366 if ( !ok ) goto skip;
367 X[j] = tempj-hj;
368 ok = FUN( X, fm );
369 if ( !ok ) goto skip;
370 Hess[j*(ldH+1)] = ((fp+fm)-2*fc)/(hj*hj);
371 for ( Integer i{j+1}; i < dim_x && ok; ++i ) {
372 tempi = X[i];
373 hi = EPS.epsilon3(tempi);
374 X[i] = tempi+hi;
375 X[j] = tempj+hj;
376 ok = FUN( X, fpp );
377 if ( !ok ) goto skip2;
378 X[i] = tempi-hi;
379 ok = FUN( X, fmp );
380 if ( !ok ) goto skip2;
381 X[j] = tempj-hj;
382 ok = FUN( X, fmm );
383 if ( !ok ) goto skip2;
384 X[i] = tempi+hi;
385 ok = FUN( X, fpm );
386 if ( !ok ) goto skip2;
387 hij = 4*hi*hj;
388 Hess[j+i*ldH] = Hess[i+j*ldH] = ( (fpp+fmm) - (fpm+fmp) )/hij;
389 skip2:
390 X[i] = tempi;
391 }
392 skip:
393 X[j] = tempj;
394 }
395 return ok;
396 }
397
398 } // namespace FiniteDifference
399
400} // namespace Optimist
401
402#endif // OPTIMIST_FINITE_DIFFERENCE_HH
Definition FiniteDifference.hh:23
Real epsilon2(Real v) const
Definition FiniteDifference.hh:30
Real epsilon1(Real v) const
Definition FiniteDifference.hh:29
Real epsilon3(Real v) const
Definition FiniteDifference.hh:31
Real const m_epsilon3
Definition FiniteDifference.hh:27
Real const m_epsilon
Definition FiniteDifference.hh:24
Real const m_epsilon2
Definition FiniteDifference.hh:26
Real const m_epsilon1
Definition FiniteDifference.hh:25
Definition FiniteDifference.hh:20
static bool finite_difference_hessian(Real const x[], Integer dim_x, FUNCTION fun, Real Hess[], Integer ldH)
Definition FiniteDifference.hh:339
static Real finite_difference_side(Real f0, Real f1, Real f2, Real h1, Real h2)
Definition FiniteDifference.hh:45
static Real finite_difference_centered(const Real fun_l, const Real fun_c, const Real fun_r, const Real h)
Definition FiniteDifference.hh:87
static bool finite_difference_gradient(Real const x[], Integer dim_x, FUNCTION fun, Real grad[])
Definition FiniteDifference.hh:170
static bool finite_difference_jacobian(Real const x[], Integer dim_x, FUNCTION fun, Integer dim_f, Real Jac[], Integer ldJ, Real *work, Integer lwork)
Definition FiniteDifference.hh:241
Namespace for the Optimist library.
Definition Optimist.hh:87
OPTIMIST_DEFAULT_INTEGER_TYPE Integer
The Integer type as used for the API.
Definition Optimist.hh:95