FEAT 3
Finite Element Analysis Toolbox
Loading...
Searching...
No Matches
auto_derive.hpp
1// FEAT3: Finite Element Analysis Toolbox, Version 3
2// Copyright (C) 2010 by Stefan Turek & the FEAT group
3// FEAT3 is released under the GNU General Public License version 3,
4// see the file 'copyright.txt' in the top level directory for details.
5
6#pragma once
7
8// includes, FEAT
9#include <kernel/analytic/function.hpp>
10
11// includes, system
12#include <vector>
13
14namespace FEAT
15{
16 namespace Analytic
17 {
19 namespace Intern
20 {
21 template<bool wrap_>
22 struct AutoDeriveGradWrapper
23 {
24 template<typename Point_, typename FuncEval_, typename AutoDer_>
25 static typename AutoDer_::GradientType wrap(const Point_& point, FuncEval_& func_eval, AutoDer_&)
26 {
27 return func_eval.gradient(point);
28 }
29 };
30
31 template<>
32 struct AutoDeriveGradWrapper<false>
33 {
34 template<typename Point_, typename FuncEval_, typename AutoDer_>
35 static typename AutoDer_::GradientType wrap(const Point_& point, FuncEval_&, AutoDer_& auto_der)
36 {
37 return auto_der.extrapol_grad(point);
38 }
39 };
40
41 template<bool wrap_>
42 struct AutoDeriveHessWrapper
43 {
44 template<typename Point_, typename FuncEval_, typename AutoDer_>
45 static typename AutoDer_::HessianType wrap(const Point_& point, FuncEval_& func_eval, AutoDer_&)
46 {
47 return func_eval.hessian(point);
48 }
49 };
50
51 template<>
52 struct AutoDeriveHessWrapper<false>
53 {
54 template<typename Point_, typename FuncEval_, typename AutoDer_>
55 static typename AutoDer_::HessianType wrap(const Point_& point, FuncEval_&, AutoDer_& auto_der)
56 {
57 return auto_der.extrapol_hess(point);
58 }
59 };
60 } // namespace Intern
62
89 template<typename Function_, typename DataType_ = Real>
90 class AutoDerive :
91 public Function_
92 {
93 public:
95 static_assert(Function_::can_value, "function cannot compute values");
96
98 static constexpr int domain_dim = Function_::domain_dim;
99
101 typedef typename Function_::ImageType ImageType;
102
104 static constexpr bool can_value = true;
106 static constexpr bool can_grad = true;
108 static constexpr bool can_hess = true;
109
110 template<typename Traits_>
111 class Evaluator :
112 public Analytic::Function::Evaluator<Traits_>
113 {
114 public:
116 typedef typename Traits_::DataType DataType;
118 typedef typename Traits_::PointType PointType;
120 typedef typename Traits_::ValueType ValueType;
122 typedef typename Traits_::GradientType GradientType;
124 typedef typename Traits_::HessianType HessianType;
126 static constexpr int domain_dim = Traits_::domain_dim;
127
128 private:
130 typename Function_::template Evaluator<Traits_> _func_eval;
132 std::vector<GradientType> _grad;
134 std::vector<HessianType> _hess;
139
140 public:
142 explicit Evaluator(const AutoDerive& function) :
143 _func_eval(function),
144 _grad(std::size_t(function._max_grad_steps)),
145 _hess(std::size_t(function._max_hess_steps)),
146 _init_grad_h(function._init_grad_h),
147 _init_hess_h(function._init_hess_h)
148 {
149 }
150
151 ValueType value(const PointType& point)
152 {
153 return _func_eval.value(point);
154 }
155
156 GradientType gradient(const PointType& point)
157 {
158 // Depending on whether the original function can compute gradients,
159 // either call the original evaluator's "gradient" function or apply
160 // our extrapolation scheme implemented in "extrapol_grad".
161 return Intern::AutoDeriveGradWrapper<Function_::can_grad>::wrap(point, _func_eval, *this);
162 }
163
164 HessianType hessian(const PointType& point)
165 {
166 // Depending on whether the original function can compute hessians,
167 // either call the original evaluator's "hessian" function or apply
168 // our extrapolation scheme implemented in "extrapol_hess".
169 return Intern::AutoDeriveHessWrapper<Function_::can_hess>::wrap(point, _func_eval, *this);
170 }
171
176 {
177 // first, create a mutable copy of our point
178 PointType v(point);
179
180 // next, choose the initial h
182
183 // Note: the '_grad' vector was already allocated to
184 // the correct size by our constructor
185
186 // evaluate gradient
187 this->_eval_grad_quot(_grad[0], v, h);
188
189 // Now comes the Richardson extrapolation loop
190 const std::size_t n(_grad.size()-1);
191 DataType def(Math::huge<DataType>());
192 for(std::size_t i(0); i < n; ++i)
193 {
194 // evaluate next gradient
195 this->_eval_grad_quot(_grad[i+1], v, h *= DataType(0.5));
196
197 // initialize scaling fator
198 DataType q = DataType(1);
199
200 // perform extrapolation steps except for the last one
201 for(std::size_t k(i); k > std::size_t(0); --k)
202 {
203 q *= DataType(4);
204 (_grad[k] -= q*_grad[k+1]) *= DataType(1) / (DataType(1) - q);
205 }
206
207 // compute and check our defect
208 DataType d = def_norm_sqr(_grad[1], _grad[0]);
209 if(def <= d)
210 {
211 // The defect has increased, so we return the previous extrapolation result
212 return _grad.front();
213 }
214
215 // remember current defect
216 def = d;
217
218 // perform last extrapolation step
219 q *= DataType(4);
220 (_grad[0] -= q*_grad[1]) *= DataType(1) / (DataType(1) - q);
221 }
222
223 // return our extrapolated gradient
224 return _grad.front();
225 }
226
231 {
232 // first, create a mutable copy of our point
233 PointType v(point);
234
235 // next, choose the initial h
237
238 // Note: the '_hess' vector was already allocated to
239 // the correct size by our constructor
240
241 // evaluate hessian
242 this->_eval_hess_quot(_hess[0], v, h);
243
244 // Now comes the Richardson extrapolation loop
245 const std::size_t n(_hess.size()-1);
246 DataType def(Math::huge<DataType>());
247 for(std::size_t i(0); i < n; ++i)
248 {
249 // evaluate next hessian
250 this->_eval_hess_quot(_hess[i+1], v, h *= DataType(0.5));
251
252 // initialize scaling fator
253 DataType q = DataType(1);
254
255 // perform extrapolation steps except for the last one
256 for(std::size_t k(i); k > std::size_t(0); --k)
257 {
258 q *= DataType(4);
259 (_hess[k] -= q*_hess[k+1]) *= DataType(1) / (DataType(1) - q);
260 }
261
262 // compute and check our defect
263 DataType d = def_norm_sqr(_hess[1], _hess[0]);
264 if(def <= d)
265 {
266 // The defect has increased, so we return the previous extrapolation result
267 return _hess.front();
268 }
269
270 // remember current defect
271 def = d;
272
273 // perform last extrapolation step
274 q *= DataType(4);
275 (_hess[0] -= q*_hess[1]) *= DataType(1) / (DataType(1) - q);
276 }
277
278 // return our extrapolated hessian
279 return _hess.front();
280 }
281
282 protected:
283 template<int n_, int s_>
284 static DataType def_norm_sqr(
286 {
287 return (x - y).norm_euclid_sqr();
288 }
289
290 template<int m_, int n_, int sm_, int sn_>
291 static DataType def_norm_sqr(
294 {
295 return (x - y).norm_hessian_sqr();
296 }
297
298 template<int l_, int m_, int n_, int sl_, int sm_, int sn_>
299 static DataType def_norm_sqr(
302 {
303 DataType r(DataType(0));
304 for(int i(0); i < l_; ++i)
305 {
306 for(int j(0); j < m_; ++j)
307 {
308 for(int k(0); k < n_; ++k)
309 {
310 r += Math::sqr(x(i,j,k) - y(i,j,k));
311 }
312 }
313 }
314 return r;
315 }
316
317 // compute scalar difference quotient for gradient
318 template<int sn_>
319 static void _set_grad_quot(Tiny::Vector<DataType, domain_dim, sn_>& x, int i, const DataType& vr, const DataType& vl, const DataType denom)
320 {
321 x[i] = denom * (vr - vl);
322 }
323
324 // compute vector difference quotient for gradient
325 template<int img_dim_, int sm_, int sn_>
326 static void _set_grad_quot(Tiny::Matrix<DataType, img_dim_, domain_dim, sm_, sn_>& x, int i, const ValueType& vr, const ValueType& vl, const DataType denom)
327 {
328 for(int k(0); k < img_dim_; ++k)
329 x[k][i] = denom * (vr[k] - vl[k]);
330 }
331
334 {
335 // difference quotient denominator
336 const DataType denom = DataType(1) / (DataType(2) * h);
337 ValueType vr, vl;
338
339 // loop over all dimensions
340 for(int i(0); i < domain_dim; ++i)
341 {
342 // backup coordinate i
343 const DataType vi(v[i]);
344
345 // evaluate f(v + h*e_i)
346 v[i] = vi + h;
347 vr = _func_eval.value(v);
348
349 // evaluate f(v - h*e_i)
350 v[i] = vi - h;
351 vl = _func_eval.value(v);
352
353 // compute difference quotient with appropriate overload (scalar/vector)
354 _set_grad_quot(x, i, vr, vl, denom);
355
356 // restore coord
357 v[i] = vi;
358 }
359 }
360
361 // compute scalar difference quotient for hessian for i == j
362 template<int sm_, int sn_>
363 static void _set_hess_quot(Tiny::Matrix<DataType, domain_dim, domain_dim, sm_, sn_>& x, int i, const ValueType& vr, const ValueType& vl, const ValueType& vc, const DataType denom)
364 {
365 x[i][i] = denom * (vr + vl - DataType(2)*vc);
366 }
367
368 // compute scalar difference quotient for hessian for i != j
369 template<int sm_, int sn_>
370 static void _set_hess_quot(Tiny::Matrix<DataType, domain_dim, domain_dim, sm_, sn_>& x, int i, int j, const ValueType& vne, const ValueType& vsw, const ValueType& vnw, const ValueType& vse, const DataType denom)
371 {
372 x[i][j] = x[j][i] = denom * ((vne + vsw) - (vnw + vse));
373 }
374
375 // compute vector difference quotient for hessian for i == j
376 template<int image_dim_, int sl_, int sm_, int sn_>
377 static void _set_hess_quot(Tiny::Tensor3<DataType, image_dim_, domain_dim, domain_dim, sl_, sm_, sn_>& x, int i, const ValueType& vr, const ValueType& vl, const ValueType& vc, const DataType denom)
378 {
379 for(int k(0); k < image_dim_; ++k)
380 x[k][i][i] = denom * (vr[k] + vl[k] - DataType(2)*vc[k]);
381 }
382
383 // compute vector difference quotient for for hessian i != j
384 template<int image_dim_, int sl_, int sm_, int sn_>
385 static void _set_hess_quot(Tiny::Tensor3<DataType, image_dim_, domain_dim, domain_dim, sl_, sm_, sn_>& x, int i, int j, const ValueType& vne, const ValueType& vsw, const ValueType& vnw, const ValueType& vse, const DataType denom)
386 {
387 for(int k(0); k < image_dim_; ++k)
388 x[k][i][j] = x[k][j][i] = denom * ((vne[k] + vsw[k]) - (vnw[k] + vse[k]));
389 }
390
393 {
394 // difference quotient denominators
395 const DataType denom1 = DataType(1) / (h * h);
396 const DataType denom2 = DataType(1) / (DataType(4) * h * h);
397 ValueType vc, vr, vl, vne, vnw, vse, vsw;
398
399 // evaluate at point
400 vc = _func_eval.value(v);
401
402 // loop over all dimensions
403 for(int i(0); i < domain_dim; ++i)
404 {
405 // backup coord
406 const DataType vi(v[i]);
407
408 // eval f(x + h*e_i)
409 v[i] = vi + h;
410 vr = _func_eval.value(v);
411
412 // eval f(x-h)
413 v[i] = vi - h;
414 vl = _func_eval.value(v);
415
416 // compute difference quotient
417 _set_hess_quot(x, i, vr, vl, vc, denom1);
418
419 // now the mixed derivatives
420 for(int j(i+1); j < domain_dim; ++j)
421 {
422 // backup coord
423 const DataType vj(v[j]);
424
425 // we need four points here:
426 // north-east: f(v + h*e_i + h*e_j)
427 v[i] = vi + h;
428 v[j] = vj + h;
429 vne = _func_eval.value(v);
430
431 // north-west: f(v - h*e_i + h*e_j)
432 v[i] = vi - h;
433 v[j] = vj + h;
434 vnw = _func_eval.value(v);
435
436 // south-east: f(v + h*e_i - h*e_j)
437 v[i] = vi + h;
438 v[j] = vj - h;
439 vse = _func_eval.value(v);
440
441 // south-west: f(v - h*e_i - h*e_j)
442 v[i] = vi - h;
443 v[j] = vj - h;
444 vsw = _func_eval.value(v);
445
446 // combine into difference quotient
447 _set_hess_quot(x, i, j, vne, vsw, vnw, vse, denom2);
448
449 // restore coord
450 v[j] = vj;
451 }
452
453 // restore coord
454 v[i] = vi;
455 }
456 }
457 }; // class AutoDeriveBase::Evaluator<...>
458
459 protected:
465 DataType_ _init_grad_h;
467 DataType_ _init_hess_h;
468
469 public:
482 template<typename... Args_>
483 explicit AutoDerive(Args_&&... args) :
484 Function_(std::forward<Args_>(args)...),
485 _max_grad_steps(10),
486 _max_hess_steps(10),
487 _init_grad_h(1E-2),
488 _init_hess_h(1E-2)
489 {
490 }
491
505 void config_grad_extrapol(DataType_ initial_h, int max_steps)
506 {
507 XASSERTM(initial_h > Math::eps<DataType_>(), "Initial h is too small or non-positive!");
508 XASSERTM(max_steps > 0, "Invalid maximum extrapolation steps!");
509 _init_grad_h = initial_h;
510 _max_grad_steps = max_steps;
511 }
512
526 void config_hess_extrapol(DataType_ initial_h, int max_steps)
527 {
528 XASSERTM(initial_h > Math::eps<DataType_>(), "Initial h is too small or non-positive!");
529 XASSERTM(max_steps > 0, "Invalid maximum extrapolation steps!");
530 _init_hess_h = initial_h;
531 _max_hess_steps = max_steps;
532 }
533 }; // class AutoDerive<...>
534 } // namespace Analytic
535} // namespace FEAT
#define XASSERTM(expr, msg)
Assertion macro definition with custom message.
Definition: assertion.hpp:263
static constexpr int domain_dim
get our dimension
Traits_::ValueType ValueType
value type
const DataType _init_grad_h
initial h for gradient extrapolation
std::vector< HessianType > _hess
our hessian extrapolation table
std::vector< GradientType > _grad
our gradient extrapolation table
const DataType _init_hess_h
initial h for hessian extrapolation
Traits_::HessianType HessianType
hessian type
void _eval_grad_quot(GradientType &x, PointType &v, const DataType h)
evaluates the first-order difference quotients
Traits_::GradientType GradientType
gradient type
void _eval_hess_quot(HessianType &x, PointType &v, const DataType h)
evaluates the second-order difference quotients
Traits_::DataType DataType
coefficient data type
Evaluator(const AutoDerive &function)
mandatory CTOR
HessianType extrapol_hess(const PointType &point)
Computes the hessian by a Richardson extrapolation scheme.
Traits_::PointType PointType
evaluation point type
GradientType extrapol_grad(const PointType &point)
Computes the gradient by a Richardson extrapolation scheme.
Function_::template Evaluator< Traits_ > _func_eval
the original function's evaluator
Auto-Derive function wrapper class template.
Definition: auto_derive.hpp:92
static constexpr bool can_grad
we provide function gradients
DataType_ _init_grad_h
initial h for gradient extrapolation
AutoDerive(Args_ &&... args)
Standard constructor.
void config_grad_extrapol(DataType_ initial_h, int max_steps)
Configures the Gradient extrapolation scheme.
static constexpr bool can_hess
we provide function hessiants
static constexpr int domain_dim
the input function must support value computation
Definition: auto_derive.hpp:98
DataType_ _init_hess_h
initial h for hessian extrapolation
Function_::ImageType ImageType
specify our image type
int _max_grad_steps
maximum number of gradient extrapolation steps
void config_hess_extrapol(DataType_ initial_h, int max_steps)
Configures the Hessian extrapolation scheme.
int _max_hess_steps
maximum number of hessian extrapolation steps
static constexpr bool can_value
our base class provides function values
Analytic Function Evaluator base-class template.
Definition: function.hpp:157
Tiny Matrix class template.
Tiny Tensor3 class template.
Tiny Vector class template.
T_ sqr(T_ x)
Returns the square of a value.
Definition: math.hpp:95
FEAT namespace.
Definition: adjactor.hpp:12
@ value
specifies whether the space should supply basis function values