13#include <smooth/concepts/lie_group.hpp>
14#include <smooth/diff.hpp>
18namespace smooth::feedback {
22template<LieGroup _X, Manifold _U,
int _Nq,
int _Ncr,
int _Nce>
50template<LieGroup _X, Manifold _U,
typename Theta,
typename F,
typename G,
typename CR,
typename CE>
59 static constexpr int Nx = Dof<X>;
61 static constexpr int Nu = Dof<U>;
63 static constexpr int Nq = std::invoke_result_t<G, double, X, U>::SizeAtCompileTime;
65 static constexpr int Ncr = std::invoke_result_t<CR, double, X, U>::SizeAtCompileTime;
67 static constexpr int Nce = std::invoke_result_t<CE, double, X, X, Eigen::Matrix<double, Nq, 1>>::SizeAtCompileTime;
72 static_assert(
Nx > 0,
"Static size required");
73 static_assert(
Nu > 0,
"Static size required");
74 static_assert(
Nq > 0,
"Static size required");
75 static_assert(
Ncr > 0,
"Static size required");
76 static_assert(
Nce > 0,
"Static size required");
89 Eigen::Vector<double, Ncr>
crl = Eigen::Vector<double, Ncr>::Zero();
91 Eigen::Vector<double, Ncr>
cru = Eigen::Vector<double, Ncr>::Zero();
96 Eigen::Vector<double, Nce>
cel = Eigen::Vector<double, Nce>::Zero();
98 Eigen::Vector<double, Nce>
ceu = Eigen::Vector<double, Nce>::Zero();
103concept OCPType = traits::is_specialization_of_v<std::decay_t<T>, OCP>;
108 OCPType<T> &&(smooth::RnType<typename std::decay_t<T>::X> && smooth::RnType<typename std::decay_t<T>::U>);
113template<LieGroup _X, Manifold _U,
int _Nq,
int _Ncr,
int _Nce>
122 static constexpr int Nq = _Nq;
124 static constexpr int Ncr = _Ncr;
126 static constexpr int Nce = _Nce;
134 Eigen::Vector<double, Nq>
Q{};
138 std::function<
U(
double)>
u;
139 std::function<
X(
double)>
x;
149 std::function<Eigen::Vector<double, Dof<X>>(double)>
lambda_dyn{};
152 std::function<Eigen::Vector<double, Ncr>(
double)>
lambda_cr{};
165template<diff::Type DT = diff::Type::Numerical>
168 using OCP = std::decay_t<
decltype(ocp)>;
170 using X =
typename OCP::X;
171 using U =
typename OCP::U;
172 using Q = Eigen::Vector<double, OCP::Nq>;
174 if (!diff::detail::diffable_order1<
decltype(ocp.theta), std::tuple<double, X, X, Q>>) {
175 std::cout <<
"no jacobian for theta\n";
177 if (!diff::detail::diffable_order2<
decltype(ocp.theta), std::tuple<double, X, X, Q>>) {
178 std::cout <<
"no hessian for theta\n";
180 if (!diff::detail::diffable_order1<
decltype(ocp.f), std::tuple<double, X, U>>) { std::cout <<
"no jacobian for f\n"; }
181 if (!diff::detail::diffable_order2<
decltype(ocp.f), std::tuple<double, X, U>>) { std::cout <<
"no hessian for f\n"; }
182 if (!diff::detail::diffable_order1<
decltype(ocp.g), std::tuple<double, X, U>>) { std::cout <<
"no jacobian for g\n"; }
183 if (!diff::detail::diffable_order2<
decltype(ocp.g), std::tuple<double, X, U>>) { std::cout <<
"no hessian for g\n"; }
184 if (!diff::detail::diffable_order1<
decltype(ocp.cr), std::tuple<double, X, U>>) {
185 std::cout <<
"no jacobian for cr\n";
187 if (!diff::detail::diffable_order2<
decltype(ocp.cr), std::tuple<double, X, U>>) {
188 std::cout <<
"no hessian for cr\n";
190 if (!diff::detail::diffable_order1<
decltype(ocp.ce), std::tuple<double, X, X, Q>>) {
191 std::cout <<
"no jacobian for ce\n";
193 if (!diff::detail::diffable_order2<
decltype(ocp.ce), std::tuple<double, X, X, Q>>) {
194 std::cout <<
"no hessian for ce\n";
197 const auto cmp = [&eps](
const auto & m1,
const auto & m2) {
200 (m1.cols() == m2.cols())
201 && (m1.rows() == m2.rows())
203 m1.isApprox(m2, 1e-4) ||
204 Eigen::MatrixXd(m1 - m2).cwiseAbs().maxCoeff() < eps
212 for (
auto trial = 0u; trial < num_trials; ++trial) {
214 const double tf = 1 +
static_cast<double>(std::rand()) / RAND_MAX;
215 const X x0 = Random<X>();
216 const X xf = Random<X>();
217 const Eigen::Vector<double, OCP::Nq> q = Eigen::Vector<double, OCP::Nq>::Random();
218 const double t = 1 +
static_cast<double>(std::rand()) / RAND_MAX;
219 const X x = Random<X>();
220 const U u = Random<U>();
223 if constexpr (diff::detail::diffable_order1<
decltype(ocp.theta), std::tuple<double, X, X, Q>>) {
224 const auto [f_def, df_def] = diff::dr<1, diff::Type::Analytic>(ocp.theta, wrt(tf, x0, xf, q));
225 const auto [f_num, df_num] = diff::dr<1, DT>(ocp.theta, wrt(tf, x0, xf, q));
227 if (!cmp(df_def, df_num)) {
228 std::cout <<
"Error in 1st derivative of theta: got\n"
229 << Eigen::MatrixXd(df_def) <<
"\nbut expected\n"
230 << Eigen::MatrixXd(df_num) <<
'\n';
234 if constexpr (diff::detail::diffable_order2<
decltype(ocp.theta), std::tuple<double, X, X, Q>>) {
235 const auto [f_def, df_def, d2f_def] = diff::dr<2, diff::Type::Analytic>(ocp.theta, wrt(tf, x0, xf, q));
236 const auto [f_num, df_num, d2f_num] = diff::dr<2, DT>(ocp.theta, wrt(tf, x0, xf, q));
238 if (!cmp(d2f_def, d2f_num)) {
239 std::cout <<
"Error in 2nd derivative of theta: got\n"
240 << Eigen::MatrixXd(d2f_def) <<
"\nbut expected\n"
241 << Eigen::MatrixXd(d2f_num) <<
'\n';
247 if constexpr (diff::detail::diffable_order1<
decltype(ocp.ce), std::tuple<double, X, X, Q>>) {
248 const auto [f_def, df_def] = diff::dr<1, diff::Type::Analytic>(ocp.ce, wrt(tf, x0, xf, q));
249 const auto [f_num, df_num] = diff::dr<1, DT>(ocp.ce, wrt(tf, x0, xf, q));
251 if (!cmp(df_def, df_num)) {
252 std::cout <<
"Error in 1st derivative of ce: got\n"
253 << Eigen::MatrixXd(df_def) <<
"\nbut expected\n"
254 << Eigen::MatrixXd(df_num) <<
'\n';
258 if constexpr (diff::detail::diffable_order2<
decltype(ocp.ce), std::tuple<double, X, X, Q>>) {
259 const auto [f_def, df_def, d2f_def] = diff::dr<2, diff::Type::Analytic>(ocp.ce, wrt(tf, x0, xf, q));
260 const auto [f_num, df_num, d2f_num] = diff::dr<2, DT>(ocp.ce, wrt(tf, x0, xf, q));
262 if (!cmp(d2f_def, d2f_num)) {
263 std::cout <<
"Error in 2nd derivative of ce: got\n"
264 << Eigen::MatrixXd(d2f_def) <<
"\nbut expected\n"
265 << Eigen::MatrixXd(d2f_num) <<
'\n';
271 if constexpr (diff::detail::diffable_order1<
decltype(ocp.f), std::tuple<double, X, U>>) {
272 const auto [f_def, df_def] = diff::dr<1, diff::Type::Analytic>(ocp.f, wrt(t, x, u));
273 const auto [f_num, df_num] = diff::dr<1, DT>(ocp.f, wrt(t, x, u));
274 if (!cmp(df_def, df_num)) {
275 std::cout <<
"Error in 1st derivative of f: got\n"
276 << Eigen::MatrixXd(df_def) <<
"\nbut expected\n"
277 << Eigen::MatrixXd(df_num) <<
'\n';
281 if constexpr (diff::detail::diffable_order2<
decltype(ocp.f), std::tuple<double, X, U>>) {
282 const auto [f_def, df_def, d2f_def] = diff::dr<2, diff::Type::Analytic>(ocp.f, wrt(t, x, u));
283 const auto [f_num, df_num, d2f_num] = diff::dr<2, DT>(ocp.f, wrt(t, x, u));
284 if (!cmp(d2f_def, d2f_num)) {
285 std::cout <<
"Error in 2nd derivative of f: got\n"
286 << Eigen::MatrixXd(d2f_def) <<
"\nbut expected\n"
287 << Eigen::MatrixXd(d2f_num) <<
'\n';
293 if constexpr (diff::detail::diffable_order1<
decltype(ocp.g), std::tuple<double, X, U>>) {
294 const auto [f_def, df_def] = diff::dr<1, diff::Type::Analytic>(ocp.g, wrt(t, x, u));
295 const auto [f_num, df_num] = diff::dr<1, DT>(ocp.g, wrt(t, x, u));
296 if (!cmp(df_def, df_num)) {
297 std::cout <<
"Error in 1st derivative of g: got\n"
298 << Eigen::MatrixXd(df_def) <<
"\nbut expected\n"
299 << Eigen::MatrixXd(df_num) <<
'\n';
303 if constexpr (diff::detail::diffable_order2<
decltype(ocp.g), std::tuple<double, X, U>>) {
304 const auto [f_def, df_def, d2f_def] = diff::dr<2, diff::Type::Analytic>(ocp.g, wrt(t, x, u));
305 const auto [f_num, df_num, d2f_num] = diff::dr<2, DT>(ocp.g, wrt(t, x, u));
306 if (!cmp(d2f_def, d2f_num)) {
307 std::cout <<
"Error in 2nd derivative of g: got\n"
308 << Eigen::MatrixXd(d2f_def) <<
"\nbut expected\n"
309 << Eigen::MatrixXd(d2f_num) <<
'\n';
315 if constexpr (diff::detail::diffable_order1<
decltype(ocp.cr), std::tuple<double, X, U>>) {
316 const auto [f_def, df_def] = diff::dr<1, diff::Type::Analytic>(ocp.cr, wrt(t, x, u));
317 const auto [f_num, df_num] = diff::dr<1, DT>(ocp.cr, wrt(t, x, u));
318 if (!cmp(df_def, df_num)) {
319 std::cout <<
"Error in 1st derivative of cr: got\n"
320 << Eigen::MatrixXd(df_def) <<
"\nbut expected\n"
321 << Eigen::MatrixXd(df_num) <<
'\n';
325 if constexpr (diff::detail::diffable_order2<
decltype(ocp.cr), std::tuple<double, X, U>>) {
326 const auto [f_def, df_def, d2f_def] = diff::dr<2, diff::Type::Analytic>(ocp.cr, wrt(t, x, u));
327 const auto [f_num, df_num, d2f_num] = diff::dr<2, DT>(ocp.cr, wrt(t, x, u));
328 if (!cmp(d2f_def, d2f_num)) {
329 std::cout <<
"Error in 2nd derivative of cr: got\n"
330 << Eigen::MatrixXd(d2f_def) <<
"\nbut expected\n"
331 << Eigen::MatrixXd(d2f_num) <<
'\n';
Concept that is true for FlatOCP specializations.
Concept that is true for OCP specializations.
bool test_ocp_derivatives(OCPType auto &ocp, uint32_t num_trials=1, double eps=1e-4)
Test analytic derivatives for an OCP problem.
static constexpr int Nq
Number of integrals.
static constexpr int Ncr
Number of running constraints.
std::function< Eigen::Vector< double, Dof< X > >(double)> lambda_dyn
Multipliers for dynamics equality constraint.
double tf
Initial and final time.
std::function< Eigen::Vector< double, Ncr >(double)> lambda_cr
Multipliers for active running constraints.
static constexpr int Nce
Number of end constraints.
double t0
Initial and final time.
std::function< X(double)> x
Initial and final time.
std::function< U(double)> u
Callable functions for state and input.
Eigen::Vector< double, Nce > lambda_ce
Multipliers for endpoint constraints.
Eigen::Vector< double, Nq > lambda_q
Multipliers for integral constraints.
Eigen::Vector< double, Nq > Q
Integral values.
Optimal control problem definition.
static constexpr int Nu
Input space dimension.
static constexpr int Nx
State space dimension.
static constexpr int Nq
Number of integrals.
static constexpr int Ncr
Number of running constraints.
Eigen::Vector< double, Ncr > cru
Running constraint upper bound .
Theta theta
Objective function .
Eigen::Vector< double, Nce > ceu
End constraint upper bound .
static constexpr int Nce
Number of end constraints.
CR cr
Running constraint .
Eigen::Vector< double, Nce > cel
End constraint lower bound .
Eigen::Vector< double, Ncr > crl
Running constraint lower bound .