@@ -242,6 +242,98 @@ namespace cddp
242242 }
243243 }
244244
245+ bool warmstartNeedsReinit (const Eigen::VectorXd &y_current,
246+ const Eigen::VectorXd &s_current,
247+ const Eigen::VectorXd &g_val,
248+ const CDDPOptions &options)
249+ {
250+ if (y_current.size () != g_val.size () || s_current.size () != g_val.size () ||
251+ !y_current.allFinite () || !s_current.allFinite ())
252+ {
253+ return true ;
254+ }
255+
256+ for (int i = 0 ; i < g_val.size (); ++i)
257+ {
258+ if (y_current (i) <= EPS_DUAL || s_current (i) <= EPS_SLACK)
259+ {
260+ return true ;
261+ }
262+
263+ const double required_slack =
264+ std::max (options.ipddp .slack_var_init_scale ,
265+ -g_val (i) + kSlackInteriorOffset );
266+ if (s_current (i) < 0.1 * required_slack)
267+ {
268+ return true ;
269+ }
270+ }
271+
272+ return false ;
273+ }
274+
275+ void initializeTerminalWarmstartDualSlack (
276+ const CDDP &context,
277+ double mu,
278+ std::map<std::string, Eigen::VectorXd> &G_T,
279+ std::map<std::string, Eigen::VectorXd> &Y_T,
280+ std::map<std::string, Eigen::VectorXd> &S_T,
281+ std::map<std::string, Eigen::VectorXd> &dY_T,
282+ std::map<std::string, Eigen::VectorXd> &dS_T)
283+ {
284+ const CDDPOptions &options = context.getOptions ();
285+ const auto layout = getTerminalInequalityLayout (context);
286+
287+ bool has_existing_dual_slack = true ;
288+ for (const auto &entry : layout)
289+ {
290+ auto y_it = Y_T.find (entry.name );
291+ auto s_it = S_T.find (entry.name );
292+ if (y_it == Y_T.end () || s_it == S_T.end () ||
293+ y_it->second .size () != entry.dim || s_it->second .size () != entry.dim )
294+ {
295+ has_existing_dual_slack = false ;
296+ break ;
297+ }
298+ }
299+
300+ for (const auto &entry : layout)
301+ {
302+ const Eigen::VectorXd &g_val = G_T.at (entry.name );
303+ Eigen::VectorXd s_current = Eigen::VectorXd::Zero (entry.dim );
304+ Eigen::VectorXd y_current = Eigen::VectorXd::Zero (entry.dim );
305+ bool need_reinit = !has_existing_dual_slack;
306+
307+ if (!need_reinit)
308+ {
309+ s_current = S_T.at (entry.name );
310+ y_current = Y_T.at (entry.name );
311+ need_reinit =
312+ warmstartNeedsReinit (y_current, s_current, g_val, options);
313+ }
314+
315+ if (need_reinit)
316+ {
317+ s_current = g_val.unaryExpr ([&](double g) {
318+ return std::max (options.ipddp .slack_var_init_scale ,
319+ -g + kSlackInteriorOffset );
320+ });
321+ for (int i = 0 ; i < entry.dim ; ++i)
322+ {
323+ y_current (i) =
324+ (mu * options.ipddp .dual_var_init_scale ) /
325+ std::max (s_current (i), EPS_SLACK);
326+ }
327+ }
328+
329+ repairWarmstartInterior (s_current, y_current, options);
330+ S_T[entry.name ] = s_current;
331+ Y_T[entry.name ] = y_current;
332+ dS_T[entry.name ] = Eigen::VectorXd::Zero (entry.dim );
333+ dY_T[entry.name ] = Eigen::VectorXd::Zero (entry.dim );
334+ }
335+ }
336+
245337 void rolloutLinearPolicy (const std::vector<Eigen::MatrixXd> &A,
246338 const std::vector<Eigen::MatrixXd> &B,
247339 const std::vector<Eigen::VectorXd> &d,
@@ -587,9 +679,19 @@ namespace cddp
587679 Lambda_T_eq_ = Eigen::VectorXd::Zero (getTerminalEqualityDim (context));
588680 dLambda_T_eq_ = Eigen::VectorXd::Zero (getTerminalEqualityDim (context));
589681
682+ const auto warmstart_dual = Y_;
683+ const auto warmstart_slack = S_;
684+ const auto warmstart_terminal_dual = Y_T_;
685+ const auto warmstart_terminal_slack = S_T_;
590686 initializeConstraintStorage (context);
687+ Y_ = warmstart_dual;
688+ S_ = warmstart_slack;
689+ Y_T_ = warmstart_terminal_dual;
690+ S_T_ = warmstart_terminal_slack;
591691 evaluateTrajectoryWarmStart (context);
592692 initializeDualSlackVariablesWarmStart (context);
693+ initializeTerminalWarmstartDualSlack (context, mu_, G_T_, Y_T_, S_T_,
694+ dY_T_, dS_T_);
593695 G_ = G_raw_;
594696 resetFilter (context);
595697 return ;
@@ -614,7 +716,15 @@ namespace cddp
614716 Lambda_T_eq_ = Eigen::VectorXd::Zero (getTerminalEqualityDim (context));
615717 dLambda_T_eq_ = Eigen::VectorXd::Zero (getTerminalEqualityDim (context));
616718
719+ const auto warmstart_dual = Y_;
720+ const auto warmstart_slack = S_;
721+ const auto warmstart_terminal_dual = Y_T_;
722+ const auto warmstart_terminal_slack = S_T_;
617723 initializeConstraintStorage (context);
724+ Y_ = warmstart_dual;
725+ S_ = warmstart_slack;
726+ Y_T_ = warmstart_terminal_dual;
727+ S_T_ = warmstart_terminal_slack;
618728
619729 // Re-rollout trajectory with provided controls
620730 if (static_cast <int >(context.U_ .size ()) != horizon)
@@ -660,6 +770,8 @@ namespace cddp
660770 context.alpha_pr_ = 1.0 ;
661771 context.alpha_du_ = 1.0 ;
662772 initializeDualSlackVariablesWarmStart (context);
773+ initializeTerminalWarmstartDualSlack (context, mu_, G_T_, Y_T_, S_T_,
774+ dY_T_, dS_T_);
663775 G_ = G_raw_;
664776 resetFilter (context);
665777 context.inf_du_ = 0.0 ;
@@ -1473,13 +1585,15 @@ namespace cddp
14731585 double expected_improvement = alpha_pr * dV_ (0 );
14741586 double constraint_violation_old =
14751587 filter_.empty () ? 0.0 : filter_.back ().constraint_violation ;
1588+ const double high_violation_reference =
1589+ filter_.empty () ? filter_theta_ : constraint_violation_old;
14761590 double merit_function_old = context.merit_function_ ;
14771591
14781592 if (theta_new > options.filter .max_violation_threshold )
14791593 {
14801594 if (theta_new <
14811595 (1 - options.filter .violation_acceptance_threshold ) *
1482- constraint_violation_old )
1596+ high_violation_reference )
14831597 {
14841598 filter_acceptance = true ;
14851599 }
@@ -1619,6 +1733,7 @@ namespace cddp
16191733 context.inf_pr_ = result.inf_pr ;
16201734 context.inf_comp_ = result.inf_comp ;
16211735 phi_ = result.merit_function ;
1736+ filter_theta_ = result.theta ;
16221737 theta_ = result.theta ;
16231738
16241739 // Update barrier parameter before convergence check (matching old solve loop timing)
@@ -2019,7 +2134,85 @@ namespace cddp
20192134
20202135 void IPDDPSolver::initializeDualSlackVariablesWarmStart (CDDP &context)
20212136 {
2022- initializeDualSlackVariables (context);
2137+ const CDDPOptions &options = context.getOptions ();
2138+ const int horizon = context.getHorizon ();
2139+ const auto &constraint_set = context.getConstraintSet ();
2140+
2141+ bool has_existing_dual_slack = true ;
2142+ for (const auto &constraint_pair : constraint_set)
2143+ {
2144+ const std::string &constraint_name = constraint_pair.first ;
2145+ auto y_it = Y_.find (constraint_name);
2146+ auto s_it = S_.find (constraint_name);
2147+ if (y_it == Y_.end () || s_it == S_.end () ||
2148+ y_it->second .size () != static_cast <size_t >(horizon) ||
2149+ s_it->second .size () != static_cast <size_t >(horizon))
2150+ {
2151+ has_existing_dual_slack = false ;
2152+ break ;
2153+ }
2154+ }
2155+
2156+ for (const auto &constraint_pair : constraint_set)
2157+ {
2158+ const std::string &constraint_name = constraint_pair.first ;
2159+ const int dual_dim = constraint_pair.second ->getDualDim ();
2160+
2161+ if (!has_existing_dual_slack)
2162+ {
2163+ Y_[constraint_name].resize (horizon);
2164+ S_[constraint_name].resize (horizon);
2165+ }
2166+
2167+ dY_[constraint_name].resize (horizon);
2168+ dS_[constraint_name].resize (horizon);
2169+ k_y_[constraint_name].resize (horizon);
2170+ K_y_[constraint_name].resize (horizon);
2171+ k_s_[constraint_name].resize (horizon);
2172+ K_s_[constraint_name].resize (horizon);
2173+
2174+ for (int t = 0 ; t < horizon; ++t)
2175+ {
2176+ const Eigen::VectorXd &g_val = G_raw_[constraint_name][t];
2177+ Eigen::VectorXd s_current = Eigen::VectorXd::Zero (dual_dim);
2178+ Eigen::VectorXd y_current = Eigen::VectorXd::Zero (dual_dim);
2179+ bool need_reinit = !has_existing_dual_slack;
2180+
2181+ if (!need_reinit)
2182+ {
2183+ s_current = S_[constraint_name][t];
2184+ y_current = Y_[constraint_name][t];
2185+ need_reinit =
2186+ warmstartNeedsReinit (y_current, s_current, g_val, options);
2187+ }
2188+
2189+ if (need_reinit)
2190+ {
2191+ s_current = g_val.unaryExpr ([&](double g) {
2192+ return std::max (options.ipddp .slack_var_init_scale ,
2193+ -g + kSlackInteriorOffset );
2194+ });
2195+ for (int i = 0 ; i < dual_dim; ++i)
2196+ {
2197+ y_current (i) =
2198+ (mu_ * options.ipddp .dual_var_init_scale ) /
2199+ std::max (s_current (i), EPS_SLACK);
2200+ }
2201+ }
2202+
2203+ repairWarmstartInterior (s_current, y_current, options);
2204+ Y_[constraint_name][t] = y_current;
2205+ S_[constraint_name][t] = s_current;
2206+ dY_[constraint_name][t] = Eigen::VectorXd::Zero (dual_dim);
2207+ dS_[constraint_name][t] = Eigen::VectorXd::Zero (dual_dim);
2208+ k_y_[constraint_name][t] = Eigen::VectorXd::Zero (dual_dim);
2209+ K_y_[constraint_name][t] =
2210+ Eigen::MatrixXd::Zero (dual_dim, context.getStateDim ());
2211+ k_s_[constraint_name][t] = Eigen::VectorXd::Zero (dual_dim);
2212+ K_s_[constraint_name][t] =
2213+ Eigen::MatrixXd::Zero (dual_dim, context.getStateDim ());
2214+ }
2215+ }
20232216 }
20242217
20252218 void IPDDPSolver::initializeDualSlackVariables (CDDP &context)
@@ -2098,16 +2291,18 @@ namespace cddp
20982291 context.inf_pr_ = inf_pr;
20992292 context.inf_comp_ = inf_comp;
21002293 phi_ = context.merit_function_ ;
2101- theta_ =
2294+ filter_theta_ =
21022295 std::max (computeTheta (context.getOptions (), G_, S_,
21032296 has_terminal_ineq ? &G_T_ : nullptr ,
21042297 has_terminal_ineq ? &S_T_ : nullptr ,
21052298 has_terminal_eq ? &h_T : nullptr ),
2106- std::max (context.getOptions ().ipddp .theta_0_floor , 1e-8 ));
2299+ 1e-8 );
2300+ theta_ = std::max (filter_theta_,
2301+ std::max (context.getOptions ().ipddp .theta_0_floor , 1e-8 ));
21072302 filter_.clear ();
21082303 if (has_terminal_ineq || has_terminal_eq)
21092304 {
2110- acceptFilterEntry (phi_, theta_ );
2305+ acceptFilterEntry (phi_, filter_theta_ );
21112306 }
21122307 }
21132308
@@ -2208,31 +2403,37 @@ namespace cddp
22082403 }
22092404 }
22102405
2406+ const bool has_terminal_ineq = !getTerminalInequalityLayout (context).empty ();
2407+ const bool has_terminal_eq = getTerminalEqualityDim (context) > 0 ;
2408+ Eigen::VectorXd h_T =
2409+ has_terminal_eq
2410+ ? evaluateTerminalEqualityResidual (context, context.X_ .back ())
2411+ : Eigen::VectorXd::Zero (0 );
2412+ const double filter_theta =
2413+ std::max (computeTheta (options, G_, S_,
2414+ has_terminal_ineq ? &G_T_ : nullptr ,
2415+ has_terminal_ineq ? &S_T_ : nullptr ,
2416+ has_terminal_eq ? &h_T : nullptr ),
2417+ 1e-8 );
2418+
22112419 const bool reset_filter = (mu_ < mu_old) && (mu_ > 0.0 );
22122420 if (reset_filter)
22132421 {
22142422 filter_.clear ();
22152423 if (getTerminalEqualityDim (context) > 0 ||
22162424 !getTerminalInequalityLayout (context).empty ())
22172425 {
2218- acceptFilterEntry (phi_, theta_ );
2426+ acceptFilterEntry (phi_, filter_theta );
22192427 }
22202428 }
22212429 else
22222430 {
2223- acceptFilterEntry (phi_, theta_ );
2431+ acceptFilterEntry (phi_, filter_theta );
22242432 if (static_cast <int >(filter_.size ()) > options.ipddp .max_filter_size )
22252433 {
22262434 detail::pruneFilterToBestPoints (filter_);
22272435 }
22282436 }
2229-
2230- const bool has_terminal_ineq = !getTerminalInequalityLayout (context).empty ();
2231- const bool has_terminal_eq = getTerminalEqualityDim (context) > 0 ;
2232- Eigen::VectorXd h_T =
2233- has_terminal_eq
2234- ? evaluateTerminalEqualityResidual (context, context.X_ .back ())
2235- : Eigen::VectorXd::Zero (0 );
22362437 const auto [inf_pr, inf_comp] = computePrimalAndComplementarity (
22372438 context, G_, S_, Y_, mu_, has_terminal_ineq ? &G_T_ : nullptr ,
22382439 has_terminal_ineq ? &S_T_ : nullptr , has_terminal_ineq ? &Y_T_ : nullptr ,
@@ -2244,10 +2445,8 @@ namespace cddp
22442445 has_terminal_eq ? &Lambda_T_eq_ : nullptr ,
22452446 has_terminal_eq ? &h_T : nullptr );
22462447 phi_ = context.merit_function_ ;
2247- theta_ = std::max (computeTheta (options, G_, S_,
2248- has_terminal_ineq ? &G_T_ : nullptr ,
2249- has_terminal_ineq ? &S_T_ : nullptr ,
2250- has_terminal_eq ? &h_T : nullptr ),
2448+ filter_theta_ = filter_theta;
2449+ theta_ = std::max (filter_theta,
22512450 std::max (options.ipddp .theta_0_floor , 1e-8 ));
22522451 }
22532452
0 commit comments