alpaqa pantr
Nonconvex constrained optimization
Loading...
Searching...
No Matches
alpaqa-driver.cpp
Go to the documentation of this file.
5
6#include "ipopt-driver.hpp"
7#include "lbfgsb-driver.hpp"
8#include "options.hpp"
9#include "panoc-driver.hpp"
10#include "pantr-driver.hpp"
11#include "results.hpp"
12#include "solver-driver.hpp"
13#include "util.hpp"
14
15#include <algorithm>
16#include <filesystem>
17#include <fstream>
18#include <iostream>
19#include <random>
20#include <span>
21#include <stdexcept>
22#include <string>
23#include <string_view>
24#include <tuple>
25#include <type_traits>
26namespace fs = std::filesystem;
27
29
30void print_usage(const char *a0) {
31 std::cout << "Usage: " << a0
32 << " [<problem-type>:][<path>/]<name> [method=<solver>] "
33 "[<key>=<value>...]\n";
34}
35
36/// Split the string @p s on the first occurrence of @p tok.
37/// Returns ("", s) if tok was not found.
38auto split_once(std::string_view s, char tok = '.') {
39 auto tok_pos = s.find(tok);
40 if (tok_pos == s.npos)
41 return std::make_tuple(std::string_view{}, s);
42 std::string_view key{s.begin(), s.begin() + tok_pos};
43 std::string_view rem{s.begin() + tok_pos + 1, s.end()};
44 return std::make_tuple(key, rem);
45}
46
47std::ostream &get_output_stream(Options &opts, std::ofstream &out_fstream) {
48 std::string out_path = "-";
49 set_params(out_path, "out", opts);
50 if (out_path != "-")
51 if (out_fstream.open(out_path); !out_fstream)
52 throw std::runtime_error("Unable to open '" + out_path + "'");
53 return out_fstream.is_open() ? out_fstream : std::cout;
54}
55
56std::string_view get_output_paths(Options &opts) {
57 std::string_view sol_path;
58 set_params(sol_path, "sol", opts);
59 return sol_path;
60}
61
62auto get_problem_path(const char *const *argv) {
63 bool rel_to_exe = argv[1][0] == '^';
64 std::string_view prob_path_s = argv[1] + static_cast<ptrdiff_t>(rel_to_exe);
65 std::string_view prob_type;
66 std::tie(prob_type, prob_path_s) = split_once(prob_path_s, ':');
67 fs::path prob_path{prob_path_s};
68 if (rel_to_exe)
69 prob_path = fs::canonical(fs::path(argv[0])).parent_path() / prob_path;
70 return std::make_tuple(std::move(prob_path), prob_type);
71}
72
74 std::string_view method = "panoc", direction;
75 set_params(method, "method", opts);
76 std::tie(method, direction) = alpaqa::params::split_key(method, '.');
77 // Dictionary of available solver builders
78 std::map<std::string_view, solver_builder_func_t> solvers{
79 {"panoc", make_panoc_driver}, {"zerofpr", make_zerofpr_driver},
80 {"pantr", make_pantr_driver},
81#ifdef WITH_LBFGSB
82 {"lbfgsb", make_lbfgsb_driver},
83#endif
84#ifdef WITH_IPOPT
85 {"ipopt", make_ipopt_driver},
86#endif
87 };
88 // Find the selected solver builder
89 auto solver_it = solvers.find(method);
90 if (solver_it == solvers.end())
91 throw std::invalid_argument(
92 "Unknown solver '" + std::string(method) + "'\n" +
93 " Available solvers: " +
94 format_string_list(solvers, [](const auto &x) { return x.first; }));
95 return std::make_tuple(std::move(solver_it->second), direction);
96}
97
98void store_solution(const fs::path &sol_output_dir, std::ostream &os,
99 BenchmarkResults &results, std::span<const char *> argv) {
100 const auto &sol_res = results.solver_results;
101 auto timestamp_str = std::to_string(results.timestamp);
102 auto rnd_str = random_hex_string(std::random_device());
103 auto name = results.problem.path.stem().string();
104 auto suffix = '_' + name + '_' + timestamp_str + '_' + rnd_str;
105 fs::create_directories(sol_output_dir);
106 std::array solutions{
107 std::tuple{"solution", "sol_x", &sol_res.solution},
108 std::tuple{"multipliers for g", "mul_g", &sol_res.multipliers},
109 std::tuple{"multipliers for x", "mul_x", &sol_res.multipliers_bounds},
110 };
111 for (auto [name, fname, value] : solutions) {
112 if (value->size() == 0)
113 continue;
114 auto pth = sol_output_dir / (std::string(fname) + suffix + ".csv");
115 os << "Writing " << name << " to " << pth << std::endl;
116 std::ofstream output_file(pth);
117 alpaqa::print_csv(output_file, *value);
118 }
119 auto pth = sol_output_dir / ("cmdline" + suffix + ".txt");
120 os << "Writing arguments to " << pth << std::endl;
121 std::ofstream output_file(pth);
122 for (const char *arg : argv)
123 output_file << std::quoted(arg, '\'') << ' ';
124 output_file << '\n';
125}
126
127int main(int argc, const char *argv[]) try {
128 // Check command line options
129 if (argc < 1)
130 return -1;
131 if (argc == 1)
132 return print_usage(argv[0]), 0;
133 if (argc < 2)
134 return print_usage(argv[0]), -1;
135 std::span args{argv, static_cast<size_t>(argc)};
136 Options opts{argc - 2, argv + 2};
137
138 // Check where to write the output to
139 std::ofstream out_fstream;
140 std::ostream &os = get_output_stream(opts, out_fstream);
141
142 // Check which problem to load
143 auto [prob_path, prob_type] = get_problem_path(argv);
144
145 // Check which solver to use
146 auto [solver_builder, direction] = get_solver_builder(opts);
147
148 // Check output paths
149 fs::path sol_output_dir = get_output_paths(opts);
150
151 // Build solver
152 auto solver = solver_builder(direction, opts);
153
154 // Load problem
155 os << "Loading problem " << prob_path << std::endl;
156 auto problem = load_problem(prob_type, prob_path.parent_path(),
157 prob_path.filename(), opts);
158 os << "Loaded problem " << problem.path.stem().c_str() << " from "
159 << problem.path << "\nProvided functions:\n";
160 alpaqa::print_provided_functions(os, problem.problem);
161 os << std::endl;
162
163 // Check options
164 auto used = opts.used();
165 auto unused_opt = std::find(used.begin(), used.end(), false);
166 auto unused_idx = static_cast<size_t>(unused_opt - used.begin());
167 if (unused_opt != used.end())
168 throw std::invalid_argument("Unused option: " +
169 std::string(opts.options()[unused_idx]));
170
171 // Solve
172 auto solver_results = solver(problem, os);
173
174 // Compute more statistics
175 real_t f = problem.problem.eval_f(solver_results.solution);
176 auto kkt_err = alpaqa::compute_kkt_error(
177 problem.problem, solver_results.solution, solver_results.multipliers);
178 BenchmarkResults results{
179 .problem = problem,
180 .solver_results = solver_results,
181 .objective = f + solver_results.h,
182 .smooth_objective = f,
183 .error = kkt_err,
184 .options = opts.options(),
185 .timestamp = timestamp_ms<std::chrono::system_clock>().count(),
186 };
187
188 // Print results
189 print_results(os, results);
190
191 // Store solution
192 if (!sol_output_dir.empty())
193 store_solution(sol_output_dir, os, results, args);
194
195} catch (std::exception &e) {
196 std::cerr << "Error: " << demangled_typename(typeid(e)) << ":\n "
197 << e.what() << std::endl;
198 return -1;
199}
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)
Definition: config.hpp:42
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 direction, Options &opts)
solver_func_t make_lbfgsb_driver(std::string_view direction, Options &opts)
auto split_key(std::string_view full, char tok='.')
Split the string full on the first occurrence of tok.
Definition: params.hpp:31
KKTError< Conf > compute_kkt_error(const alpaqa::TypeErasedProblem< Conf > &problem, alpaqa::crvec< Conf > x, alpaqa::crvec< Conf > y)
Definition: kkt-error.hpp:16
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)
Definition: print.hpp:66
decltype(auto) set_params(T &t, std::string_view prefix, Options &opts)
Definition: options.hpp:29
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)
Definition: problem.cpp:97
fs::path path
Definition: problem.hpp:17
std::string random_hex_string(auto &&rng)
Definition: results.hpp:51
void print_results(std::ostream &os, const BenchmarkResults &results)
Definition: results.hpp:134
LoadedProblem & problem
Definition: results.hpp:43
int64_t timestamp
Definition: results.hpp:48
SolverResults solver_results
Definition: results.hpp:44
std::string format_string_list(const auto &container, const auto &proj=[](const auto &x) -> decltype(auto) { return x;})
Definition: util.hpp:7