alpaqa 1.0.0a15
Nonconvex constrained optimization
Loading...
Searching...
No Matches
C++/DLProblem/main.cpp

This example shows how to load optimization problems from an external library using dlopen.

This example shows how to load optimization problems from an external library using dlopen.

It solves a simple quadratic program of the form:

\[ \begin{aligned} & \underset{x}{\text{minimize}} && \tfrac12 \tp x Q x \\ & \text{subject to} && Ax \le b \\ \end{aligned} \]

Problem definition in C

#include <problem-c-export.h>
#include <stdlib.h>
#include <string.h>
#include "matmul.h"
typedef alpaqa_real_t real_t;
typedef alpaqa_length_t length_t;
typedef alpaqa_index_t index_t;
struct ProblemData {
real_t *Q;
real_t *A;
real_t *work;
};
static void eval_grad_f(void *instance, const real_t *x, real_t *grad_f) {
struct ProblemData *problem = instance;
length_t n = problem->functions.n;
matmul(n, n, 1, problem->Q, x, grad_f); // grad_f = Q x
}
static real_t eval_f_grad_f(void *instance, const real_t *x, real_t *grad_f) {
struct ProblemData *problem = instance;
length_t n = problem->functions.n;
real_t result;
eval_grad_f(instance, x, grad_f);
matmul(1, n, 1, x, grad_f, &result); // result = xᵀ grad_f
return (real_t)0.5 * result;
}
static real_t eval_f(void *instance, const real_t *x) {
struct ProblemData *problem = instance;
return eval_f_grad_f(instance, x, problem->work);
}
static void eval_g(void *instance, const real_t *x, real_t *gx) {
struct ProblemData *problem = instance;
length_t n = problem->functions.n;
length_t m = problem->functions.m;
matmul(m, n, 1, problem->A, x, gx); // gx = A x
}
static void eval_grad_g_prod(void *instance, const real_t *x, const real_t *y,
real_t *grad_gxy) {
(void)x;
struct ProblemData *problem = instance;
length_t n = problem->functions.n;
length_t m = problem->functions.m;
matvec_transp(m, n, problem->A, y, grad_gxy); // grad_gxy = Aᵀ y
}
static void eval_jac_g(void *instance, const real_t *x, real_t *J_values) {
(void)x;
struct ProblemData *problem = instance;
size_t n = (size_t)problem->functions.n;
size_t m = (size_t)problem->functions.m;
memcpy(J_values, problem->A, sizeof(real_t) * m * n);
}
static void initialize_box_D(void *instance, real_t *lb, real_t *ub) {
(void)instance;
(void)lb; // -inf by default
ub[0] = -1;
}
static struct ProblemData *create_problem(void *user_data) {
(void)user_data;
struct ProblemData *problem = malloc(sizeof(*problem));
size_t n = 2, m = 1;
problem->functions = (alpaqa_problem_functions_t){
.n = (length_t)n,
.m = (length_t)m,
.eval_f = &eval_f,
.eval_grad_f = &eval_grad_f,
.eval_g = &eval_g,
.eval_grad_g_prod = &eval_grad_g_prod,
.eval_jac_g = &eval_jac_g,
.eval_f_grad_f = &eval_f_grad_f,
.initialize_box_D = &initialize_box_D,
};
problem->Q = malloc(sizeof(real_t) * n * n);
problem->A = malloc(sizeof(real_t) * m * n);
problem->work = malloc(sizeof(real_t) * n);
// Note: column major order
problem->Q[0] = 3;
problem->Q[1] = -1;
problem->Q[2] = -1;
problem->Q[3] = 3;
problem->A[0] = 2;
problem->A[1] = 1;
return problem;
}
static void cleanup_problem(void *instance) {
struct ProblemData *problem = instance;
free(problem->Q);
free(problem->A);
free(problem->work);
free(problem);
}
PROBLEM_C_EXPORT alpaqa_problem_register_t
register_alpaqa_problem(void *user_data) {
struct ProblemData *problem = create_problem(user_data);
result.instance = problem;
result.cleanup = &cleanup_problem;
result.functions = &problem->functions;
return result;
}
ptrdiff_t alpaqa_length_t
Definition dl-problem.h:20
alpaqa_length_t alpaqa_index_t
Definition dl-problem.h:21
double alpaqa_real_t
Definition dl-problem.h:19
#define ALPAQA_PROBLEM_REGISTER_INIT(self)
Initialize an instance of alpaqa_problem_register_t or alpaqa_control_problem_register_t.
Definition dl-problem.h:576
typename Conf::real_t real_t
Definition config.hpp:65
typename Conf::length_t length_t
Definition config.hpp:76
C API providing function pointers to problem functions.
Definition dl-problem.h:157
void(* cleanup)(void *)
Pointer to the function to clean up instance.
Definition dl-problem.h:372
void * instance
Owning pointer.
Definition dl-problem.h:368
alpaqa_problem_functions_t * functions
Non-owning pointer, lifetime at least as long as instance.
Definition dl-problem.h:370

