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::Ref< const Eigen::MatrixX< float > > &M, std::string_view sep, std::string_view begin, std::string_view end)
 
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;})