This example generates a random optimal control problem (without inequality constraints), and solves the resulting KKT system using Cyqlone.
6#include <iostream>
7#include <print>
8#include <random>
9#include <string>
10
11#if CYQLONE_WITH_MATIO
13#endif
14
15using cyqlone::index_t;
16using cyqlone::real_t;
19
20
24
25 std::mt19937 rng{12345};
26 std::uniform_real_distribution<real_t> uni_dist{-1.2, 1.2};
27 auto uni_gen = [&] { return uni_dist(rng); };
28
30 auto diag = 2 * static_cast<real_t>(nx + nu);
31 ocp.
b(0).generate(uni_gen);
32 for (index_t j = 0; j < N; ++j) {
33 ocp.
A(j).generate(uni_gen);
34 ocp.
B(j).generate(uni_gen);
35 ocp.
b(j + 1).generate(uni_gen);
36 ocp.
Q(j).generate(uni_gen);
37 ocp.
R(j).generate(uni_gen);
38 ocp.
S(j).generate(uni_gen);
39 ocp.
q(j).generate(uni_gen);
40 ocp.
r(j).generate(uni_gen);
41 ocp.
Q(j).add_to_diagonal(diag);
42 ocp.
R(j).add_to_diagonal(diag);
44 }
45 ocp.
Q(N).generate(uni_gen);
46 ocp.
q(N).generate(uni_gen);
47 ocp.
Q(N).add_to_diagonal(diag);
49 return ocp;
50}
51
52
53int main(
int argc,
char *argv[])
try {
54
55 const index_t p = argc > 1 ? std::stoi(argv[1]) : 8;
56 const index_t N = argc > 2 ? std::stoi(argv[2]) : 128;
57 const index_t nx = argc > 3 ? std::stoi(argv[3]) : 10;
58 const index_t nu = argc > 4 ? std::stoi(argv[4]) : 5;
60
61
62 auto ocp = init_ocp(N, nx, nu);
63
65
66
68
73
74
77
81 const real_t γ = 1e99;
82
83
85 auto t0 = std::chrono::high_resolution_clock::now();
89 });
90 auto t1 = std::chrono::high_resolution_clock::now();
91 std::println("Time: {:.3f} ms", std::chrono::duration<double, std::milli>(t1 - t0).count());
92
93
100 });
101
102
104 std::println(
"Eq. residual (compact): {:.17e}",
norm_inf(Mxb));
105 std::println(
"Lagr. stationarity (compact): {:.17e}",
norm_inf(grad));
106
107
110 auto sol = cocp.reconstruct_solution(ocp, ux_unpacked, {}, λ_unpacked);
111
112
113 auto resid = ocp.compute_kkt_error(sol);
114 std::println("Eq. residual (full): {:.17e}", resid.equality_residual);
115 std::println("Lagr. stationarity (full): {:.17e}", resid.stationarity);
116
117#if CYQLONE_WITH_MATIO
118
119 std::filesystem::path filename = "ocp_solution.mat";
124 std::cout << "Saved OCP and solution to " << filename << "\n";
125#endif
126} catch (std::exception &e) {
127 std::cerr << "Error: " << e.what() << "\n";
128 return 1;
129}
Data structure for optimal control problems where the initial states are eliminated.
The main header for the Cyqlone and Tricyqle linear solvers.
SolveMethod solve_method
Algorithm to use for solving the final reduced block tridiagonal system.
@ PCR
Parallel Cyclic Reduction (direct).
simdified_value_t< Vx > norm_inf(Vx &&x)
Compute the infinity norm of a vector.
void negate(VA &&A, VB &&B, with_rotate_t< Rotate >={})
Negate a matrix or vector B = -A.
void copy(VA &&A, VB &&B, Opts... opts)
constexpr auto triu(M &&m)
constexpr auto tril(M &&m)
MatFilePtr create_mat(const std::filesystem::path &filename)
Create and open a new .mat file for writing.
void add_to_mat(mat_t *mat, const std::string &varname, float value)
Add a value to an open .mat file.
Functions for exporting and loading matrices and OCP data to and from .mat files.
constexpr index_t vl_at_most
constexpr bool is_pow_2(index_t n)
Data structure for optimal control problems.
Linear solver for systems with optimal control structure.
matrix initialize_variables() const
Get a zero-initialized matrix for the primal variables u and x.
void factor_solve(Context &ctx, value_type γ, view<> Σ, mut_view<> ux, mut_view<> λ)
Compute the Cyqlone factorization of the KKT matrix of the OCP and perform a forward solve (fused for...
void initialize_gradient(const CyqloneStorage< value_type > &ocp, mut_view<> grad) const
Initialize the gradient vector for the OCP cost function, using the custom Cyqlone storage format.
void unpack_variables(view<> ux, std::span< value_type > ux_lin) const
void transposed_dynamics_constr(Context &ctx, view<> λ, mut_view<> Mᵀλ, bool accum=false) const
Compute Mᵀλ, where M is the dynamics constraint Jacobian matrix of the OCP.
void residual_dynamics_constr(Context &ctx, view<> x, view<> b, mut_view<> Mxb) const
Compute Mx + b, where M is the dynamics constraint Jacobian matrix of the OCP.
TricyqleParams< value_type > get_tricyqle_params() const
Get the current Tricyqle solver parameters.
std::unique_ptr< SharedContext > create_parallel_context() const
std::string get_params_string() const
Get a string representation of the main solver parameters. Used mainly for file names.
tricyqle_t::Context Context
static CyqloneSolver build(const CyqloneStorage< value_type > &ocp, index_t p)
Initialize a Cyqlone solver for the given OCP.
void cost_gradient(Context &ctx, view<> ux, value_type α, view<> q, value_type β, mut_view<> grad_f) const
Compute the cost gradient, with optional scaling factors.
matrix initialize_dynamics_constraints() const
Get a zero-initialized matrix for the dynamics constraints (or their multipliers).
void initialize_rhs(const CyqloneStorage< value_type > &ocp, mut_view<> rhs) const
Initialize the right-hand side vector for the dynamics constraints of the OCP, using the custom Cyqlo...
void solve_reverse(Context &ctx, mut_view<> ux, mut_view<> λ)
Perform a reverse solve with the Cyqlone factorization.
void update_tricyqle_params(const TricyqleParams< value_type > &new_params)
Update the Tricyqle solver parameters.
void unpack_dynamics(view<> λ, std::span< value_type > λ_lin) const
static CyqloneStorage build(const LinearOCPStorage &ocp, index_t ny_0=-1)
Storage for a linear-quadratic OCP of the form.
guanaqo::MatrixView< real_t, index_t > r(index_t i)
guanaqo::MatrixView< real_t, index_t > q(index_t i)
guanaqo::MatrixView< real_t, index_t > B(index_t i)
guanaqo::MatrixView< real_t, index_t > b()
guanaqo::MatrixView< real_t, index_t > R(index_t i)
guanaqo::MatrixView< real_t, index_t > H(index_t i)
guanaqo::MatrixView< real_t, index_t > S(index_t i)
guanaqo::MatrixView< real_t, index_t > Q(index_t i)
guanaqo::MatrixView< real_t, index_t > A(index_t i)