45 void AICOSolver::Instantiate(
const AICOSolverInitializer& init)
47 std::string mode = init.SweepMode;
48 if (mode ==
"Forwardly")
49 sweep_mode_ = FORWARD;
50 else if (mode ==
"Symmetric")
51 sweep_mode_ = SYMMETRIC;
52 else if (mode ==
"LocalGaussNewton")
53 sweep_mode_ = LOCAL_GAUSS_NEWTON;
54 else if (mode ==
"LocalGaussNewtonDamped")
55 sweep_mode_ = LOCAL_GAUSS_NEWTON_DAMPED;
58 ThrowNamed(
"Unknown sweep mode '" << init.SweepMode <<
"'");
60 max_backtrack_iterations_ = init.MaxBacktrackIterations;
61 minimum_step_tolerance_ = init.MinStep;
62 step_tolerance_ = init.StepTolerance;
63 function_tolerance_ = init.FunctionTolerance;
64 damping_init_ = init.Damping;
65 use_bwd_msg_ = init.UseBackwardMessage;
66 verbose_ = init.Verbose;
69 AICOSolver::AICOSolver() =
default;
71 AICOSolver::~AICOSolver() =
default;
75 if (problem->type() !=
"exotica::UnconstrainedTimeIndexedProblem")
77 ThrowNamed(
"This solver can't use problem of type '" << problem->type() <<
"'!");
79 MotionSolver::SpecifyProblem(problem);
85 void AICOSolver::Solve(Eigen::MatrixXd& solution)
88 prob_->ResetCostEvolution(GetNumberOfMaxIterations() + 1);
89 prob_->termination_criterion = TerminationCriterion::NotStarted;
92 Eigen::VectorXd q0 = prob_->ApplyStartState();
93 std::vector<Eigen::VectorXd> q_init = prob_->GetInitialTrajectory();
98 if (!q0.isApprox(q_init[0]))
100 if (verbose_)
HIGHLIGHT(
"AICO::Solve cold-started");
101 q_init.assign(prob_->GetT(), q0);
105 if (verbose_)
HIGHLIGHT(
"AICO::Solve called with initial trajectory guess");
108 prob_->SetStartState(q_init[0]);
109 prob_->ApplyStartState();
112 if (prob_->GetT() != last_T_) InitMessages();
117 damping = damping_init_;
119 if (prob_->GetT() <= 0)
121 ThrowNamed(
"Problem has not been initialized properly: T=0!");
123 iteration_count_ = -1;
124 InitTrajectory(q_init);
129 iteration_count_ = 0;
130 while (iteration_count_ < GetNumberOfMaxIterations())
133 if (Server::IsRos() && !
ros::ok())
135 if (debug_)
HIGHLIGHT(
"Solving cancelled by user");
136 prob_->termination_criterion = TerminationCriterion::UserDefined;
147 if (damping != 0.0 && sweep_ >= max_backtrack_iterations_)
149 if (debug_)
HIGHLIGHT(
"Maximum backtrack iterations reached, exiting.");
150 prob_->termination_criterion = TerminationCriterion::BacktrackIterationLimit;
155 if (iteration_count_ > 1)
160 if ((damping != 0.0 && sweep_improved_cost_) || !(damping != 0.0))
167 if (d < minimum_step_tolerance_)
169 if (debug_)
HIGHLIGHT(
"Satisfied tolerance\titer=" << iteration_count_ <<
"\td=" << d <<
"\tminimum_step_tolerance=" << minimum_step_tolerance_);
170 prob_->termination_criterion = TerminationCriterion::StepTolerance;
176 if ((cost_prev_ - cost_) <= function_tolerance_ * std::max(1.0, std::abs(cost_)))
178 if (debug_)
HIGHLIGHT(
"Function tolerance achieved: " << (cost_prev_ - cost_) <<
" <= " << function_tolerance_ * std::max(1.0, std::abs(cost_)));
179 prob_->termination_criterion = TerminationCriterion::FunctionTolerance;
188 if (iteration_count_ == GetNumberOfMaxIterations())
190 if (debug_)
HIGHLIGHT(
"Maximum iterations reached");
191 prob_->termination_criterion = TerminationCriterion::IterationLimit;
194 Eigen::MatrixXd sol(prob_->GetT(), prob_->N);
195 for (
int tt = 0; tt < prob_->GetT(); ++tt)
203 void AICOSolver::InitMessages()
205 if (prob_ ==
nullptr)
ThrowNamed(
"Problem definition is a NULL pointer!");
209 ThrowNamed(
"State dimension is too small: n=" << prob_->N);
211 if (prob_->GetT() < 2)
213 ThrowNamed(
"Number of time steps is too small: T=" << prob_->GetT());
216 s.assign(prob_->GetT(), Eigen::VectorXd::Zero(prob_->N));
217 Sinv.assign(prob_->GetT(), Eigen::MatrixXd::Zero(prob_->N, prob_->N));
218 Sinv[0].diagonal().setConstant(1e10);
219 v.assign(prob_->GetT(), Eigen::VectorXd::Zero(prob_->N));
220 Vinv.assign(prob_->GetT(), Eigen::MatrixXd::Zero(prob_->N, prob_->N));
223 if (bwd_msg_v_.rows() == prob_->N && bwd_msg_Vinv_.rows() == prob_->N && bwd_msg_Vinv_.cols() == prob_->N)
225 v[prob_->GetT() - 1] = bwd_msg_v_;
226 Vinv[prob_->GetT() - 1] = bwd_msg_Vinv_;
230 use_bwd_msg_ =
false;
231 WARNING(
"Backward message initialisation skipped, matrices have incorrect dimensions.");
234 b.assign(prob_->GetT(), Eigen::VectorXd::Zero(prob_->N));
235 damping_reference_.assign(prob_->GetT(), Eigen::VectorXd::Zero(prob_->N));
236 Binv.assign(prob_->GetT(), Eigen::MatrixXd::Zero(prob_->N, prob_->N));
237 Binv[0].setIdentity();
238 Binv[0] = Binv[0] * 1e10;
239 r.assign(prob_->GetT(), Eigen::VectorXd::Zero(prob_->N));
240 R.assign(prob_->GetT(), Eigen::MatrixXd::Zero(prob_->N, prob_->N));
241 rhat = Eigen::VectorXd::Zero(prob_->GetT());
242 qhat.assign(prob_->GetT(), Eigen::VectorXd::Zero(prob_->N));
245 cost_control_.resize(prob_->GetT());
246 cost_control_.setZero();
247 cost_task_.resize(prob_->GetT());
248 cost_task_.setZero();
250 q_stat_.resize(prob_->GetT());
251 for (
int t = 0; t < prob_->GetT(); ++t)
253 q_stat_[t].resize(prob_->N);
257 last_T_ = prob_->GetT();
260 void AICOSolver::InitTrajectory(
const std::vector<Eigen::VectorXd>& q_init)
262 if (q_init.size() !=
static_cast<std::size_t
>(prob_->GetT()))
264 ThrowNamed(
"Incorrect number of timesteps provided!");
268 damping_reference_ = q_init;
272 for (
int t = 1; t < prob_->GetT(); ++t)
274 Sinv.at(t).setZero();
275 Sinv.at(t).diagonal().setConstant(damping);
277 for (
int t = 0; t < prob_->GetT(); ++t)
279 Vinv.at(t).setZero();
280 Vinv.at(t).diagonal().setConstant(damping);
282 for (
int t = 0; t < prob_->GetT(); ++t)
285 UpdateTaskMessage(t, b[t], 0.0);
289 if (prob_->W.rows() != prob_->N)
291 ThrowNamed(prob_->W.rows() <<
"!=" << prob_->N);
298 cost_ = EvaluateTrajectory(b,
true);
300 prob_->SetCostEvolution(0, cost_);
301 if (cost_ < 0)
ThrowNamed(
"Invalid cost! " << cost_);
302 if (debug_)
HIGHLIGHT(
"Initial cost, updates: " << update_count_ <<
", cost_(ctrl/task/total): " << cost_control_.sum() <<
"/" << cost_task_.sum() <<
"/" << cost_);
306 void AICOSolver::UpdateFwdMessage(
int t)
308 Eigen::MatrixXd barS(prob_->N, prob_->N), St;
310 s[t] = barS * (Sinv[t - 1] *
s[t - 1] +
r[t - 1]);
315 void AICOSolver::UpdateBwdMessage(
int t)
317 Eigen::MatrixXd barV(prob_->N, prob_->N), Vt;
318 if (t < prob_->GetT() - 1)
321 v[t] = barV * (Vinv[t + 1] * v[t + 1] +
r[t + 1]);
325 if (t == prob_->GetT() - 1)
330 Vinv[t].diagonal().setConstant(1);
334 v[prob_->GetT() - 1] = bwd_msg_v_;
335 Vinv[prob_->GetT() - 1] = bwd_msg_Vinv_;
340 void AICOSolver::UpdateTaskMessage(
int t,
341 const Eigen::Ref<const Eigen::VectorXd>& qhat_t,
double tolerance,
342 double max_step_size)
344 Eigen::VectorXd
diff = qhat_t - qhat[t];
345 if ((diff.array().abs().maxCoeff() < tolerance))
return;
346 double nrm = diff.norm();
347 if (max_step_size > 0. && nrm > max_step_size)
349 qhat[t] += diff * (max_step_size / nrm);
356 prob_->Update(qhat[t], t);
358 double c = GetTaskCosts(t);
359 q_stat_[t].addw(c > 0 ? 1.0 / (1.0 + c) : 1.0, qhat_t);
362 double AICOSolver::GetTaskCosts(
int t)
370 for (
int i = 0; i < prob_->cost.num_tasks; ++i)
372 prec = prob_->cost.rho[t](i);
375 int start = prob_->cost.indexing[i].start_jacobian;
376 int len = prob_->cost.indexing[i].length_jacobian;
377 Jt = prob_->cost.jacobian[t].middleRows(start, len).transpose();
378 C += prec * (prob_->cost.ydiff[t].segment(start, len)).squaredNorm();
379 R[t] += prec * Jt * prob_->cost.jacobian[t].middleRows(start, len);
380 r[t] += prec * Jt * (-prob_->cost.ydiff[t].segment(start, len) + prob_->cost.jacobian[t].middleRows(start, len) * qhat[t]);
381 rhat[t] += prec * (-prob_->cost.ydiff[t].segment(start, len) + prob_->cost.jacobian[t].middleRows(start, len) * qhat[t]).squaredNorm();
384 return prob_->get_ct() * C;
387 void AICOSolver::UpdateTimestep(
int t,
bool update_fwd,
bool update_bwd,
388 int max_relocation_iterations,
double tolerance,
bool force_relocation,
389 double max_step_size)
391 if (update_fwd) UpdateFwdMessage(t);
392 if (update_bwd) UpdateBwdMessage(t);
396 Binv[t] = Sinv[t] + Vinv[t] + R[t] + Eigen::MatrixXd::Identity(prob_->N, prob_->N) * damping;
397 AinvBSymPosDef(b[t], Binv[t], Sinv[t] *
s[t] + Vinv[t] * v[t] +
r[t] + damping * damping_reference_[t]);
401 Binv[t] = Sinv[t] + Vinv[t] + R[t];
405 for (
int k = 0; k < max_relocation_iterations && !(Server::IsRos() && !
ros::ok()); ++k)
407 if (!((!k && force_relocation) || (b[t] - qhat[t]).array().abs().maxCoeff() > tolerance))
break;
409 UpdateTaskMessage(t, b.at(t), 0., max_step_size);
412 if (update_fwd) UpdateFwdMessage(t);
413 if (update_bwd) UpdateBwdMessage(t);
417 Binv[t] = Sinv[t] + Vinv[t] + R[t] + Eigen::MatrixXd::Identity(prob_->N, prob_->N) * damping;
418 AinvBSymPosDef(b[t], Binv[t], Sinv[t] *
s[t] + Vinv[t] * v[t] +
r[t] + damping * damping_reference_[t]);
422 Binv[t] = Sinv[t] + Vinv[t] + R[t];
428 void AICOSolver::UpdateTimestepGaussNewton(
int t,
bool update_fwd,
429 bool update_bwd,
int max_relocation_iterations,
double tolerance,
430 double max_step_size)
436 double AICOSolver::EvaluateTrajectory(
const std::vector<Eigen::VectorXd>& x,
439 if (verbose_)
ROS_WARN_STREAM(
"Evaluating, iteration " << iteration_count_ <<
", sweep " << sweep_);
447 for (
int t = 0; t < prob_->GetT(); ++t)
450 if (!q[t].allFinite())
452 ThrowNamed(
"q[" << t <<
"] is not finite: " << q[t].transpose());
454 prob_->Update(q[t], t);
459 for (
int t = 1; t < prob_->GetT(); ++t)
461 if (Server::IsRos() && !
ros::ok())
return -1.0;
464 cost_control_(t) = prob_->GetScalarTransitionCost(t);
467 cost_task_(t) = prob_->GetScalarTaskCost(t);
470 cost_ = cost_control_.sum() + cost_task_.sum();
474 double AICOSolver::Step()
482 for (t = 1; t < prob_->GetT(); ++t)
484 UpdateTimestep(t,
true,
false, 1, minimum_step_tolerance_, !iteration_count_, 1.);
486 for (t = prob_->GetT() - 2; t > 0; t--)
488 UpdateTimestep(t,
false,
true, 0, minimum_step_tolerance_,
false, 1.);
493 for (t = 1; t < prob_->GetT(); ++t)
495 UpdateTimestep(t,
true,
false, 1, minimum_step_tolerance_, !iteration_count_, 1.);
498 for (t = prob_->GetT() - 2; t > 0; t--)
500 UpdateTimestep(t,
false,
true, (iteration_count_ ? 1 : 0), minimum_step_tolerance_,
false, 1.);
503 case LOCAL_GAUSS_NEWTON:
504 for (t = 1; t < prob_->GetT(); ++t)
506 UpdateTimestep(t,
true,
false, (iteration_count_ ? 5 : 1), minimum_step_tolerance_, !iteration_count_, 1.);
508 for (t = prob_->GetT() - 2; t > 0; t--)
510 UpdateTimestep(t,
false,
true, (iteration_count_ ? 5 : 0), minimum_step_tolerance_,
false, 1.);
513 case LOCAL_GAUSS_NEWTON_DAMPED:
514 for (t = 1; t < prob_->GetT(); ++t)
516 UpdateTimestepGaussNewton(t,
true,
false, (iteration_count_ ? 5 : 1),
517 minimum_step_tolerance_, 1.);
519 for (t = prob_->GetT() - 2; t > 0; t--)
521 UpdateTimestep(t,
false,
true, (iteration_count_ ? 5 : 0), minimum_step_tolerance_,
false, 1.);
528 for (t = 0; t < static_cast<int>(b.size()); ++t)
530 b_step_ = std::max((b_old[t] - b[t]).array().
abs().maxCoeff(), b_step_);
532 damping_reference_ = b;
534 cost_ = EvaluateTrajectory(b);
537 HIGHLIGHT(
"Iteration: " << iteration_count_ <<
", Sweep: " << sweep_ <<
", updates: " << update_count_ <<
", cost(ctrl/task/total): " << cost_control_.sum() <<
"/" << cost_task_.sum() <<
"/" << cost_ <<
" (dq=" << b_step_ <<
", damping=" << damping <<
")");
539 else if (debug_ && sweep_ == 0)
541 HIGHLIGHT(
"Iteration: " << iteration_count_ <<
", updates: " << update_count_ <<
", cost(ctrl/task/total): " << cost_control_.sum() <<
"/" << cost_task_.sum() <<
"/" << cost_ <<
" (dq=" << b_step_ <<
", damping=" << damping <<
")");
543 if (cost_ < 0)
return -1.0;
544 best_sweep_ = sweep_;
547 if (damping != 0.0) PerhapsUndoStep();
550 if (sweep_improved_cost_)
555 prob_->SetCostEvolution(iteration_count_, cost_);
561 void AICOSolver::RememberOldState()
576 cost_control_old_ = cost_control_;
577 cost_task_old_ = cost_task_;
578 best_sweep_old_ = best_sweep_;
579 b_step_old_ = b_step_;
582 void AICOSolver::PerhapsUndoStep()
584 if (cost_ > cost_old_)
586 sweep_improved_cost_ =
false;
601 damping_reference_ = b_old;
602 cost_control_ = cost_control_old_;
603 cost_task_ = cost_task_old_;
604 best_sweep_ = best_sweep_old_;
605 b_step_ = b_step_old_;
606 if (verbose_)
HIGHLIGHT(
"Reverting to previous line-search step (" << best_sweep_ <<
")");
610 sweep_improved_cost_ =
true;
static void AinvBSymPosDef(T1 &x, const T2 &A, const T3 &b)
Computes the solution to the linear problem for symmetric positive definite matrix A...
Solves motion planning problem using Approximate Inference Control method.
IMETHOD Vector diff(const Vector &p_w_a, const Vector &p_w_b, double dt=1)
#define REGISTER_MOTIONSOLVER_TYPE(TYPE, DERIV)
#define ROS_WARN_STREAM(args)
static void inverseSymPosDef(T1 &Ainv, const T2 &A)
Computes an inverse of a symmetric positive definite matrix.
INLINE Rall1d< T, V, S > abs(const Rall1d< T, V, S > &x)
Approximate Inference Control.
double GetDuration() const
std::shared_ptr< PlanningProblem > PlanningProblemPtr