11#include <alpaqa-version.h>
24#ifdef ALPAQA_WITH_EXTERNAL_CASADI
25#include <casadi/config.h>
27#ifdef ALPAQA_WITH_JSON
28#include <nlohmann/json_fwd.hpp>
30#ifdef ALPAQA_WITH_IPOPT
31#include <IpoptConfig.h>
45namespace fs = std::filesystem;
46using namespace std::string_view_literals;
52 dl: Dynamically loaded problem using the DLProblem class.
53 Specify the name of the registration function using the
54 problem.register option, e.g. problem.register=register_alpaqa_problem.
55 Further options can be passed to the problem using
56 problem.<key>[=<value>].
57 cs: Load a CasADi problem using the CasADiProblem class.
58 If a .tsv file with the same name as the shared library file exists,
59 the bounds and parameters will be loaded from that file. See
60 CasADiProblem::load_numerical_data for more details.
61 The problem parameter can be set using the problem.param option.
62 cu: Load a CUTEst problem using the CUTEstProblem class.
66 PANOC solver with the given direction.
67 Directions include: lbfgs, struclbfgs, anderson, convex-newton.
68 zerofpr[.<direction>]:
69 ZeroFPR solver, supports the same directions as PANOC.
71 PANTR solver. Requires products with the Hessian of the augmented
72 Lagrangian (unless dir.finite_diff=true).
74 FISTA (fast iterative shrinkage-thresholding algorithm). Only for
77 Ipopt interior point solver. Requires Jacobian of the constraints
78 and Hessian of the Lagrangian (unless finite memory is enabled).
80 QPALM proximal ALM QP solver. Assumes that the problem is a QP.
81 Requires Jacobian of the constraints and Hessian of the Lagrangian.
84 Solver-specific options can be specified as key-value pairs, where the
85 keys use periods to access struct members. For example,
86 solver.Lipschitz.L_0=1e3.
88 solver: Parameters for the main (inner) solver.
89 alm: Parameters for the outer ALM solver (if applicable).
90 dir: Parameters for solver's direction provider (if applicable).
91 accel: Parameters for direction's accelerator (if applicable).
92 out: File to write output to (default: -, i.e. standard output).
93 sol: Folder to write the solutions (and optional statistics) to.
94 x0: Initial guess for the solution.
95 mul_g0: Initial guess for the multipliers of the general constraints.
96 mul_x0: Initial guess for the multipliers of the bound constraints on x.
97 num_exp: Repeat the experiment this many times for more accurate timings.
98 extra_stats: Log more per-iteration solver statistics, such as step sizes,
99 Newton step acceptance, and residuals. Requires `sol' to be set.
100 show_funcs: Print an overview of the functions provided by the problem.
101 dl_flags: Flags passed to dlopen when loading the problem.
103 The prefix @ can be added to the values of x0, mul_g0 and mul_x0 to read
104 the values from the given CSV file.
106 Options can be loaded from a JSON file by using an @ prefix. For example,
107 an argument @options.json loads the options from a file options.json in the
108 current directory. Multiple JSON files are processed in the order they
109 appear in the command line arguments. Options specified on the command line
110 always have precedence over options in a JSON file, regardless of order.
113 alpaqa-driver problem.so \
114 problem.register=register_alpaqa_problem \
115 problem.custom_arg=foo \
116 method=panoc.struclbfgs \
118 alm.{tolerance,dual_tolerance}=1e-8 \
119 solver.print_interval=50 \
122 alpaqa-driver cs:build/casadi_problem.so \
123 @options/default.json \
124 problem.param=1,2,3 \
126 solver.tol=1e-8 solver.constr_viol_tol=1e-8 \
127 solver.warm_start_init_point=yes \
129 mul_g0=@/some/other/file.csv \
130 mul_x0=@/yet/another/file.csv
134 const auto *opts =
" [<problem-type>:][<path>/]<name> [method=<solver>] "
135 "[<key>=<value>...]\n";
136 std::cout <<
"alpaqa-driver " ALPAQA_VERSION_FULL
" (" << alpaqa_build_time
138 " Command-line interface to the alpaqa solvers.\n"
139 " alpaqa is published under the LGPL-3.0.\n"
140 " https://github.com/kul-optec/alpaqa"
143 << a0 << opts <<
docs << std::endl;
144 std::cout <<
"Third-party libraries:\n"
145 <<
" * Eigen " << EIGEN_WORLD_VERSION <<
'.'
146 << EIGEN_MAJOR_VERSION <<
'.' << EIGEN_MINOR_VERSION
147 <<
" (https://gitlab.com/libeigen/eigen) - MPL-2.0\n"
148#ifdef ALPAQA_WITH_EXTERNAL_CASADI
149 <<
" * CasADi " CASADI_VERSION_STRING
150 " (https://github.com/casadi/casadi) - LGPL-3.0-or-later\n"
152#ifdef ALPAQA_WITH_CUTEST
154 " (https://github.com/ralna/CUTEst) - BSD-3-Clause\n"
156#ifdef ALPAQA_WITH_LBFGSB
157 <<
" * L-BFGS-B 3.0 "
158 "(http://users.iems.northwestern.edu/~nocedal/lbfgsb.html) - "
161#ifdef ALPAQA_WITH_IPOPT
162 <<
" * Ipopt " IPOPT_VERSION
163 " (https://github.com/coin-or/Ipopt) - EPL-2.0\n"
164 <<
" * MUMPS (https://mumps-solver.org) - CECILL-C\n"
165 <<
" * OpenBLAS (https://github.com/OpenMathLib/OpenBLAS) - "
168#ifdef ALPAQA_WITH_QPALM
169 <<
" * QPALM " QPALM_VERSION_STR
170 " (https://github.com/kul-optec/QPALM) - LGPL-3.0\n"
172#ifdef ALPAQA_WITH_JSON
173 <<
" * nlohmann/json " << NLOHMANN_JSON_VERSION_MAJOR <<
'.'
174 << NLOHMANN_JSON_VERSION_MINOR <<
'.'
175 << NLOHMANN_JSON_VERSION_PATCH
176 <<
" (https://github.com/nlohmann/json) - MIT\n"
182 std::cout << ALPAQA_VERSION_FULL
" (" << ALPAQA_BUILD_TIME <<
")\n";
188 auto tok_pos = s.find(tok);
189 if (tok_pos == s.npos)
190 return std::make_tuple(std::string_view{}, s);
191 std::string_view key{s.begin(), s.begin() + tok_pos};
192 std::string_view rem{s.begin() + tok_pos + 1, s.end()};
193 return std::make_tuple(key, rem);
197 std::string out_path =
"-";
200 if (out_fstream.open(out_path); !out_fstream)
201 throw std::runtime_error(
"Unable to open '" + out_path +
"'");
202 return out_fstream.is_open() ? out_fstream : std::cout;
206 std::string sol_path;
212 bool rel_to_exe = argv[1][0] ==
'^';
213 std::string_view prob_path_s = argv[1] +
static_cast<ptrdiff_t
>(rel_to_exe);
214 std::string_view prob_type;
215 std::tie(prob_type, prob_path_s) =
split_once(prob_path_s,
':');
216 fs::path prob_path{prob_path_s};
218 prob_path = fs::canonical(fs::path(argv[0])).parent_path() / prob_path;
219 return std::make_tuple(std::move(prob_path), prob_type);
224 os <<
"Loaded problem \"" << problem.
name <<
"\"\n"
225 <<
"Number of variables: " << problem.
problem.
get_n() <<
"\n"
226 <<
"Number of constraints: " << problem.
problem.
get_m() <<
"\n";
228 os <<
"Nonzeros in Jg: " << *problem.
nnz_jac_g <<
"\n";
230 os <<
"Nonzeros in ∇²L: " << *problem.
nnz_hess_L <<
"\n";
232 os <<
"Nonzeros in ∇²ψ: " << *problem.
nnz_hess_ψ <<
"\n";
234 os <<
"Box constraints:"
241 os <<
"General constraints:"
248 os <<
"Provided functions:\n";
254 std::string method =
"panoc", direction;
258 std::map<std::string_view, solver_builder_func> solvers{
265 auto solver_it = solvers.find(method);
266 if (solver_it == solvers.end())
267 throw std::invalid_argument(
268 "Unknown solver '" + std::string(method) +
"'\n" +
269 " Available solvers: " +
272 return std::make_tuple(std::move(solver_it->second), direction);
277 [[maybe_unused]]
const Options &opts,
278 std::span<const char *> argv) {
280 auto timestamp_str = std::to_string(results.
timestamp);
283 if (name ==
"PROBLEM")
285 auto suffix =
'_' + name +
'_' + timestamp_str +
'_' + rnd_str;
286 fs::create_directories(sol_output_dir);
287 std::array solutions{
288 std::tuple{
"solution",
"sol_x", &sol_res.solution},
289 std::tuple{
"multipliers for g",
"mul_g", &sol_res.multipliers},
290 std::tuple{
"multipliers for x",
"mul_x", &sol_res.multipliers_bounds},
292 for (
auto [name, fname, value] : solutions) {
293 if (value->size() == 0)
295 auto pth = sol_output_dir / (std::string(fname) + suffix +
".csv");
296 os <<
"Writing " << name <<
" to " << pth << std::endl;
297 std::ofstream output_file(pth);
301 auto pth = sol_output_dir / (
"cmdline" + suffix +
".txt");
302 os <<
"Writing arguments to " << pth << std::endl;
303 std::ofstream output_file(pth);
304 for (
const char *arg : argv)
305 output_file << std::quoted(arg,
'\'') <<
' ';
308 if (solver->has_statistics()) {
309 auto pth = sol_output_dir / (
"stats" + suffix +
".csv");
310 os <<
"Writing statistics to " << pth << std::endl;
311 std::ofstream output_file(pth);
312 solver->write_statistics_to_stream(output_file);
316 auto pth = sol_output_dir / (
"options" + suffix +
".json");
317 os <<
"Writing options to " << pth << std::endl;
318 std::ofstream output_file(pth);
319 output_file << opts.get_json_out() <<
'\n';
324int main(
int argc,
const char *argv[])
try {
326 SetConsoleOutputCP(CP_UTF8);
335 if (argv[1] ==
"-h"sv || argv[1] ==
"--help"sv || argv[1] ==
"?"sv)
337 if (argv[1] ==
"-v"sv || argv[1] ==
"--version"sv)
339 if (argv[1] ==
"--complete"sv) {
346 std::span args{argv,
static_cast<size_t>(argc)};
347 Options opts{argc - 2, argv + 2};
350 std::ofstream out_fstream;
363 auto solver = solver_builder(direction, opts);
366 os <<
"Loading " << prob_path <<
" ..." << std::endl;
367 auto problem =
load_problem(prob_type, prob_path.parent_path(),
368 prob_path.filename(), opts);
370 bool show_funcs =
false;
376 auto used = opts.used();
377 auto unused_opt = std::ranges::find(used, 0);
378 auto unused_idx =
static_cast<size_t>(unused_opt - used.begin());
379 if (unused_opt != used.end())
380 throw std::invalid_argument(
"Unused option: " +
381 std::string(opts.options()[unused_idx]));
384 auto solver_results = solver->run(problem, os);
387 real_t f = problem.problem.eval_f(solver_results.solution);
389 problem.problem, solver_results.solution, solver_results.multipliers);
392 .solver_results = solver_results,
393 .objective = f + solver_results.h,
394 .smooth_objective = f,
396 .options = opts.options(),
397 .timestamp = timestamp_ms<std::chrono::system_clock>().count(),
404 if (!sol_output_dir.empty())
407}
catch (std::exception &e) {
409 << e.what() << std::endl;
auto get_solver_builder(Options &opts)
std::string get_output_paths(Options &opts)
std::ostream & get_output_stream(Options &opts, std::ofstream &out_fstream)
auto get_problem_path(const char *const *argv)
void print_usage(const char *a0)
void print_problem_description(std::ostream &os, LoadedProblem &problem, bool show_funcs)
void store_solution(const fs::path &sol_output_dir, std::ostream &os, BenchmarkResults &results, auto &solver, const Options &opts, std::span< const char * > argv)
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[])
length_t get_n() const
[Required] Number of decision variables.
length_t get_m() const
[Required] Number of constraints.
#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.
SharedSolverWrapper make_fista_driver(std::string_view direction, Options &opts)
void print_provided_functions(std::ostream &os, const TypeErasedProblem< Conf > &problem)
SharedSolverWrapper make_ipopt_driver(std::string_view, Options &)
SharedSolverWrapper make_lbfgsb_driver(std::string_view, Options &)
auto split_key(std::string_view full, char tok='.')
Split the string full on the first occurrence of tok.
std::string join(std::ranges::input_range auto strings, join_opt opt={})
Join the list of strings into a single string, using the separator given by opt.
std::ostream & print_csv(std::ostream &os, const Eigen::DenseBase< Derived > &M, Args &&...args)
KKTError< Conf > compute_kkt_error(const TypeErasedProblem< Conf > &problem, crvec< Conf > x, crvec< Conf > y)
void set_params(T &t, std::string_view prefix, Options &opts)
SharedSolverWrapper make_zerofpr_driver(std::string_view direction, Options &opts)
SharedSolverWrapper make_panoc_driver(std::string_view direction, Options &opts)
SharedSolverWrapper make_pantr_driver(std::string_view direction, Options &opts)
void print_completion(std::string_view method, std::string_view params)
LoadedProblem load_problem(std::string_view type, const fs::path &dir, const fs::path &file, Options &opts)
std::optional< length_t > nnz_hess_L
std::optional< ConstrCount > general_constr_count
std::optional< length_t > nnz_hess_ψ
alpaqa::TypeErasedProblem< config_t > problem
std::optional< ConstrCount > box_constr_count
std::optional< length_t > nnz_jac_g
SharedSolverWrapper make_qpalm_driver(std::string_view, Options &)
std::string random_hex_string(auto &&rng)
void print_results(std::ostream &os, const BenchmarkResults &results)
SolverResults solver_results
Double-precision double configuration.