117int main(
int argc,
const char *argv[])
try {
119 SetConsoleOutputCP(CP_UTF8);
128 std::span args{argv,
static_cast<size_t>(argc)};
129 Options opts{argc - 2, argv + 2};
132 std::ostream &os = std::cout;
138 os <<
"Loading problem " << prob_path << std::endl;
139 auto problem =
load_problem(prob_type, prob_path.parent_path(),
140 prob_path.filename(), opts);
141 os <<
"Loaded problem " << problem.path.stem().string() <<
" from "
142 << problem.path <<
"\nnvar: " << problem.problem.get_n()
143 <<
"\nncon: " << problem.problem.get_m() <<
"\nProvided functions:\n";
148 auto has_opt = [&opts](std::string_view o) {
149 auto o_it = std::ranges::find(opts.options(), o);
150 if (o_it == opts.options().end())
152 auto index =
static_cast<size_t>(o_it - opts.options().begin());
153 ++opts.used()[index];
158 .hessians = !has_opt(
"--no-hessians"),
159 .scale_perturbations = 1e-2,
161 set_params(cg_opts.scale_perturbations,
"--scale-perturbations", opts);
164 auto seed =
static_cast<unsigned int>(std::time(
nullptr));
169 auto used = opts.used();
170 auto unused_opt = std::ranges::find(used, 0);
171 auto unused_idx =
static_cast<size_t>(unused_opt - used.begin());
172 if (unused_opt != used.end())
173 throw std::invalid_argument(
"Unused option: " +
174 std::string(opts.options()[unused_idx]));
179}
catch (std::exception &e) {
181 << e.what() << std::endl;
231 const auto n = x.size();
232 std::vector<Eigen::Triplet<real_t, index_t>> coo;
233 vec h = vec::Zero(n);
235 const auto δ = 1e-2 * ε;
236 vec grad_x(n), grad_xh(n);
238 for (index_t i = 0; i < n; ++i) {
239 real_t hh = std::abs(x(i)) * ε > δ ? x(i) * ε : δ;
241 grad_L(x + h, grad_xh);
242 grad_xh = (grad_xh - grad_x) / hh;
243 for (index_t j = 0; j < n; ++j)
244 if (real_t v = grad_xh(j); v != 0)
245 coo.emplace_back(std::min(j, i), std::max(i, j),
246 v * (i == j ? 1 : 0.5));
249 Eigen::SparseMatrix<real_t, 0, index_t> hess(n, n);
250 hess.setFromTriplets(coo.begin(), coo.end());
274 auto &te_problem = lproblem.
problem;
279 auto n = te_problem.get_n(), m = te_problem.get_m();
281 std::srand(
static_cast<unsigned int>(std::time(
nullptr)));
282 vec Σ = 1.5 * vec::Random(m).array() + 2;
284 vec x = x0 + sc * vec::Random(n);
285 vec v = 5e-6 * sc * vec::Random(n);
289 auto print_compare = [&log, &opts](
const auto &other,
const auto &ref) {
291 (ref - other).reshaped().template lpNorm<Eigen::Infinity>();
293 abs_err / ref.reshaped().template lpNorm<Eigen::Infinity>();
301 auto print_compare_scal = [&log, &opts](
const auto &fd,
const auto &ad) {
302 auto abs_err = std::abs(fd - ad);
303 auto rel_err = abs_err / std::abs(fd);
312 auto f = [&](crvec x) {
return te_problem.eval_f(x); };
313 log <<
"Gradient verification: ∇f(x) (grad_f compared to finite "
314 "differences of f)\n";
317 te_problem.eval_grad_f(x, grad_f);
318 print_compare(grad_f, fd_grad_f);
320 if (te_problem.provides_eval_f_grad_f()) {
321 log <<
"Gradient verification: ∇f(x) (f_grad_f compared to grad_f)\n";
323 auto f2 = te_problem.eval_f_grad_f(x, f_grad_f);
324 print_compare(f_grad_f, grad_f);
325 log <<
"Function verification: f(x) (f_grad_f compared to f)\n";
326 print_compare_scal(f2, fx);
329 log <<
"Gradient verification: ∇L(x) (grad_L compared to finite "
330 "differences of f + yᵀg)\n";
331 auto L = [&](crvec x) {
332 te_problem.eval_g(x, gx);
333 return te_problem.eval_f(x) + gx.dot(y);
337 te_problem.eval_grad_L(x, y, grad_L, wn);
338 print_compare(grad_L, fd_grad_L);
340 log <<
"Gradient verification: ∇ψ(x) (grad_ψ compared to finite "
341 "differences of ψ)\n";
342 auto ψ = [&](crvec x) {
return te_problem.eval_ψ(x, y, Σ, wm); };
345 te_problem.eval_grad_ψ(x, y, Σ, grad_ψ, wn, wm);
346 print_compare(grad_ψ, fd_grad_ψ);
348 log <<
"Gradient verification: ∇ψ(x) (grad_ψ compared to reference "
349 "implementation based on g, ∇f, ∇g)\n";
350 vec grad_ψ_default(n);
353 print_compare(grad_ψ, grad_ψ_default);
354 log <<
"Function verification: ψ(x) (ψ compared to reference "
355 "implementation based on f, g)\n";
356 print_compare_scal(ψx, ψ_default);
358 if (te_problem.provides_eval_ψ_grad_ψ()) {
359 log <<
"Gradient verification: ∇ψ(x) (grad_ψ compared to ψ_grad_ψ)\n";
361 real_t ψ2 = te_problem.eval_ψ_grad_ψ(x, y, Σ, ψ_grad_ψ, wn, wm);
362 print_compare(grad_ψ, ψ_grad_ψ);
363 log <<
"Function verification: ψ(x) (ψ compared to ψ_grad_ψ)\n";
364 print_compare_scal(ψx, ψ2);
367 if (te_problem.provides_eval_hess_L_prod()) {
368 log <<
"Hessian product verification: ∇²L(x) (hess_L_prod compared to "
369 "finite differences of grad_L)\n";
372 te_problem.eval_grad_L(xv, y, grad_Lv, wn);
373 vec fd_hess_Lv = grad_Lv - grad_L;
375 te_problem.eval_hess_L_prod(x, y, 1, v, hess_Lv);
376 print_compare(hess_Lv, fd_hess_Lv);
379 if (te_problem.provides_eval_hess_ψ_prod()) {
380 log <<
"Hessian product verification: ∇²ψ(x) (hess_ψ_prod compared to "
381 "finite differences of grad_ψ)\n";
384 te_problem.eval_grad_ψ(xv, y, Σ, grad_ψv, wn, wm);
385 vec fd_hess_ψv = grad_ψv - grad_ψ;
387 te_problem.eval_hess_ψ_prod(x, y, Σ, 1, v, hess_ψv);
388 print_compare(hess_ψv, fd_hess_ψv);
391 if (opts.
hessians && te_problem.provides_eval_hess_L()) {
392 log <<
"Hessian verification: ∇²L(x) (hess_L compared to finite "
393 "differences of grad_L)\n";
395 auto sparsity = te_problem.get_hess_L_sparsity();
396 sp::SparsityConverter<sp::Sparsity<config_t>, sp::Dense<config_t>> cvt{
399 auto eval_h = [&](rvec v) { te_problem.eval_hess_L(x, y, 1., v); };
400 cvt.convert_values(eval_h, hess_L.reshaped());
402 [&](crvec x, rvec g) { te_problem.eval_grad_L(x, y, g, wn); }, x);
403 print_compare(hess_L, fd_hess_L);
406 if (opts.
hessians && te_problem.provides_eval_hess_ψ()) {
407 log <<
"Hessian verification: ∇²ψ(x) (hess_ψ compared to finite "
408 "differences of grad_ψ)\\n";
410 auto sparsity = te_problem.get_hess_ψ_sparsity();
411 sp::SparsityConverter<sp::Sparsity<config_t>, sp::Dense<config_t>> cvt{
414 auto eval_h = [&](rvec v) { te_problem.eval_hess_ψ(x, y, Σ, 1., v); };
415 cvt.convert_values(eval_h, hess_ψ.reshaped());
417 [&](crvec x, rvec g) {
418 te_problem.eval_grad_ψ(x, y, Σ, g, wn, wm);
421 print_compare(hess_ψ, fd_hess_ψ);