alpaqa pi-pico
Nonconvex constrained optimization
Loading...
Searching...
No Matches
C++/CustomCppProblem/main.cpp

This example shows how to define optimization problems using ordinary C++ functions.

This example shows how to define optimization problems using ordinary C++ functions.

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 specification

The quadratic program described above can be formulated as a general NLP of the following form:

\[ \begin{aligned} & \underset{x}{\text{minimize}} && f(x) \\ & \text{subject to} && x \in C \subseteq \R^n \\ &&& g(x) \in D \subseteq \R^m. \\ \end{aligned} \]

The problem is specified by creating a class (Problem) that inherits from alpaqa's alpaqa::BoxConstrProblem class. It defines the problem-specific functions for the evaluation of the cost function \( f(x) = \tfrac12 \tp xQx \) (eval_f) and its gradient \( \nabla f(x) = Qx \) (eval_grad_f), as well as the constraint function \( g(x) = Ax \) (eval_g) and the function that evaluates the product of the gradient of the constraints and a given vector \( y \), \( \nabla g(x)\,y = \tp A y \) (eval_grad_g_prod).

If you have more efficient ways to combine evaluations of these functions and gradients, you can specify them as well, see the Problem formulations page and the alpaqa::TypeErasedProblem documentation for the full list of supported functions.

The alpaqa::BoxConstrProblem class exposes the two constraint sets, \( x \in C \) and \( g(x) \in D \), and provides the projection functions (eval_prox_grad_step, eval_proj_diff_g and eval_proj_multipliers) for you. The alpaqa::BoxConstrProblem constructor accepts the number of variables \( n = 2 \) and the number of constraints \( m = 1 \) as arguments.

Note
Alpaqa uses structural typing for problem definitions. This means that you just have to provide the supported functions with the correct names and arguments, and the library will pick them up automatically, you don't have to inherit from any abstract interfaces or override any virtual functions. See Problem formulations for more information.
See also
If you haven't already, be sure to go through the Problem formulations page.

Solver selection

The solver consists of three layers:

  1. The outer augmented Lagrangian solver (alpaqa::ALMSolver) that handles the general constraints \( g(x) \in D \);
  2. The inner PANOC solver (alpaqa::PANOCSolver) that is used to solve the ALM subproblems;
  3. The L-BFGS direction (alpaqa::LBFGSDirection) that provides fast Newton-type directions to speed up PANOC.

You can try out different inner solvers (Inner solvers) and direction providers (Direction providers), you can even write your own.

Solver configuration

Each solver and direction class has a set of parameters, which are collected in a struct. Tuning these parameters can often significantly improve solver performance. You can also use them to limit the run time or the number of iterations, and to enable verbose output from the solvers.

Solver invocation

The solver is invoked by calling the solver object with an instance of the problem and an initial guess for the Lagrange multipliers and the decision variables. The solver will overwrite these guesses with the (approximate) solution, and returns a struct containing solver statistics, the most important of which is the status, which reports whether convergence to the desired tolerance was achieved.

