5#include <alpaqa-version.h>
16#ifdef ALPAQA_HAVE_CASADI
17#include <casadi/config.h>
20#include <IpoptConfig.h>
34namespace fs = std::filesystem;
39 const auto *opts =
" [<problem-type>:][<path>/]<name> [method=<solver>] "
40 "[<key>=<value>...]\n";
41 const auto *docs = R
"==(
43 dl: Dynamically loaded problem using the DLProblem class.
44 Specify the prefix of the registration function using the
45 problem.prefix option, e.g. problem.prefix=alpaqa_problem will look
46 for a registration function with the name alpaqa_problem_register.
47 Further options can be passed to the problem using
48 problem.<key>[=<value>].
49 cs: Load a CasADi problem using the CasADiProblem class.
50 If a .tsv file with the same name as the shared library file exists,
51 the bounds and parameters will be loaded from that file. See
52 CasADiProblem::load_numerical_data for more details.
53 The problem parameter can be set using the problem.param option.
54 cu: Load a CUTEst problem using the CUTEstProblem class.
58 PANOC solver with the given direction.
59 Directions include: lbfgs, struclbfgs, anderson.
60 zerofpr[.<direction>]:
61 ZeroFPR solver, supports the same directions as PANOC.
63 PANTR solver. Requires products with the Hessian of the augmented
64 Lagrangian (unless dir.finite_diff=true).
66 Ipopt interior point solver. Requires Jacobian of the constraints
67 and Hessian of the Lagrangian (unless finite memory is enabled).
70 Solver-specific options can be specified as key-value pairs, where the
71 keys use periods to access struct members. For example,
72 solver.Lipschitz.L_0=1e3.
74 solver: Parameters for the main (inner) solver.
75 alm: Parameters for the outer ALM solver (if applicable).
76 dir: Parameters for solver's direction provider (if applicable).
77 accel: Parameters for direction's accelerator (if applicable).
78 out: File to write output to (default: -, i.e. standard output).
79 sol: Folder to write the solutions to.
80 num_exp: Repeat the experiment this many times for more accurate timings.
81 x0: Initial guess for the solution.
82 mul_g0: Initial guess for the multipliers of the general constraints.
83 mul_x0: Initial guess for the multipliers of the bound constraints on x.
85 The prefix @ can be added to the values of x0, mul_g0 and mul_x0 to read
86 the values from the given CSV file.
89 alpaqa-driver problem.so \
90 problem.prefix=alpaqa_problem \
91 problem.custom_arg=foo \
92 method=panoc.struclbfgs \
94 alm.{tolerance,dual_tolerance}=1e-8 \
95 solver.print_interval=50 \
98 alpaqa-driver cs:build/casadi_problem.so \
101 solver.tol=1e-8 solver.constr_viol_tol=1e-8 \
102 solver.warm_start_init_point=yes \
104 mul_g0=@/some/other/file.csv \
105 mul_x0=@/yet/another/file.csv
107 std::cout << "alpaqa-driver (" ALPAQA_VERSION_FULL
")\n\n"
108 " Command-line interface to the alpaqa solvers.\n"
109 " alpaqa is published under the LGPL-3.0.\n"
110 " https://github.com/kul-optec/alpaqa"
113 << a0 << opts << docs << std::endl;
115 <<
"Third-party libraries:\n"
116 <<
" * Eigen " << EIGEN_WORLD_VERSION <<
'.' << EIGEN_MAJOR_VERSION
117 <<
'.' << EIGEN_MINOR_VERSION
118 <<
" (https://gitlab.com/libeigen/eigen) - MPL-2.0\n"
119#ifdef ALPAQA_HAVE_CASADI
120 <<
" * CasADi " CASADI_VERSION_STRING
121 " (https://github.com/casadi/casadi) - LGPL-3.0+\n"
124 <<
" * L-BFGS-B 3.0 "
125 "(http://users.iems.northwestern.edu/~nocedal/lbfgsb.html) - "
129 <<
" * Ipopt " IPOPT_VERSION
130 " (https://github.com/coin-or/Ipopt) - EPL-2.0\n"
131 <<
" * MUMPS (https://mumps-solver.org) - CECILL-C\n"
132 <<
" * OpenBLAS (https://github.com/xianyi/OpenBLAS) - BSD-3-Clause\n"
140 auto tok_pos = s.find(tok);
141 if (tok_pos == s.npos)
142 return std::make_tuple(std::string_view{}, s);
143 std::string_view key{s.begin(), s.begin() + tok_pos};
144 std::string_view rem{s.begin() + tok_pos + 1, s.end()};
145 return std::make_tuple(key, rem);
149 std::string out_path =
"-";
152 if (out_fstream.open(out_path); !out_fstream)
153 throw std::runtime_error(
"Unable to open '" + out_path +
"'");
154 return out_fstream.is_open() ? out_fstream : std::cout;
158 std::string_view sol_path;
164 bool rel_to_exe = argv[1][0] ==
'^';
165 std::string_view prob_path_s = argv[1] +
static_cast<ptrdiff_t
>(rel_to_exe);
166 std::string_view prob_type;
167 std::tie(prob_type, prob_path_s) =
split_once(prob_path_s,
':');
168 fs::path prob_path{prob_path_s};
170 prob_path = fs::canonical(fs::path(argv[0])).parent_path() / prob_path;
171 return std::make_tuple(std::move(prob_path), prob_type);
175 std::string_view method =
"panoc", direction;
179 std::map<std::string_view, solver_builder_func_t> solvers{
185 auto solver_it = solvers.find(method);
186 if (solver_it == solvers.end())
187 throw std::invalid_argument(
188 "Unknown solver '" + std::string(method) +
"'\n" +
189 " Available solvers: " +
191 return std::make_tuple(std::move(solver_it->second), direction);
197 auto timestamp_str = std::to_string(results.
timestamp);
200 auto suffix =
'_' + name +
'_' + timestamp_str +
'_' + rnd_str;
201 fs::create_directories(sol_output_dir);
202 std::array solutions{
203 std::tuple{
"solution",
"sol_x", &sol_res.solution},
204 std::tuple{
"multipliers for g",
"mul_g", &sol_res.multipliers},
205 std::tuple{
"multipliers for x",
"mul_x", &sol_res.multipliers_bounds},
207 for (
auto [name, fname, value] : solutions) {
208 if (value->size() == 0)
210 auto pth = sol_output_dir / (std::string(fname) + suffix +
".csv");
211 os <<
"Writing " << name <<
" to " << pth << std::endl;
212 std::ofstream output_file(pth);
215 auto pth = sol_output_dir / (
"cmdline" + suffix +
".txt");
216 os <<
"Writing arguments to " << pth << std::endl;
217 std::ofstream output_file(pth);
218 for (
const char *arg : argv)
219 output_file << std::quoted(arg,
'\'') <<
' ';
223int main(
int argc,
const char *argv[])
try {
231 std::span args{argv,
static_cast<size_t>(argc)};
232 Options opts{argc - 2, argv + 2};
235 std::ofstream out_fstream;
248 auto solver = solver_builder(direction, opts);
251 os <<
"Loading problem " << prob_path << std::endl;
252 auto problem =
load_problem(prob_type, prob_path.parent_path(),
253 prob_path.filename(), opts);
254 os <<
"Loaded problem " << problem.path.stem().c_str() <<
" from "
255 << problem.path <<
"\nProvided functions:\n";
260 auto used = opts.used();
261 auto unused_opt = std::find(used.begin(), used.end(),
false);
262 auto unused_idx =
static_cast<size_t>(unused_opt - used.begin());
263 if (unused_opt != used.end())
264 throw std::invalid_argument(
"Unused option: " +
265 std::string(opts.options()[unused_idx]));
268 auto solver_results = solver(problem, os);
271 real_t f = problem.problem.eval_f(solver_results.solution);
273 problem.problem, solver_results.solution, solver_results.multipliers);
276 .solver_results = solver_results,
277 .objective = f + solver_results.h,
278 .smooth_objective = f,
280 .options = opts.options(),
281 .timestamp = timestamp_ms<std::chrono::system_clock>().count(),
288 if (!sol_output_dir.empty())
291}
catch (std::exception &e) {
293 << 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.
KKTError< Conf > compute_kkt_error(const alpaqa::TypeErasedProblem< Conf > &problem, alpaqa::crvec< Conf > x, alpaqa::crvec< Conf > y)
std::ostream & print_csv(std::ostream &os, const Eigen::Ref< const Eigen::MatrixX< float > > &M, std::string_view sep, std::string_view begin, std::string_view end)
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)
std::string random_hex_string(auto &&rng)
void print_results(std::ostream &os, const BenchmarkResults &results)
SolverResults solver_results
std::string format_string_list(const auto &container, const auto &proj=[](const auto &x) -> decltype(auto) { return x;})