version 0.4
functionsanitychecks.hh
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2021-2024 The Ikarus Developers mueller@ibb.uni-stuttgart.de
2// SPDX-License-Identifier: LGPL-3.0-or-later
3
9#pragma once
10#include "findlinesegment.hh"
11
12#include <iostream>
13
14#include <dune/common/float_cmp.hh>
15
16#include <spdlog/spdlog.h>
17namespace Ikarus::utils {
18 namespace Impl {
28 double drawResultAndReturnSlope(std::string&& functionName, const std::function<double(double)>& ftfunc, bool draw,
29 int slopeOfReference);
30 } // namespace Impl
34 struct CheckFlags {
35 bool draw = true;
37 double tolerance = 1e-2;
38 };
39
52 template <typename NonlinearOperator, typename UpdateType = typename NonlinearOperator::template ParameterValue<0>>
54 NonlinearOperator& nonLinOp, CheckFlags checkFlags = CheckFlags(),
55 std::function<void(typename NonlinearOperator::template ParameterValue<0>&, const UpdateType&)> p_updateFunction
56 = [](typename NonlinearOperator::template ParameterValue<0>& a, const UpdateType& b) { a += b; }) {
57 auto& x = nonLinOp.firstParameter();
58 const auto xOld = x;
59 UpdateType b;
60 if constexpr (not std::is_floating_point_v<UpdateType>) {
61 b.resizeLike(nonLinOp.derivative());
62 b.setRandom();
63 b /= b.norm();
64 } else
65 b = 1;
66
67 nonLinOp.updateAll();
68 const auto e = nonLinOp.value();
69
70 double gradfv;
71 if constexpr (not std::is_floating_point_v<UpdateType>)
72 gradfv = nonLinOp.derivative().dot(b);
73 else
74 gradfv = nonLinOp.derivative() * b;
75
76 auto ftfunc = [&](auto t) {
77 p_updateFunction(x, t * b);
78 nonLinOp.template update<0>();
79 auto value = std::abs(nonLinOp.value() - e - t * gradfv);
80 x = xOld;
81 return value;
82 };
83
84 const double slope = Impl::drawResultAndReturnSlope("Gradient", ftfunc, checkFlags.draw, 2);
85
86 const bool checkPassed = Dune::FloatCmp::le(2.0, slope, checkFlags.tolerance);
87
88 if (checkFlags.writeSlopeStatementIfFailed and not checkPassed) {
89 spdlog::info("Gradient check:");
90 spdlog::info("The slope should be 2. It seems to be {}.", slope);
91 if (checkPassed)
92 spdlog::info("We consider this as sufficient.");
93 else
94 spdlog::info("The gradient seems wrong.");
95 }
96
97 nonLinOp.updateAll();
98 return checkPassed;
99 }
100
113 template <typename NonlinearOperator, typename UpdateType = typename NonlinearOperator::template ParameterValue<0>>
115 NonlinearOperator& nonLinOp, CheckFlags checkFlags = CheckFlags(),
116 std::function<void(typename NonlinearOperator::template ParameterValue<0>&, const UpdateType&)> p_updateFunction
117 = [](typename NonlinearOperator::template ParameterValue<0>& a, const UpdateType& b) { a += b; }) {
118 auto& x = nonLinOp.firstParameter();
119 const auto xOld = x;
120 UpdateType b;
121 b.resizeLike(nonLinOp.derivative().row(0).transpose());
122 b.setRandom();
123 b /= b.norm();
124
125 nonLinOp.updateAll();
126 const auto e = nonLinOp.value();
127
128 const auto jacofv = (nonLinOp.derivative() * b).eval();
129
130 auto ftfunc = [&](auto t) {
131 p_updateFunction(x, t * b);
132 nonLinOp.template update<0>();
133 auto value = (nonLinOp.value() - e - t * jacofv).norm();
134 x = xOld;
135 return value;
136 };
137
138 const double slope = Impl::drawResultAndReturnSlope("Jacobian", ftfunc, checkFlags.draw, 2);
139
140 const bool checkPassed = Dune::FloatCmp::le(2.0, slope, checkFlags.tolerance);
141
142 if (checkFlags.writeSlopeStatementIfFailed and not checkPassed) {
143 spdlog::info("Jacobian check:");
144 spdlog::info("The slope should be 2. It seems to be {}.", slope);
145 if (checkPassed)
146 spdlog::info("We consider this as sufficient.");
147 else
148 spdlog::info("The Jacobian seems wrong.");
149 }
150 nonLinOp.updateAll();
151 return checkPassed;
152 }
153
166 template <typename NonlinearOperator, typename UpdateType = typename NonlinearOperator::template ParameterValue<0>>
168 NonlinearOperator& nonLinOp, CheckFlags checkFlags = CheckFlags(),
169 std::function<void(typename NonlinearOperator::template ParameterValue<0>&, const UpdateType&)> p_updateFunction
170 = [](typename NonlinearOperator::template ParameterValue<0>& a, const UpdateType& b) { a += b; }) {
171 auto& x = nonLinOp.firstParameter();
172 const auto xOld = x;
173 UpdateType b;
174 if constexpr (not std::is_floating_point_v<UpdateType>) {
175 b.resizeLike(nonLinOp.derivative());
176 b.setRandom();
177 b /= b.norm();
178 } else
179 b = 1;
180
181 nonLinOp.updateAll();
182 const auto e = nonLinOp.value();
183
184 double gradfv, vhessv;
185 if constexpr (not std::is_floating_point_v<UpdateType>) {
186 gradfv = nonLinOp.derivative().dot(b);
187 vhessv = (nonLinOp.secondDerivative() * b).dot(b);
188 } else {
189 gradfv = nonLinOp.derivative() * b;
190 vhessv = nonLinOp.secondDerivative() * b * b;
191 }
192
193 auto ftfunc = [&](auto t) {
194 p_updateFunction(x, t * b);
195 nonLinOp.template update<0>();
196 auto value = std::abs(nonLinOp.value() - e - t * gradfv - 0.5 * t * t * vhessv);
197 x = xOld;
198 return value;
199 };
200
201 const double slope = Impl::drawResultAndReturnSlope("Hessian", ftfunc, checkFlags.draw, 3);
202
203 const bool checkPassed = Dune::FloatCmp::le(3.0, slope, checkFlags.tolerance);
204
205 if (checkFlags.writeSlopeStatementIfFailed and not checkPassed) {
206 spdlog::info("Hessian check:");
207 spdlog::info("The slope should be 3. It seems to be {}.", slope);
208 if (checkPassed)
209 spdlog::info("We consider this as sufficient.");
210 else
211 spdlog::info("The Hessian seems wrong.");
212 }
213 nonLinOp.updateAll();
214 return checkPassed;
215 }
216} // namespace Ikarus::utils
Implementation of the findLineSegment algorithm.
void draw(const GridView &gridView, bool forever=false)
Draw function for visualizing the elements of a DUNE grid view.
Definition: griddrawer.hh:31
auto norm(const Eigen::MatrixBase< Derived > &v)
Adding free norm function to Eigen types.
Definition: linearalgebrahelper.hh:258
bool checkJacobian(NonlinearOperator &nonLinOp, CheckFlags checkFlags=CheckFlags(), std::function< void(typename NonlinearOperator::template ParameterValue< 0 > &, const UpdateType &)> p_updateFunction=[](typename NonlinearOperator::template ParameterValue< 0 > &a, const UpdateType &b) { a+=b;})
Checks the Jacobian of a nonlinear operator.
Definition: functionsanitychecks.hh:114
bool checkHessian(NonlinearOperator &nonLinOp, CheckFlags checkFlags=CheckFlags(), std::function< void(typename NonlinearOperator::template ParameterValue< 0 > &, const UpdateType &)> p_updateFunction=[](typename NonlinearOperator::template ParameterValue< 0 > &a, const UpdateType &b) { a+=b;})
Checks the Hessian of a nonlinear operator.
Definition: functionsanitychecks.hh:167
bool checkGradient(NonlinearOperator &nonLinOp, CheckFlags checkFlags=CheckFlags(), std::function< void(typename NonlinearOperator::template ParameterValue< 0 > &, const UpdateType &)> p_updateFunction=[](typename NonlinearOperator::template ParameterValue< 0 > &a, const UpdateType &b) { a+=b;})
Checks the gradient of a nonlinear operator.
Definition: functionsanitychecks.hh:53
Definition: algorithms.hh:17
Struct to hold flags for function checks.
Definition: functionsanitychecks.hh:34
bool draw
Definition: functionsanitychecks.hh:35
double tolerance
Definition: functionsanitychecks.hh:37
bool writeSlopeStatementIfFailed
Definition: functionsanitychecks.hh:36