1#include <alpaqa/example-util.hpp>
5
6#include <iostream>
7
8int main() {
9 alpaqa::init_stdout();
11
12 // Problem specification
13 // minimize ½ xᵀQx
14 // s.t. Ax ≤ b
15 struct Problem : alpaqa::BoxConstrProblem<config_t> {
16 mat Q{n, n};
17 mat A{m, n};
18 vec b{m};
19 mutable vec Qx{n};
20
21 Problem() : alpaqa::BoxConstrProblem<config_t>{2, 1} {
22 // Initialize problem matrices
23 Q << 3, -1, -1, 3;
24 A << 2, 1;
25 b << -1;
26
27 // Specify the bounds
28 C.lowerbound = vec::Constant(n, -alpaqa::inf<config_t>);
29 C.upperbound = vec::Constant(n, +alpaqa::inf<config_t>);
30 D.lowerbound = vec::Constant(m, -alpaqa::inf<config_t>);
31 D.upperbound = b;
32 }
33
34 // Evaluate the cost
35 real_t eval_f(crvec x) const {
36 Qx.noalias() = Q * x;
37 return 0.5 * x.dot(Qx);
38 }
39 // Evaluat the gradient of the cost
40 void eval_grad_f(crvec x, rvec gr) const { gr.noalias() = Q * x; }
41 // Evaluate the constraints
42 void eval_g(crvec x, rvec g) const { g.noalias() = A * x; }
43 // Evaluate a matrix-vector product with the gradient of the constraints
44 void eval_grad_g_prod(crvec x, crvec y, rvec gr) const {
45 (void)x;
46 gr.noalias() = A.transpose() * y;
47 }
48 };
49
50 Problem problem;
51
52 // Wrap the problem to count the function evaluations
53 auto counted_problem = alpaqa::problem_with_counters_ref(problem);
54
55 // Define the solvers to use
56 using Direction = alpaqa::LBFGSDirection<config_t>;
57 using InnerSolver = alpaqa::PANOCSolver<Direction>;
58 using OuterSolver = alpaqa::ALMSolver<InnerSolver>;
59
60 // Settings for the outer augmented Lagrangian method
61 OuterSolver::Params almparam;
62 almparam.tolerance = 1e-8; // tolerance
63 almparam.dual_tolerance = 1e-8;
64 almparam.penalty_update_factor = 10; // penalty update factor
65 almparam.max_iter = 20;
66 almparam.print_interval = 1;
67
68 // Settings for the inner PANOC solver
69 InnerSolver::Params panocparam;
70 panocparam.max_iter = 500;
71 panocparam.print_interval = 1;
72 // Settings for the L-BFGS algorithm used by PANOC
73 Direction::AcceleratorParams lbfgsparam;
74 lbfgsparam.memory = 2;
75
76 // Create an ALM solver using PANOC as inner solver
77 OuterSolver solver{
78 almparam, // params for outer solver
79 {panocparam, lbfgsparam}, // inner solver
80 };
81
82 // Initial guess
83 vec x(2);
84 x << 2, 2; // decision variables
85 vec y(1);
86 y << 1; // Lagrange multipliers
87
88 // Solve the problem
89 auto stats = solver(counted_problem, x, y);
90 // y and x have been overwritten by the solution
91
92 // Print the results
93 std::cout << '\n' << *counted_problem.evaluations << '\n';
94 std::cout << "status: " << stats.status << '\n'
95 << "f = " << problem.eval_f(x) << '\n'
96 << "inner iterations: " << stats.inner.iterations << '\n'
97 << "outer iterations: " << stats.outer_iterations << '\n'
98 << "ε = " << stats.ε << '\n'
99 << "δ = " << stats.δ << '\n'
100 << "elapsed time: "
101 << std::chrono::duration<double>{stats.elapsed_time}.count()
102 << " s" << '\n'
103 << "x = " << x.transpose() << '\n'
104 << "y = " << y.transpose() << '\n'
105 << "avg τ = " << (stats.inner.sum_τ / stats.inner.count_τ) << '\n'
106 << "L-BFGS rejected = " << stats.inner.lbfgs_rejected << '\n'
107 << "L-BFGS failures = " << stats.inner.lbfgs_failures << '\n'
108 << "Line search failures = " << stats.inner.linesearch_failures
109 << '\n'
110 << std::endl;
111}
int main(int argc, const char *argv[])
Augmented Lagrangian Method solver.
Definition alm.hpp:69
Implements common problem functions for minimization problems with box constraints.
length_t m
Number of constraints, dimension of g(x) and z.
length_t n
Number of decision variables, dimension of x.
PANOC solver for ALM.
Definition panoc.hpp:142
#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
Double-precision double configuration.
Definition config.hpp:176