alpaqa sparse
Nonconvex constrained optimization
Loading...
Searching...
No Matches
C++/CustomControlCppProblem/main.cpp

Bare-bones example demonstrating how to define optimal control problems for alpaqa::PANOCOCPSolver.

Bare-bones example demonstrating how to define optimal control problems for alpaqa::PANOCOCPSolver.

5
6#include <iomanip>
7#include <iostream>
8
9struct Problem {
11 using Box = alpaqa::Box<config_t>;
12
13 length_t N = 32, // Horizon length
14 nu = 1, // Number of inputs
15 nx = 2, // Number of states
16 nh = nu + nx, // Number of stage outputs
17 nh_N = nx, // Number of terminal outputs
18 nc = 0, // Number of stage constraints
19 nc_N = 0; // Number of terminal constraints
20
21 mat A, B;
22
23 Problem() : A(nx, nx), B(nx, nu) {
24 A.setIdentity();
25 B.setIdentity();
26 }
27
28 // Horizon length
29 [[nodiscard]] length_t get_N() const { return N; }
30 // Number of inputs
31 [[nodiscard]] length_t get_nu() const { return nu; }
32 // Number of states
33 [[nodiscard]] length_t get_nx() const { return nx; }
34 // Number of stage outputs
35 [[nodiscard]] length_t get_nh() const { return nh; }
36 // Number of terminal outputs
37 [[nodiscard]] length_t get_nh_N() const { return nh_N; }
38 // Number of stage constraints
39 [[nodiscard]] length_t get_nc() const { return nc; }
40 // Number of terminal constraints
41 [[nodiscard]] length_t get_nc_N() const { return nc_N; }
42
43 // Bound constraints on the inputs u.
44 void get_U(Box &U) const {
45 U.lowerbound.setConstant(-1);
46 U.upperbound.setConstant(+1);
47 }
48 // Bound constraints on function of the states
49 void get_D([[maybe_unused]] Box &D) const {}
50 // Bound constraints on function of the terminal state
51 void get_D_N([[maybe_unused]] Box &D) const {}
52
53 // Get the initial state
54 void get_x_init(rvec x_init) const { x_init.setConstant(10.); }
55
56 // Discrete-time dynamics x⁺ = f(x, u)
57 void eval_f([[maybe_unused]] index_t timestep, crvec x, crvec u,
58 rvec fxu) const {
59 fxu.noalias() = A * x + B * u;
60 }
61 // Jacobian of dynamics
62 void eval_jac_f([[maybe_unused]] index_t timestep, [[maybe_unused]] crvec x,
63 [[maybe_unused]] crvec u, rmat J_fxu) const {
64 J_fxu.leftCols(nx).noalias() = A;
65 J_fxu.rightCols(nu).noalias() = B;
66 }
67 // Gradient-vector product of dynamics
68 void eval_grad_f_prod([[maybe_unused]] index_t timestep,
69 [[maybe_unused]] crvec x, [[maybe_unused]] crvec u,
70 crvec p, rvec grad_fxu_p) const {
71 grad_fxu_p.topRows(nx).noalias() = A.transpose() * p;
72 grad_fxu_p.bottomRows(nu).noalias() = B.transpose() * p;
73 }
74 // Output mapping
75 void eval_h([[maybe_unused]] index_t timestep, crvec x, crvec u,
76 rvec h) const {
77 h.topRows(nx) = x;
78 h.bottomRows(nu) = u;
79 }
80 // Terminal output mapping
81 void eval_h_N(crvec x, rvec h) const { h = x; }
82 // Stage cost
83 [[nodiscard]] real_t eval_l([[maybe_unused]] index_t timestep,
84 crvec h) const {
85 return 0.5 * h.squaredNorm();
86 }
87 // Terminal cost
88 [[nodiscard]] real_t eval_l_N(crvec h) const {
89 return 5.0 * h.squaredNorm();
90 }
91 // Gradient of the cost l(h(x, u))
92 void eval_qr([[maybe_unused]] index_t timestep, [[maybe_unused]] crvec xu,
93 crvec h, rvec qr) const {
94 auto Jh_xu = mat::Identity(nx + nu, nx + nu);
95 auto &&grad_l = h;
96 qr = Jh_xu.transpose() * grad_l;
97 }
98 // Gradient of the terminal cost l_N(h_N(x))
99 void eval_q_N([[maybe_unused]] crvec x, crvec h, rvec q) const {
100 auto Jh_x = mat::Identity(nx, nx);
101 auto &&grad_l = 10 * h;
102 q = Jh_x.transpose() * grad_l;
103 }
104 // Hessian of stage cost w.r.t. x
105 void eval_add_Q([[maybe_unused]] index_t timestep,
106 [[maybe_unused]] crvec xu, [[maybe_unused]] crvec h,
107 rmat Q) const {
108 Q += mat::Identity(nx, nx);
109 }
110 // Hessian of terminal cost
111 void eval_add_Q_N([[maybe_unused]] crvec x, [[maybe_unused]] crvec h,
112 rmat Q) const {
113 Q += 10 * mat::Identity(nx, nx);
114 }
115 // Hessian of stage cost w.r.t. u
116 void eval_add_R_masked([[maybe_unused]] index_t timestep,
117 [[maybe_unused]] crvec xu, [[maybe_unused]] crvec h,
118 crindexvec mask, rmat R,
119 [[maybe_unused]] rvec work) const {
120 auto R_full = mat::Identity(nu, nu);
121 R.noalias() += R_full(mask, mask);
122 }
123 // Hessian of stage cost w.r.t. x and u
124 void eval_add_S_masked([[maybe_unused]] index_t timestep,
125 [[maybe_unused]] crvec xu, [[maybe_unused]] crvec h,
126 crindexvec mask, rmat S,
127 [[maybe_unused]] rvec work) const {
128 // Mixed derivatives are zero, so the following has no effect
129 using Eigen::indexing::all;
130 auto S_full = mat::Zero(nu, nx);
131 S += S_full(mask, all);
132 }
133 // Hessian-vector product of stage cost w.r.t. u
134 void eval_add_R_prod_masked([[maybe_unused]] index_t timestep,
135 [[maybe_unused]] crvec xu,
136 [[maybe_unused]] crvec h, crindexvec mask_J,
137 crindexvec mask_K, crvec v, rvec out,
138 [[maybe_unused]] rvec work) const {
139 // R is diagonal, and J ∩ K = ∅, so the following has no effect
140 auto R_full = mat::Identity(nu, nu);
141 out.noalias() += R_full(mask_J, mask_K) * v(mask_K);
142 }
143 // Hessian-vector product of stage cost w.r.t. u and x
144 void eval_add_S_prod_masked([[maybe_unused]] index_t timestep,
145 [[maybe_unused]] crvec xu,
146 [[maybe_unused]] crvec h,
147 [[maybe_unused]] crindexvec mask_K,
148 [[maybe_unused]] crvec v,
149 [[maybe_unused]] rvec out,
150 [[maybe_unused]] rvec work) const {
151 // Mixed derivatives are zero, so the following has no effect
152 using Eigen::indexing::all;
153 auto Sᵀ = mat::Zero(nx, nu);
154 out.noalias() += Sᵀ(all, mask_K) * v(mask_K);
155 }
156 [[nodiscard]] length_t get_R_work_size() const {
157 // No workspace needed to share data between eval_add_R_masked and
158 // eval_add_R_prod_masked
159 return 0;
160 }
161 [[nodiscard]] length_t get_S_work_size() const {
162 // No workspace needed to share data between eval_add_S_masked and
163 // eval_add_S_prod_masked
164 return 0;
165 }
166
167 // Project the Lagrange multipliers for ALM
168 void eval_proj_multipliers([[maybe_unused]] rvec y,
169 [[maybe_unused]] real_t M) const {
170 // Empty for unconstrained problems
171 }
172
173 // Get the distance to the feasible set
174 void eval_proj_diff_g([[maybe_unused]] crvec z,
175 [[maybe_unused]] rvec e) const {
176 // Empty for unconstrained problems
177 }
178
179 void check() const {
180 // You could do some sanity checks here
181 }
182};
183
184int main() {
186
187 // Problem
189
190 // Problem dimensions
191 const auto n = problem.get_N() * problem.get_nu(),
192 m = problem.get_N() * problem.get_nc() + problem.get_nc_N();
193
194 // Initial guess and other solver inputs
195 vec u = vec::Zero(n); // Inputs (single shooting)
196 vec y = vec::Zero(m); // Lagrange multipliers
197 vec μ = vec::Ones(m); // Penalty factors
198 vec e(m); // Constraint violation
199
200 // Solver
203 params.gn_interval = 1;
204 params.print_interval = 1;
206
207 // Solve
208 auto stats = solver(problem, {.tolerance = 1e-8}, u, y, μ, e);
209
210 // Print statistics
211 auto δ = e.lpNorm<Eigen::Infinity>();
212 auto time_s = std::chrono::duration<double>(stats.elapsed_time).count();
213 std::cout << '\n'
214 << *problem.evaluations << '\n'
215 << "solver: " << solver.get_name() << '\n'
216 << "status: " << stats.status << '\n'
217 << "ψ = " << alpaqa::float_to_str(stats.final_ψ) << '\n'
218 << "ε = " << alpaqa::float_to_str(stats.ε) << '\n'
219 << "δ = " << alpaqa::float_to_str(δ) << '\n'
220 << "time: " << alpaqa::float_to_str(time_s, 3) << " s\n"
221 << "iter: " << std::setw(6) << stats.iterations << '\n'
222 << "line search backtrack: " << std::setw(6)
223 << stats.linesearch_backtracks << '\n'
224 << "step size backtrack: " << std::setw(6)
225 << stats.stepsize_backtracks << '\n'
226 << "solution: ";
227 alpaqa::print_python(std::cout, u) << std::endl;
228}
int main(int argc, const char *argv[])
#define USING_ALPAQA_CONFIG(Conf)
Definition config.hpp:56
@ ProjGradUnitNorm
∞-norm of the projected gradient with unit step size:
unsigned print_interval
When to print progress.
Definition panoc-ocp.hpp:53
std::ostream & print_python(std::ostream &os, const Eigen::DenseBase< Derived > &M, Args &&...args)
Definition print.hpp:68
std::string float_to_str(F value, int precision)
Definition print.tpp:67
unsigned gn_interval
How often to use a Gauss-Newton step. Zero to disable GN entirely.
Definition panoc-ocp.hpp:43
PANOCStopCrit stop_crit
What stopping criterion to use.
Definition panoc-ocp.hpp:39
Tuning parameters for the PANOC algorithm.
Definition panoc-ocp.hpp:16
std::shared_ptr< OCPEvalCounter > evaluations
Double-precision double configuration.
Definition config.hpp:135