Problem solution using alpaqa

4
5#include <filesystem>
6#include <iostream>
7
8namespace fs = std::filesystem;
9
10// Double precision, same as in C
12
13int main(int argc, char *argv[]) {
14 // Find the problem to load
15 fs::path so_name = DLPROBLEM_DLL;
16 if (argc > 1)
17 so_name = fs::path(argv[1]);
18 else if (argc > 0)
19 so_name = fs::canonical(fs::path(argv[0])).parent_path() / so_name;
20 std::cout << "Loading " << so_name << std::endl;
21
22 // Instantiate a problem
23 alpaqa::dl::DLProblem problem{so_name.string()};
24 // Wrap the problem to count the function evaluations
25 auto counted_problem = alpaqa::problem_with_counters_ref(problem);
26
27 // Specify the bounds (optional, overrides any bounds set in problem.c)
28 length_t n = problem.get_n();
29 length_t m = problem.get_m();
30 vec b = vec::Constant(m, -1);
31 problem.C.lowerbound = vec::Constant(n, -alpaqa::inf<config_t>);
32 problem.C.upperbound = vec::Constant(n, +alpaqa::inf<config_t>);
33 problem.D.lowerbound = vec::Constant(m, -alpaqa::inf<config_t>);
34 problem.D.upperbound = b;
35
36 // Define the solvers to use
38 using InnerSolver = alpaqa::PANOCSolver<Direction>;
39 using OuterSolver = alpaqa::ALMSolver<InnerSolver>;
40
41 // Settings for the outer augmented Lagrangian method
42 OuterSolver::Params almparam;
43 almparam.tolerance = 1e-8; // tolerance
44 almparam.dual_tolerance = 1e-8;
45 almparam.penalty_update_factor = 10; // penalty update factor
46 almparam.max_iter = 20;
47 almparam.print_interval = 1;
48
49 // Settings for the inner PANOC solver
50 InnerSolver::Params panocparam;
51 panocparam.max_iter = 500;
52 panocparam.print_interval = 10;
53 // Settings for the L-BFGS algorithm used by PANOC
54 Direction::AcceleratorParams lbfgsparam;
55 lbfgsparam.memory = 2;
56
57 // Create an ALM solver using PANOC as inner solver
58 OuterSolver solver{
59 almparam, // params for outer solver
60 {panocparam, lbfgsparam}, // inner solver
61 };
62
63 // Initial guess
64 vec x(2);
65 x << 2, 2; // decision variables
66 vec y(1);
67 y << 1; // Lagrange multipliers
68
69 // Solve the problem
70 auto stats = solver(counted_problem, x, y);
71 // y and x have been overwritten by the solution
72
73 // Print the results
74 std::cout << '\n' << *counted_problem.evaluations << '\n';
75 std::cout << "status: " << stats.status << '\n'
76 << "solver: " << solver.get_name() << '\n'
77 << "f = " << problem.eval_f(x) << '\n'
78 << "inner iterations: " << stats.inner.iterations << '\n'
79 << "outer iterations: " << stats.outer_iterations << '\n'
80 << "ε = " << stats.ε << '\n'
81 << "δ = " << stats.δ << '\n'
82 << "elapsed time: "
83 << std::chrono::duration<double>{stats.elapsed_time}.count()
84 << " s" << '\n'
85 << "x = " << x.transpose() << '\n'
86 << "y = " << y.transpose() << '\n'
87 << "avg τ = " << (stats.inner.sum_τ / stats.inner.count_τ) << '\n'
88 << "L-BFGS rejected = " << stats.inner.lbfgs_rejected << '\n'
89 << "L-BFGS failures = " << stats.inner.lbfgs_failures << '\n'
90 << "Line search failures = " << stats.inner.linesearch_failures
91 << '\n'
92 << std::endl;
93}
int main(int argc, const char *argv[])
Augmented Lagrangian Method solver.
Definition alm.hpp:69
PANOC solver for ALM.
Definition panoc.hpp:139
Class that loads a problem using dlopen.
#define USING_ALPAQA_CONFIG(Conf)
Definition config.hpp:56
auto problem_with_counters_ref(Problem &p)
Wraps the given problem into a ProblemWithCounters and keeps track of how many times each function is...
constexpr const auto inf
Definition config.hpp:85
typename Conf::vec vec
Definition config.hpp:66
Double-precision double configuration.
Definition config.hpp:135