5#include <alpaqa-version.h>
17#ifdef ALPAQA_HAVE_CASADI
18#include <casadi/config.h>
21#include <IpoptConfig.h>
35namespace fs = std::filesystem;
40 const auto *opts =
" [<problem-type>:][<path>/]<name> [method=<solver>] "
41 "[<key>=<value>...]\n";
42 const auto *docs = R
"==(
44 dl: Dynamically loaded problem using the DLProblem class.
45 Specify the prefix of the registration function using the
46 problem.prefix option, e.g. problem.prefix=alpaqa_problem will look
47 for a registration function with the name alpaqa_problem_register.
48 Further options can be passed to the problem using
49 problem.<key>[=<value>].
50 cs: Load a CasADi problem using the CasADiProblem class.
51 If a .tsv file with the same name as the shared library file exists,
52 the bounds and parameters will be loaded from that file. See
53 CasADiProblem::load_numerical_data for more details.
54 The problem parameter can be set using the problem.param option.
55 cu: Load a CUTEst problem using the CUTEstProblem class.
59 PANOC solver with the given direction.
60 Directions include: lbfgs, struclbfgs, anderson.
61 zerofpr[.<direction>]:
62 ZeroFPR solver, supports the same directions as PANOC.
64 PANTR solver. Requires products with the Hessian of the augmented
65 Lagrangian (unless dir.finite_diff=true).
67 Ipopt interior point solver. Requires Jacobian of the constraints
68 and Hessian of the Lagrangian (unless finite memory is enabled).
70 QPALM proximal ALM QP solver. Assumes that the problem is a QP.
71 Requires Jacobian of the constraints and Hessian of the Lagrangian.
74 Solver-specific options can be specified as key-value pairs, where the
75 keys use periods to access struct members. For example,
76 solver.Lipschitz.L_0=1e3.
78 solver: Parameters for the main (inner) solver.
79 alm: Parameters for the outer ALM solver (if applicable).
80 dir: Parameters for solver's direction provider (if applicable).
81 accel: Parameters for direction's accelerator (if applicable).
82 out: File to write output to (default: -, i.e. standard output).
83 sol: Folder to write the solutions to.
84 num_exp: Repeat the experiment this many times for more accurate timings.
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.
89 The prefix @ can be added to the values of x0, mul_g0 and mul_x0 to read
90 the values from the given CSV file.
93 alpaqa-driver problem.so \
94 problem.prefix=alpaqa_problem \
95 problem.custom_arg=foo \
96 method=panoc.struclbfgs \
98 alm.{tolerance,dual_tolerance}=1e-8 \
99 solver.print_interval=50 \
102 alpaqa-driver cs:build/casadi_problem.so \
103 problem.param=1,2,3 \
105 solver.tol=1e-8 solver.constr_viol_tol=1e-8 \
106 solver.warm_start_init_point=yes \
108 mul_g0=@/some/other/file.csv \
109 mul_x0=@/yet/another/file.csv
111 std::cout << "alpaqa-driver (" ALPAQA_VERSION_FULL
")\n\n"
112 " Command-line interface to the alpaqa solvers.\n"
113 " alpaqa is published under the LGPL-3.0.\n"
114 " https://github.com/kul-optec/alpaqa"
117 << a0 << opts << docs << std::endl;
119 <<
"Third-party libraries:\n"
120 <<
" * Eigen " << EIGEN_WORLD_VERSION <<
'.' << EIGEN_MAJOR_VERSION
121 <<
'.' << EIGEN_MINOR_VERSION
122 <<
" (https://gitlab.com/libeigen/eigen) - MPL-2.0\n"
123#ifdef ALPAQA_HAVE_CASADI
124 <<
" * CasADi " CASADI_VERSION_STRING
125 " (https://github.com/casadi/casadi) - LGPL-3.0-or-later\n"
127#ifdef ALPAQA_HAVE_CUTEST
129 " (https://github.com/ralna/CUTEst) - BSD-3-Clause\n"
132 <<
" * L-BFGS-B 3.0 "
133 "(http://users.iems.northwestern.edu/~nocedal/lbfgsb.html) - "
137 <<
" * Ipopt " IPOPT_VERSION
138 " (https://github.com/coin-or/Ipopt) - EPL-2.0\n"
139 <<
" * MUMPS (https://mumps-solver.org) - CECILL-C\n"
140 <<
" * OpenBLAS (https://github.com/xianyi/OpenBLAS) - BSD-3-Clause\n"
143 <<
" * QPALM " QPALM_VERSION_STR
144 " (https://github.com/kul-optec/QPALM) - LGPL-3.0\n"
152 auto tok_pos = s.find(tok);
153 if (tok_pos == s.npos)
154 return std::make_tuple(std::string_view{}, s);
155 std::string_view key{s.begin(), s.begin() + tok_pos};
156 std::string_view rem{s.begin() + tok_pos + 1, s.end()};
157 return std::make_tuple(key, rem);
161 std::string out_path =
"-";
164 if (out_fstream.open(out_path); !out_fstream)
165 throw std::runtime_error(
"Unable to open '" + out_path +
"'");
166 return out_fstream.is_open() ? out_fstream : std::cout;
170 std::string_view sol_path;
176 bool rel_to_exe = argv[1][0] ==
'^';
177 std::string_view prob_path_s = argv[1] +
static_cast<ptrdiff_t
>(rel_to_exe);
178 std::string_view prob_type;
179 std::tie(prob_type, prob_path_s) =
split_once(prob_path_s,
':');
180 fs::path prob_path{prob_path_s};
182 prob_path = fs::canonical(fs::path(argv[0])).parent_path() / prob_path;
183 return std::make_tuple(std::move(prob_path), prob_type);
187 std::string_view method =
"panoc", direction;
191 std::map<std::string_view, solver_builder_func_t> solvers{
197 auto solver_it = solvers.find(method);
198 if (solver_it == solvers.end())
199 throw std::invalid_argument(
200 "Unknown solver '" + std::string(method) +
"'\n" +
201 " Available solvers: " +
203 return std::make_tuple(std::move(solver_it->second), direction);
209 auto timestamp_str = std::to_string(results.
timestamp);
212 if (name ==
"PROBLEM")
214 auto suffix =
'_' + name +
'_' + timestamp_str +
'_' + rnd_str;
215 fs::create_directories(sol_output_dir);
216 std::array solutions{
217 std::tuple{
"solution",
"sol_x", &sol_res.solution},
218 std::tuple{
"multipliers for g",
"mul_g", &sol_res.multipliers},
219 std::tuple{
"multipliers for x",
"mul_x", &sol_res.multipliers_bounds},
221 for (
auto [name, fname, value] : solutions) {
222 if (value->size() == 0)
224 auto pth = sol_output_dir / (std::string(fname) + suffix +
".csv");
225 os <<
"Writing " << name <<
" to " << pth << std::endl;
226 std::ofstream output_file(pth);
229 auto pth = sol_output_dir / (
"cmdline" + suffix +
".txt");
230 os <<
"Writing arguments to " << pth << std::endl;
231 std::ofstream output_file(pth);
232 for (
const char *arg : argv)
233 output_file << std::quoted(arg,
'\'') <<
' ';
237int main(
int argc,
const char *argv[])
try {
245 std::span args{argv,
static_cast<size_t>(argc)};
246 Options opts{argc - 2, argv + 2};
249 std::ofstream out_fstream;
262 auto solver = solver_builder(direction, opts);
265 os <<
"Loading problem " << prob_path << std::endl;
266 auto problem =
load_problem(prob_type, prob_path.parent_path(),
267 prob_path.filename(), opts);
268 os <<
"Loaded problem \"" << problem.name <<
"\" from " << problem.path
269 <<
"\nProvided functions:\n";
274 auto used = opts.used();
275 auto unused_opt = std::find(used.begin(), used.end(), 0);
276 auto unused_idx =
static_cast<size_t>(unused_opt - used.begin());
277 if (unused_opt != used.end())
278 throw std::invalid_argument(
"Unused option: " +
279 std::string(opts.options()[unused_idx]));
282 auto solver_results = solver(problem, os);
285 real_t f = problem.problem.eval_f(solver_results.solution);
287 problem.problem, solver_results.solution, solver_results.multipliers);
290 .solver_results = solver_results,
291 .objective = f + solver_results.h,
292 .smooth_objective = f,
294 .options = opts.options(),
295 .timestamp = timestamp_ms<std::chrono::system_clock>().count(),
302 if (!sol_output_dir.empty())
305}
catch (std::exception &e) {
307 << e.what() << std::endl;
auto get_solver_builder(Options &opts)
std::ostream & get_output_stream(Options &opts, std::ofstream &out_fstream)
void store_solution(const fs::path &sol_output_dir, std::ostream &os, BenchmarkResults &results, std::span< const char * > argv)
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.
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)
solver_func_t make_ipopt_driver(std::string_view, Options &)
solver_func_t 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)
solver_func_t make_panoc_driver(std::string_view direction, Options &opts)
solver_func_t make_zerofpr_driver(std::string_view direction, Options &opts)
solver_func_t 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)
solver_func_t 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;})