alpaqa develop
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(alpaqa_register_arg_t 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,
.name = "example problem",
.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(alpaqa_register_arg_t user_data) {
struct ProblemData *problem = create_problem(user_data);
result.instance = problem;
result.cleanup = &cleanup_problem;
result.functions = &problem->functions;
return result;
}
PROBLEM_C_EXPORT alpaqa_dl_abi_version_t register_alpaqa_problem_version(void) {
}
#define ALPAQA_DL_ABI_VERSION
Definition dl-problem.h:8
ptrdiff_t alpaqa_length_t
Definition dl-problem.h:32
alpaqa_length_t alpaqa_index_t
Definition dl-problem.h:33
uint64_t alpaqa_dl_abi_version_t
Definition dl-problem.h:34
double alpaqa_real_t
Definition dl-problem.h:31
#define ALPAQA_PROBLEM_REGISTER_INIT(self)
Initialize an instance of alpaqa_problem_register_t or alpaqa_control_problem_register_t.
Definition dl-problem.h:663
User-provided argument that is passed to the problem registration functions.
Definition dl-problem.h:65
typename Conf::real_t real_t
Definition config.hpp:86
typename Conf::length_t length_t
Definition config.hpp:103
C API providing function pointers to problem functions.
Definition dl-problem.h:210
void(* cleanup)(void *)
Pointer to the function to clean up instance.
Definition dl-problem.h:456
void * instance
Owning pointer.
Definition dl-problem.h:452
alpaqa_problem_functions_t * functions
Non-owning pointer, lifetime at least as long as instance.
Definition dl-problem.h:454

Problem solution using alpaqa

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