10#include <alpaqa-version.h>
22#ifdef ALPAQA_HAVE_CASADI
23#include <casadi/config.h>
26#include <IpoptConfig.h>
40namespace fs = std::filesystem;
46 dl: Dynamically loaded problem using the DLProblem class.
47 Specify the name of the registration function using the
48 problem.register option, e.g. problem.register=register_alpaqa_problem.
49 Further options can be passed to the problem using
50 problem.<key>[=<value>].
51 cs: Load a CasADi problem using the CasADiProblem class.
52 If a .tsv file with the same name as the shared library file exists,
53 the bounds and parameters will be loaded from that file. See
54 CasADiProblem::load_numerical_data for more details.
55 The problem parameter can be set using the problem.param option.
56 cu: Load a CUTEst problem using the CUTEstProblem class.
60 PANOC solver with the given direction.
61 Directions include: lbfgs, struclbfgs, anderson.
62 zerofpr[.<direction>]:
63 ZeroFPR solver, supports the same directions as PANOC.
65 PANTR solver. Requires products with the Hessian of the augmented
66 Lagrangian (unless dir.finite_diff=true).
68 Ipopt interior point solver. Requires Jacobian of the constraints
69 and Hessian of the Lagrangian (unless finite memory is enabled).
71 QPALM proximal ALM QP solver. Assumes that the problem is a QP.
72 Requires Jacobian of the constraints and Hessian of the Lagrangian.
75 Solver-specific options can be specified as key-value pairs, where the
76 keys use periods to access struct members. For example,
77 solver.Lipschitz.L_0=1e3.
79 solver: Parameters for the main (inner) solver.
80 alm: Parameters for the outer ALM solver (if applicable).
81 dir: Parameters for solver's direction provider (if applicable).
82 accel: Parameters for direction's accelerator (if applicable).
83 out: File to write output to (default: -, i.e. standard output).
84 sol: Folder to write the solutions (and optional statistics) to.
85 x0: Initial guess for the solution.
86 mul_g0: Initial guess for the multipliers of the general constraints.
87 mul_x0: Initial guess for the multipliers of the bound constraints on x.
88 num_exp: Repeat the experiment this many times for more accurate timings.
89 extra_stats: Log more per-iteration solver statistics, such as step sizes,
90 Newton step acceptance, and residuals. Requires `sol' to be set.
92 The prefix @ can be added to the values of x0, mul_g0 and mul_x0 to read
93 the values from the given CSV file.
96 alpaqa-driver problem.so \
97 problem.register=register_alpaqa_problem \
98 problem.custom_arg=foo \
99 method=panoc.struclbfgs \
101 alm.{tolerance,dual_tolerance}=1e-8 \
102 solver.print_interval=50 \
105 alpaqa-driver cs:build/casadi_problem.so \
106 problem.param=1,2,3 \
108 solver.tol=1e-8 solver.constr_viol_tol=1e-8 \
109 solver.warm_start_init_point=yes \
111 mul_g0=@/some/other/file.csv \
112 mul_x0=@/yet/another/file.csv
116 const auto *opts =
" [<problem-type>:][<path>/]<name> [method=<solver>] "
117 "[<key>=<value>...]\n";
118 std::cout <<
"alpaqa-driver (" ALPAQA_VERSION_FULL
")\n\n"
119 " Command-line interface to the alpaqa solvers.\n"
120 " alpaqa is published under the LGPL-3.0.\n"
121 " https://github.com/kul-optec/alpaqa"
124 << a0 << opts <<
docs << std::endl;
126 <<
"Third-party libraries:\n"
127 <<
" * Eigen " << EIGEN_WORLD_VERSION <<
'.' << EIGEN_MAJOR_VERSION
128 <<
'.' << EIGEN_MINOR_VERSION
129 <<
" (https://gitlab.com/libeigen/eigen) - MPL-2.0\n"
130#ifdef ALPAQA_HAVE_CASADI
131 <<
" * CasADi " CASADI_VERSION_STRING
132 " (https://github.com/casadi/casadi) - LGPL-3.0-or-later\n"
134#ifdef ALPAQA_HAVE_CUTEST
136 " (https://github.com/ralna/CUTEst) - BSD-3-Clause\n"
139 <<
" * L-BFGS-B 3.0 "
140 "(http://users.iems.northwestern.edu/~nocedal/lbfgsb.html) - "
144 <<
" * Ipopt " IPOPT_VERSION
145 " (https://github.com/coin-or/Ipopt) - EPL-2.0\n"
146 <<
" * MUMPS (https://mumps-solver.org) - CECILL-C\n"
147 <<
" * OpenBLAS (https://github.com/xianyi/OpenBLAS) - BSD-3-Clause\n"
150 <<
" * QPALM " QPALM_VERSION_STR
151 " (https://github.com/kul-optec/QPALM) - LGPL-3.0\n"
159 auto tok_pos = s.find(tok);
160 if (tok_pos == s.npos)
161 return std::make_tuple(std::string_view{}, s);
162 std::string_view key{s.begin(), s.begin() + tok_pos};
163 std::string_view rem{s.begin() + tok_pos + 1, s.end()};
164 return std::make_tuple(key, rem);
168 std::string out_path =
"-";
171 if (out_fstream.open(out_path); !out_fstream)
172 throw std::runtime_error(
"Unable to open '" + out_path +
"'");
173 return out_fstream.is_open() ? out_fstream : std::cout;
177 std::string_view sol_path;
183 bool rel_to_exe = argv[1][0] ==
'^';
184 std::string_view prob_path_s = argv[1] +
static_cast<ptrdiff_t
>(rel_to_exe);
185 std::string_view prob_type;
186 std::tie(prob_type, prob_path_s) =
split_once(prob_path_s,
':');
187 fs::path prob_path{prob_path_s};
189 prob_path = fs::canonical(fs::path(argv[0])).parent_path() / prob_path;
190 return std::make_tuple(std::move(prob_path), prob_type);
194 std::string_view method =
"panoc", direction;
198 std::map<std::string_view, solver_builder_func> solvers{
204 auto solver_it = solvers.find(method);
205 if (solver_it == solvers.end())
206 throw std::invalid_argument(
207 "Unknown solver '" + std::string(method) +
"'\n" +
208 " Available solvers: " +
210 return std::make_tuple(std::move(solver_it->second), direction);
215 std::span<const char *> argv) {
217 auto timestamp_str = std::to_string(results.
timestamp);
220 if (name ==
"PROBLEM")
222 auto suffix =
'_' + name +
'_' + timestamp_str +
'_' + rnd_str;
223 fs::create_directories(sol_output_dir);
224 std::array solutions{
225 std::tuple{
"solution",
"sol_x", &sol_res.solution},
226 std::tuple{
"multipliers for g",
"mul_g", &sol_res.multipliers},
227 std::tuple{
"multipliers for x",
"mul_x", &sol_res.multipliers_bounds},
229 for (
auto [name, fname, value] : solutions) {
230 if (value->size() == 0)
232 auto pth = sol_output_dir / (std::string(fname) + suffix +
".csv");
233 os <<
"Writing " << name <<
" to " << pth << std::endl;
234 std::ofstream output_file(pth);
238 auto pth = sol_output_dir / (
"cmdline" + suffix +
".txt");
239 os <<
"Writing arguments to " << pth << std::endl;
240 std::ofstream output_file(pth);
241 for (
const char *arg : argv)
242 output_file << std::quoted(arg,
'\'') <<
' ';
245 if (solver->has_statistics()) {
246 auto pth = sol_output_dir / (
"stats" + suffix +
".csv");
247 os <<
"Writing statistics to " << pth << std::endl;
248 std::ofstream output_file(pth);
249 solver->write_statistics_to_stream(output_file);
253int main(
int argc,
const char *argv[])
try {
255 SetConsoleOutputCP(CP_UTF8);
264 std::span args{argv,
static_cast<size_t>(argc)};
265 Options opts{argc - 2, argv + 2};
268 std::ofstream out_fstream;
281 auto solver = solver_builder(direction, opts);
284 os <<
"Loading problem " << prob_path << std::endl;
285 auto problem =
load_problem(prob_type, prob_path.parent_path(),
286 prob_path.filename(), opts);
287 os <<
"Loaded problem \"" << problem.name <<
"\" from " << problem.path
288 <<
"\nProvided functions:\n";
293 auto used = opts.used();
294 auto unused_opt = std::ranges::find(used, 0);
295 auto unused_idx =
static_cast<size_t>(unused_opt - used.begin());
296 if (unused_opt != used.end())
297 throw std::invalid_argument(
"Unused option: " +
298 std::string(opts.options()[unused_idx]));
301 auto solver_results = solver->run(problem, os);
304 real_t f = problem.problem.eval_f(solver_results.solution);
306 problem.problem, solver_results.solution, solver_results.multipliers);
309 .solver_results = solver_results,
310 .objective = f + solver_results.h,
311 .smooth_objective = f,
313 .options = opts.options(),
314 .timestamp = timestamp_ms<std::chrono::system_clock>().count(),
321 if (!sol_output_dir.empty())
324}
catch (std::exception &e) {
326 << e.what() << std::endl;
auto get_solver_builder(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 store_solution(const fs::path &sol_output_dir, std::ostream &os, BenchmarkResults &results, auto &solver, std::span< const char * > argv)
auto split_once(std::string_view s, char tok='.')
Split the string s on the first occurrence of tok.
std::string_view get_output_paths(Options &opts)
int main(int argc, const char *argv[])
#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.
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::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)
decltype(auto) 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)
LoadedProblem load_problem(std::string_view type, const fs::path &dir, const fs::path &file, Options &opts)
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.
std::string format_string_list(const auto &container, const auto &proj=[](const auto &x) -> decltype(auto) { return x;})