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.