1515*/
1616
1717#include < iostream>
18+ #include < limits>
1819#include < vector>
1920#include < string>
2021#include < memory>
@@ -203,6 +204,53 @@ class ThrowingPrecomputeSolver : public CDDPSolverBase {
203204 void printIteration (int iter, const CDDP &context) const override {}
204205};
205206
207+ class FixedDualDimConstraint : public Constraint {
208+ public:
209+ explicit FixedDualDimConstraint (int dual_dim, const std::string& name = " FixedDualDimConstraint" )
210+ : Constraint(name), dual_dim_(dual_dim) {}
211+
212+ int getDualDim () const override { return dual_dim_; }
213+
214+ Eigen::VectorXd evaluate (const Eigen::VectorXd& state,
215+ const Eigen::VectorXd& control,
216+ int index = 0 ) const override {
217+ return Eigen::VectorXd::Zero (dual_dim_);
218+ }
219+
220+ Eigen::VectorXd getLowerBound () const override {
221+ return Eigen::VectorXd::Constant (dual_dim_, -std::numeric_limits<double >::infinity ());
222+ }
223+
224+ Eigen::VectorXd getUpperBound () const override {
225+ return Eigen::VectorXd::Zero (dual_dim_);
226+ }
227+
228+ Eigen::MatrixXd getStateJacobian (const Eigen::VectorXd& state,
229+ const Eigen::VectorXd& control,
230+ int index = 0 ) const override {
231+ return Eigen::MatrixXd::Zero (dual_dim_, state.size ());
232+ }
233+
234+ Eigen::MatrixXd getControlJacobian (const Eigen::VectorXd& state,
235+ const Eigen::VectorXd& control,
236+ int index = 0 ) const override {
237+ return Eigen::MatrixXd::Zero (dual_dim_, control.size ());
238+ }
239+
240+ double computeViolation (const Eigen::VectorXd& state,
241+ const Eigen::VectorXd& control,
242+ int index = 0 ) const override {
243+ return 0.0 ;
244+ }
245+
246+ double computeViolationFromValue (const Eigen::VectorXd& g) const override {
247+ return 0.0 ;
248+ }
249+
250+ private:
251+ int dual_dim_;
252+ };
253+
206254// Factory functions for the mock solvers
207255std::unique_ptr<ISolverAlgorithm> createMockExternalSolver () {
208256 return std::make_unique<MockExternalSolver>();
@@ -214,6 +262,8 @@ std::unique_ptr<ISolverAlgorithm> createAnotherMockSolver() {
214262
215263} // namespace cddp
216264
265+ using cddp::FixedDualDimConstraint;
266+
217267// Test fixture for CDDP core functionality
218268class CDDPCoreTest : public ::testing::Test {
219269protected:
@@ -493,3 +543,103 @@ TEST_F(CDDPCoreTest, IntegrationWithTrajectoryAndOptions) {
493543 EXPECT_EQ (solution.state_trajectory .size (), horizon + 1 );
494544 EXPECT_EQ (solution.control_trajectory .size (), horizon);
495545}
546+
547+ TEST_F (CDDPCoreTest, SetReferenceStatesUpdatesObjectiveTerminalReference) {
548+ cddp::CDDP cddp_solver (initial_state, goal_state, horizon, timestep,
549+ std::make_unique<cddp::Unicycle>(timestep, " euler" ),
550+ std::make_unique<cddp::QuadraticObjective>(
551+ Eigen::MatrixXd::Identity (state_dim, state_dim),
552+ Eigen::MatrixXd::Identity (control_dim, control_dim),
553+ 10.0 * Eigen::MatrixXd::Identity (state_dim, state_dim),
554+ goal_state, std::vector<Eigen::VectorXd>(), timestep),
555+ options);
556+
557+ std::vector<Eigen::VectorXd> reference_states (horizon + 1 ,
558+ Eigen::VectorXd::Zero (state_dim));
559+ for (int k = 0 ; k <= horizon; ++k) {
560+ reference_states[k] << 0.1 * k, 0.2 * k, 0.3 * k;
561+ }
562+
563+ cddp_solver.setReferenceStates (reference_states);
564+
565+ Eigen::VectorXd zero_control = Eigen::VectorXd::Zero (control_dim);
566+ EXPECT_TRUE (cddp_solver.getReferenceState ().isApprox (reference_states.back ()));
567+ EXPECT_NEAR (
568+ cddp_solver.getObjective ().running_cost (reference_states.front (),
569+ zero_control, 0 ),
570+ 0.0 , 1e-12 );
571+ EXPECT_NEAR (
572+ cddp_solver.getObjective ().terminal_cost (reference_states.back ()),
573+ 0.0 , 1e-12 );
574+ }
575+
576+ TEST_F (CDDPCoreTest, SetObjectiveUsesExistingReferenceTrajectoryTerminalState) {
577+ cddp::CDDP cddp_solver (initial_state, goal_state, horizon, timestep,
578+ std::make_unique<cddp::Unicycle>(timestep, " euler" ),
579+ nullptr , options);
580+
581+ std::vector<Eigen::VectorXd> reference_states (horizon + 1 ,
582+ Eigen::VectorXd::Zero (state_dim));
583+ for (int k = 0 ; k < horizon; ++k) {
584+ reference_states[k] << 1.0 + 0.1 * k, 0.5 + 0.1 * k, 0.2 + 0.1 * k;
585+ }
586+ reference_states.back () = Eigen::VectorXd::Zero (state_dim);
587+ cddp_solver.setReferenceStates (reference_states);
588+
589+ cddp_solver.setObjective (std::make_unique<cddp::QuadraticObjective>(
590+ Eigen::MatrixXd::Identity (state_dim, state_dim),
591+ Eigen::MatrixXd::Identity (control_dim, control_dim),
592+ 10.0 * Eigen::MatrixXd::Identity (state_dim, state_dim), goal_state,
593+ std::vector<Eigen::VectorXd>(), timestep));
594+
595+ Eigen::VectorXd zero_control = Eigen::VectorXd::Zero (control_dim);
596+ EXPECT_NEAR (
597+ cddp_solver.getObjective ().running_cost (reference_states.front (),
598+ zero_control, 0 ),
599+ 0.0 , 1e-12 );
600+ EXPECT_NEAR (
601+ cddp_solver.getObjective ().terminal_cost (reference_states.back ()),
602+ 0.0 , 1e-12 );
603+ }
604+
605+ TEST_F (CDDPCoreTest, ReplacingConstraintsKeepsTotalDualDimensionAccurate) {
606+ cddp::CDDP cddp_solver (initial_state, goal_state, horizon, timestep,
607+ std::make_unique<cddp::Unicycle>(timestep, " euler" ),
608+ std::make_unique<cddp::QuadraticObjective>(
609+ Eigen::MatrixXd::Identity (state_dim, state_dim),
610+ Eigen::MatrixXd::Identity (control_dim, control_dim),
611+ 10.0 * Eigen::MatrixXd::Identity (state_dim, state_dim),
612+ goal_state, std::vector<Eigen::VectorXd>(), timestep),
613+ options);
614+
615+ Eigen::VectorXd control_bound_two_dim = Eigen::VectorXd::Ones (control_dim);
616+ cddp_solver.addPathConstraint (
617+ " RepeatedPathConstraint" ,
618+ std::make_unique<cddp::ControlConstraint>(-control_bound_two_dim,
619+ control_bound_two_dim));
620+ EXPECT_EQ (cddp_solver.getTotalDualDim (), 2 * control_dim);
621+
622+ Eigen::VectorXd control_bound_one_dim = Eigen::VectorXd::Ones (1 );
623+ cddp_solver.addPathConstraint (
624+ " RepeatedPathConstraint" ,
625+ std::make_unique<cddp::ControlConstraint>(-control_bound_one_dim,
626+ control_bound_one_dim));
627+ EXPECT_EQ (cddp_solver.getTotalDualDim (), 2 );
628+
629+ cddp_solver.addTerminalConstraint (
630+ " RepeatedTerminalConstraint" ,
631+ std::make_unique<FixedDualDimConstraint>(state_dim));
632+ EXPECT_EQ (cddp_solver.getTotalDualDim (), 2 + state_dim);
633+
634+ cddp_solver.addTerminalConstraint (
635+ " RepeatedTerminalConstraint" ,
636+ std::make_unique<FixedDualDimConstraint>(1 ));
637+ EXPECT_EQ (cddp_solver.getTotalDualDim (), 3 );
638+
639+ EXPECT_TRUE (cddp_solver.removePathConstraint (" RepeatedPathConstraint" ));
640+ EXPECT_EQ (cddp_solver.getTotalDualDim (), 1 );
641+
642+ EXPECT_TRUE (
643+ cddp_solver.removeTerminalConstraint (" RepeatedTerminalConstraint" ));
644+ EXPECT_EQ (cddp_solver.getTotalDualDim (), 0 );
645+ }
0 commit comments