alpaqa 1.1.0a1
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_objective_gradient(void *instance, const real_t *x,
real_t *grad_f) {
struct ProblemData *problem = instance;
length_t n = problem->functions.num_variables;
matmul(n, n, 1, problem->Q, x, grad_f); // grad_f = Q x
}
static real_t eval_objective_and_gradient(void *instance, const real_t *x,
real_t *grad_f) {
struct ProblemData *problem = instance;
length_t n = problem->functions.num_variables;
real_t result;
eval_objective_gradient(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_objective(void *instance, const real_t *x) {
struct ProblemData *problem = instance;
return eval_objective_and_gradient(instance, x, problem->work);
}
static void eval_constraints(void *instance, const real_t *x, real_t *gx) {
struct ProblemData *problem = instance;
length_t n = problem->functions.num_variables;
length_t m = problem->functions.num_constraints;
matmul(m, n, 1, problem->A, x, gx); // gx = A x
}
static void eval_constraints_gradient_product(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.num_variables;
length_t m = problem->functions.num_constraints;
matvec_transp(m, n, problem->A, y, grad_gxy); // grad_gxy = Aᵀ y
}
static void eval_constraints_jacobian(void *instance, const real_t *x,
real_t *J_values) {
(void)x;
struct ProblemData *problem = instance;
size_t n = (size_t)problem->functions.num_variables;
size_t m = (size_t)problem->functions.num_constraints;
memcpy(J_values, problem->A, sizeof(real_t) * m * n);
}
static void initialize_general_bounds(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){
.num_variables = (length_t)n,
.num_constraints = (length_t)m,
.name = "example problem",
.eval_objective = &eval_objective,
.eval_objective_gradient = &eval_objective_gradient,
.eval_constraints = &eval_constraints,
.eval_constraints_gradient_product = &eval_constraints_gradient_product,
.eval_constraints_jacobian = &eval_constraints_jacobian,
.eval_objective_and_gradient = &eval_objective_and_gradient,
.initialize_general_bounds = &initialize_general_bounds,
};
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
void(* cleanup)(void *)
Pointer to the function to clean up instance.
Definition dl-problem.h:452
ptrdiff_t alpaqa_length_t
Definition dl-problem.h:27
alpaqa_length_t alpaqa_index_t
Definition dl-problem.h:28
uint64_t alpaqa_dl_abi_version_t
Definition dl-problem.h:29
double alpaqa_real_t
Definition dl-problem.h:26
#define ALPAQA_PROBLEM_REGISTER_INIT(self)
Initialize an instance of alpaqa_problem_register_t or alpaqa_control_problem_register_t.
Definition dl-problem.h:661
void * instance
Owning pointer.
Definition dl-problem.h:448
alpaqa_problem_functions_t * functions
Non-owning pointer, lifetime at least as long as instance.
Definition dl-problem.h:450
C API providing function pointers to problem functions.
Definition dl-problem.h:205
User-provided argument that is passed to the problem registration functions.
Definition dl-problem.h:60
typename Conf::real_t real_t
Definition config.hpp:86
typename Conf::length_t length_t
Definition config.hpp:103

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[]) {
15 alpaqa::init_stdout();
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_num_variables();
32 length_t m = problem.get_num_constraints();
33 vec b = vec::Constant(m, -1);
34 const auto inf = alpaqa::inf<config_t>;
35 problem.variable_bounds.lower = vec::Constant(n, -inf);
36 problem.variable_bounds.upper = vec::Constant(n, +inf);
37 problem.general_bounds.lower = vec::Constant(m, -inf);
38 problem.general_bounds.upper = b;
39
40 // Define the solvers to use
42 using InnerSolver = alpaqa::PANOCSolver<Direction>;
43 using OuterSolver = alpaqa::ALMSolver<InnerSolver>;
44
45 // Settings for the outer augmented Lagrangian method
46 OuterSolver::Params almparam;
47 almparam.tolerance = 1e-8; // tolerance
48 almparam.dual_tolerance = 1e-8;
49 almparam.penalty_update_factor = 10; // penalty update factor
50 almparam.max_iter = 20;
51 almparam.print_interval = 1;
52
53 // Settings for the inner PANOC solver
54 InnerSolver::Params panocparam;
55 panocparam.max_iter = 500;
56 panocparam.print_interval = 10;
57 // Settings for the L-BFGS algorithm used by PANOC
58 Direction::AcceleratorParams lbfgsparam;
59 lbfgsparam.memory = 2;
60
61 // Create an ALM solver using PANOC as inner solver
62 OuterSolver solver{
63 almparam, // params for outer solver
64 {panocparam, lbfgsparam}, // inner solver
65 };
66
67 // Initial guess
68 vec x(2);
69 x << 2, 2; // decision variables
70 vec y(1);
71 y << 1; // Lagrange multipliers
72
73 // Solve the problem
74 auto stats = solver(counted_problem, x, y);
75 // y and x have been overwritten by the solution
76
77 // Print the results
78 std::cout << '\n' << *counted_problem.evaluations << '\n';
79 std::cout //
80 << "status: " << stats.status << '\n'
81 << "solver: " << solver.get_name() << '\n'
82 << "f = " << problem.eval_objective(x) << '\n'
83 << "inner iterations: " << stats.inner.iterations << '\n'
84 << "outer iterations: " << stats.outer_iterations << '\n'
85 << "ε = " << stats.ε << '\n'
86 << "δ = " << stats.δ << '\n'
87 << "elapsed time: "
88 << std::chrono::duration<double>{stats.elapsed_time}.count() << " s"
89 << '\n'
90 << "x = " << x.transpose() << '\n'
91 << "y = " << y.transpose() << '\n'
92 << "avg τ = " << (stats.inner.sum_τ / stats.inner.count_τ) << '\n'
93 << "L-BFGS rejected = " << stats.inner.direction_update_rejected << '\n'
94 << "L-BFGS failures = " << stats.inner.direction_failures << '\n'
95 << "Line search failures = " << stats.inner.linesearch_failures << '\n'
96 << std::endl;
97}
int main(int argc, const char *argv[])
Augmented Lagrangian Method solver.
Definition alm.hpp:69
Box general_bounds
Other constraints, .
Box variable_bounds
Constraints of the decision variables, .
length_t get_num_constraints() const
Number of constraints , num_constraints.
length_t get_num_variables() const
Number of decision variables , num_variables.
PANOC solver for ALM.
Definition panoc.hpp:142
Class that loads a problem using dlopen.
real_t eval_objective(crvec x) const
#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...
EigenConfigd DefaultConfig
Definition config.hpp:31
constexpr const auto inf
Definition config.hpp:112
typename Conf::vec vec
Definition config.hpp:88