version 0.4.1
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 {
18namespace Impl {
28 double drawResultAndReturnSlope(std::string&& functionName, const std::function<double(double)>& ftfunc, bool draw,
29 int slopeOfReference);
30} // namespace Impl
35{
36 bool draw = true;
38 double tolerance = 1e-2;
39};
40
53template <typename NonlinearOperator, typename UpdateType = typename NonlinearOperator::template ParameterValue<0>>
55 NonlinearOperator& nonLinOp, CheckFlags checkFlags = CheckFlags(),
56 std::function<void(typename NonlinearOperator::template ParameterValue<0>&, const UpdateType&)> p_updateFunction =
57 [](typename NonlinearOperator::template ParameterValue<0>& a, const UpdateType& b) { a += b; }) {
58 auto& x = nonLinOp.firstParameter();
59 const auto xOld = x;
60 UpdateType b;
61 if constexpr (not std::is_floating_point_v<UpdateType>) {
62 b.resizeLike(nonLinOp.derivative());
63 b.setRandom();
64 b /= b.norm();
65 } else
66 b = 1;
67
68 nonLinOp.updateAll();
69 const auto e = nonLinOp.value();
70
71 double gradfv;
72 if constexpr (not std::is_floating_point_v<UpdateType>)
73 gradfv = nonLinOp.derivative().dot(b);
74 else
75 gradfv = nonLinOp.derivative() * b;
76
77 auto ftfunc = [&](auto t) {
78 p_updateFunction(x, t * b);
79 nonLinOp.template update<0>();
80 auto value = std::abs(nonLinOp.value() - e - t * gradfv);
81 x = xOld;
82 return value;
83 };
84
85 const double slope = Impl::drawResultAndReturnSlope("Gradient", ftfunc, checkFlags.draw, 2);
86
87 const bool checkPassed = Dune::FloatCmp::le(2.0, slope, checkFlags.tolerance);
88
89 if (checkFlags.writeSlopeStatementIfFailed and not checkPassed) {
90 spdlog::info("Gradient check:");
91 spdlog::info("The slope should be 2. It seems to be {}.", slope);
92 if (checkPassed)
93 spdlog::info("We consider this as sufficient.");
94 else
95 spdlog::info("The gradient seems wrong.");
96 }
97
98 nonLinOp.updateAll();
99 return checkPassed;
100}
101
114template <typename NonlinearOperator, typename UpdateType = typename NonlinearOperator::template ParameterValue<0>>
116 NonlinearOperator& nonLinOp, CheckFlags checkFlags = CheckFlags(),
117 std::function<void(typename NonlinearOperator::template ParameterValue<0>&, const UpdateType&)> p_updateFunction =
118 [](typename NonlinearOperator::template ParameterValue<0>& a, const UpdateType& b) { a += b; }) {
119 auto& x = nonLinOp.firstParameter();
120 const auto xOld = x;
121 UpdateType b;
122 b.resizeLike(nonLinOp.derivative().row(0).transpose());
123 b.setRandom();
124 b /= b.norm();
125
126 nonLinOp.updateAll();
127 const auto e = nonLinOp.value();
128
129 const auto jacofv = (nonLinOp.derivative() * b).eval();
130
131 auto ftfunc = [&](auto t) {
132 p_updateFunction(x, t * b);
133 nonLinOp.template update<0>();
134 auto value = (nonLinOp.value() - e - t * jacofv).norm();
135 x = xOld;
136 return value;
137 };
138
139 const double slope = Impl::drawResultAndReturnSlope("Jacobian", ftfunc, checkFlags.draw, 2);
140
141 const bool checkPassed = Dune::FloatCmp::le(2.0, slope, checkFlags.tolerance);
142
143 if (checkFlags.writeSlopeStatementIfFailed and not checkPassed) {
144 spdlog::info("Jacobian check:");
145 spdlog::info("The slope should be 2. It seems to be {}.", slope);
146 if (checkPassed)
147 spdlog::info("We consider this as sufficient.");
148 else
149 spdlog::info("The Jacobian seems wrong.");
150 }
151 nonLinOp.updateAll();
152 return checkPassed;
153}
154
167template <typename NonlinearOperator, typename UpdateType = typename NonlinearOperator::template ParameterValue<0>>
169 NonlinearOperator& nonLinOp, CheckFlags checkFlags = CheckFlags(),
170 std::function<void(typename NonlinearOperator::template ParameterValue<0>&, const UpdateType&)> p_updateFunction =
171 [](typename NonlinearOperator::template ParameterValue<0>& a, const UpdateType& b) { a += b; }) {
172 auto& x = nonLinOp.firstParameter();
173 const auto xOld = x;
174 UpdateType b;
175 if constexpr (not std::is_floating_point_v<UpdateType>) {
176 b.resizeLike(nonLinOp.derivative());
177 b.setRandom();
178 b /= b.norm();
179 } else
180 b = 1;
181
182 nonLinOp.updateAll();
183 const auto e = nonLinOp.value();
184
185 double gradfv, vhessv;
186 if constexpr (not std::is_floating_point_v<UpdateType>) {
187 gradfv = nonLinOp.derivative().dot(b);
188 vhessv = (nonLinOp.secondDerivative() * b).dot(b);
189 } else {
190 gradfv = nonLinOp.derivative() * b;
191 vhessv = nonLinOp.secondDerivative() * b * b;
192 }
193
194 auto ftfunc = [&](auto t) {
195 p_updateFunction(x, t * b);
196 nonLinOp.template update<0>();
197 auto value = std::abs(nonLinOp.value() - e - t * gradfv - 0.5 * t * t * vhessv);
198 x = xOld;
199 return value;
200 };
201
202 const double slope = Impl::drawResultAndReturnSlope("Hessian", ftfunc, checkFlags.draw, 3);
203
204 const bool checkPassed = Dune::FloatCmp::le(3.0, slope, checkFlags.tolerance);
205
206 if (checkFlags.writeSlopeStatementIfFailed and not checkPassed) {
207 spdlog::info("Hessian check:");
208 spdlog::info("The slope should be 3. It seems to be {}.", slope);
209 if (checkPassed)
210 spdlog::info("We consider this as sufficient.");
211 else
212 spdlog::info("The Hessian seems wrong.");
213 }
214 nonLinOp.updateAll();
215 return checkPassed;
216}
217} // namespace Ikarus::utils
Implementation of the findLineSegment algorithm.
void draw(const GV &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:259
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:115
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:168
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:54
Definition: algorithms.hh:17
Struct to hold flags for function checks.
Definition: functionsanitychecks.hh:35
bool draw
Definition: functionsanitychecks.hh:36
double tolerance
Definition: functionsanitychecks.hh:38
bool writeSlopeStatementIfFailed
Definition: functionsanitychecks.hh:37