4#include <alpaqa-version.h>
10#ifdef ALPAQA_HAVE_CASADI
11#include <casadi/config.h>
14#include <IpoptConfig.h>
28namespace fs = std::filesystem;
33 const auto *opts =
" [<problem-type>:][<path>/]<name> [options...]\n";
34 const auto *docs = R
"==(
36 dl: Dynamically loaded problem using the DLProblem class.
37 Specify the prefix of the registration function using the
38 problem.prefix option, e.g. problem.prefix=alpaqa_problem will look
39 for a registration function with the name alpaqa_problem_register.
40 Further options can be passed to the problem using
41 problem.<key>[=<value>].
42 cs: Load a CasADi problem using the CasADiProblem class.
43 If a .tsv file with the same name as the shared library file exists,
44 the bounds and parameters will be loaded from that file. See
45 CasADiProblem::load_numerical_data for more details.
46 The problem parameter can be set using the problem.param option.
47 cu: Load a CUTEst problem using the CUTEstProblem class.
51 Print the full gradients.
53 Seed for the random number generator.
55 std::cout << "alpaqa gradient-checker (" ALPAQA_VERSION_FULL
")\n\n"
56 " Command-line utility to check problem gradients.\n"
57 " alpaqa is published under the LGPL-3.0.\n"
58 " https://github.com/kul-optec/alpaqa"
61 << a0 << opts << docs << std::endl;
63 <<
"Third-party libraries:\n"
64 <<
" * Eigen " << EIGEN_WORLD_VERSION <<
'.' << EIGEN_MAJOR_VERSION
65 <<
'.' << EIGEN_MINOR_VERSION
66 <<
" (https://gitlab.com/libeigen/eigen) - MPL-2.0\n"
67#ifdef ALPAQA_HAVE_CASADI
68 <<
" * CasADi " CASADI_VERSION_STRING
69 " (https://github.com/casadi/casadi) - LGPL-3.0-or-later\n"
71#ifdef ALPAQA_HAVE_CUTEST
73 " (https://github.com/ralna/CUTEst) - BSD-3-Clause\n"
81 auto tok_pos = s.find(tok);
82 if (tok_pos == s.npos)
83 return std::make_tuple(std::string_view{}, s);
84 std::string_view key{s.begin(), s.begin() + tok_pos};
85 std::string_view rem{s.begin() + tok_pos + 1, s.end()};
86 return std::make_tuple(key, rem);
90 bool rel_to_exe = argv[1][0] ==
'^';
91 std::string_view prob_path_s = argv[1] +
static_cast<ptrdiff_t
>(rel_to_exe);
92 std::string_view prob_type;
93 std::tie(prob_type, prob_path_s) =
split_once(prob_path_s,
':');
94 fs::path prob_path{prob_path_s};
96 prob_path = fs::canonical(fs::path(argv[0])).parent_path() / prob_path;
97 return std::make_tuple(std::move(prob_path), prob_type);
107int main(
int argc,
const char *argv[])
try {
115 std::span args{argv,
static_cast<size_t>(argc)};
116 Options opts{argc - 2, argv + 2};
119 std::ostream &os = std::cout;
125 os <<
"Loading problem " << prob_path << std::endl;
126 auto problem =
load_problem(prob_type, prob_path.parent_path(),
127 prob_path.filename(), opts);
128 os <<
"Loaded problem " << problem.path.stem().c_str() <<
" from "
129 << problem.path <<
"\nnvar: " << problem.problem.get_n()
130 <<
"\nncon: " << problem.problem.get_m() <<
"\nProvided functions:\n";
135 auto has_opt = [&opts](std::string_view o) {
136 auto o_it = std::ranges::find(opts.options(), o);
137 if (o_it == opts.options().end())
139 auto index =
static_cast<size_t>(o_it - opts.options().begin());
140 opts.used()[index] =
true;
148 auto seed =
static_cast<unsigned int>(std::time(
nullptr));
153 auto used = opts.used();
154 auto unused_opt = std::ranges::find(used,
false);
155 auto unused_idx =
static_cast<size_t>(unused_opt - used.begin());
156 if (unused_opt != used.end())
157 throw std::invalid_argument(
"Unused option: " +
158 std::string(opts.options()[unused_idx]));
163}
catch (std::exception &e) {
165 << e.what() << std::endl;
170 const auto n = x.size();
172 vec h = vec::Zero(n);
174 const auto δ = 1e-2 * ε;
175 for (index_t i = 0; i < n; ++i) {
176 real_t hh = std::abs(x(i)) * ε > δ ? x(i) * ε : δ;
178 grad.coeffRef(i) = (f(x + h) - f(x)) / hh;
186 auto &te_problem = lproblem.
problem;
191 auto n = te_problem.get_n(), m = te_problem.get_m();
193 std::srand(
static_cast<unsigned int>(std::time(
nullptr)));
194 vec Σ = 1.5 * vec::Random(m).array() + 2;
195 vec y = y0 + y0.norm() * vec::Random(m);
196 vec x = x0 + sc * vec::Random(n);
197 vec v = 5e-6 * sc * vec::Random(n);
201 auto print_compare = [&log, &opts](
const auto &fd,
const auto &ad) {
202 auto abs_err = (fd - ad).
template lpNorm<Eigen::Infinity>();
203 auto rel_err = abs_err / fd.template lpNorm<Eigen::Infinity>();
212 auto f = [&](crvec x) {
return te_problem.eval_f(x); };
213 log <<
"Gradient verification: ∇f(x)\n";
216 te_problem.eval_grad_f(x, grad_f);
217 print_compare(fd_grad_f, grad_f);
219 log <<
"Gradient verification: ∇L(x)\n";
220 auto L = [&](crvec x) {
221 te_problem.eval_g(x, gx);
222 return te_problem.eval_f(x) + gx.dot(y);
226 te_problem.eval_grad_L(x, y, grad_L, wn);
227 print_compare(fd_grad_L, grad_L);
229 log <<
"Gradient verification: ∇ψ(x)\n";
230 auto ψ = [&](crvec x) {
return te_problem.eval_ψ(x, y, Σ, wm); };
233 te_problem.eval_grad_ψ(x, y, Σ, grad_ψ, wn, wm);
234 print_compare(fd_grad_ψ, grad_ψ);
236 if (te_problem.provides_eval_hess_ψ_prod()) {
237 log <<
"Hessian product verification: ∇²ψ(x)\n";
240 te_problem.eval_grad_ψ(xv, y, Σ, grad_ψv, wn, wm);
241 vec fd_hess_ψv = grad_ψv - grad_ψ;
243 te_problem.eval_hess_ψ_prod(x, y, Σ, 1, v, hess_ψv);
244 print_compare(fd_hess_ψv, hess_ψv);
#define USING_ALPAQA_CONFIG(Conf)
std::string demangled_typename(const std::type_info &t)
Get the pretty name of the given type as a string.
auto get_problem_path(const char *const *argv)
void print_usage(const char *a0)
auto split_once(std::string_view s, char tok='.')
Split the string s on the first occurrence of tok.
int main(int argc, const char *argv[])
vec finite_diff(const std::function< real_t(crvec)> &f, crvec x)
void check_gradients(LoadedProblem &, std::ostream &, const CheckGradientsOpts &)
void print_provided_functions(std::ostream &os, const TypeErasedProblem< Conf > &problem)
std::ostream & print_matlab(std::ostream &os, const Eigen::Ref< const Eigen::MatrixX< float > > &M, std::string_view end)
std::string float_to_str(F value, int precision)
decltype(auto) set_params(T &t, std::string_view prefix, Options &opts)
LoadedProblem load_problem(std::string_view type, const fs::path &dir, const fs::path &file, Options &opts)
vec initial_guess_y
Unknowns.
alpaqa::TypeErasedProblem< config_t > problem
Double-precision double configuration.