levenberg_marquardt_solver.cpp
Go to the documentation of this file.
1 //
2 // Copyright 2018, University of Edinburgh
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to
6 // deal in the Software without restriction, including without limitation the
7 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 // sell copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 // SOFTWARE.
21 //
22 
24 
25 REGISTER_MOTIONSOLVER_TYPE("LevenbergMarquardtSolverSolver", exotica::LevenbergMarquardtSolver)
26 
27 namespace exotica
28 {
29 void LevenbergMarquardtSolver::SpecifyProblem(PlanningProblemPtr pointer)
30 {
31  if (pointer->type() != "exotica::UnconstrainedEndPoseProblem")
32  {
33  ThrowNamed("This LevenbergMarquardtSolver can't solve problem of type '" << pointer->type() << "'!");
34  }
35 
36  MotionSolver::SpecifyProblem(pointer);
37 
38  // generic problem
39  problem_ = pointer;
40 
41  // specific problem
42  prob_ = std::static_pointer_cast<UnconstrainedEndPoseProblem>(pointer);
43 
44  // check dimension of alpha
45  if (parameters_.Alpha.size() > 1 && parameters_.Alpha.size() != prob_->N)
46  {
47  ThrowNamed("Wrong alpha dimension: alpha(" << parameters_.Alpha.size() << ") != states(" << prob_->N << ")")
48  }
49 
50  // Allocate variables
51  JT_times_J_.resize(prob_->N, prob_->N);
52  q_.resize(prob_->N);
53  qd_.resize(prob_->N);
54  yd_.resize(prob_->cost.S.rows());
55  cost_jacobian_.resize(prob_->cost.S.rows(), prob_->N);
56 
57  if (parameters_.ScaleProblem == "none")
58  {
59  M_.setIdentity(prob_->N, prob_->N);
60  }
61  else if (parameters_.ScaleProblem == "Jacobian")
62  {
63  M_.setZero(prob_->N, prob_->N);
64  }
65  else
66  {
67  throw std::runtime_error("No ScaleProblem of type " + parameters_.ScaleProblem);
68  }
69 }
70 
71 void LevenbergMarquardtSolver::Solve(Eigen::MatrixXd& solution)
72 {
73  if (!prob_) ThrowNamed("Solver has not been initialized!");
74 
75  prob_->ResetCostEvolution(GetNumberOfMaxIterations() + 1);
76  Timer timer;
77 
78  q_ = prob_->ApplyStartState();
79  if (prob_->N != q_.rows()) ThrowNamed("Wrong size q0 size=" << q_.rows() << ", required size=" << prob_->N);
80 
81  solution.resize(1, prob_->N);
82 
83  lambda_ = parameters_.Damping; // Reset initial damping
84  for (int i = 0; i < GetNumberOfMaxIterations(); ++i)
85  {
86  prob_->Update(q_);
87 
88  yd_.noalias() = prob_->cost.S * prob_->cost.ydiff;
89 
90  // weighted sum of squares
91  error_prev_ = error_;
92  error_ = prob_->GetScalarCost();
93 
94  prob_->SetCostEvolution(i, error_);
95 
96  cost_jacobian_.noalias() = prob_->cost.S * prob_->cost.jacobian;
97 
98  // source: https://uk.mathworks.com/help/optim/ug/least-squares-model-fitting-algorithms.html, eq. 13
99  if (i > 0)
100  {
101  if (error_ < error_prev_)
102  {
103  // success, error decreased: decrease damping
104  lambda_ = lambda_ / 10.0;
105  }
106  else
107  {
108  // failure, error increased: increase damping
109  lambda_ = lambda_ * 10.0;
110  }
111  }
112 
113  if (debug_) HIGHLIGHT_NAMED("Levenberg-Marquardt", "damping: " << lambda_);
114 
115  if (parameters_.ScaleProblem == "Jacobian")
116  {
117  M_.diagonal().noalias() = (cost_jacobian_.transpose() * cost_jacobian_).diagonal();
118  }
119 
120  JT_times_J_.noalias() = cost_jacobian_.transpose() * cost_jacobian_;
121  JT_times_J_ += lambda_ * M_;
122 
123  llt_.compute(JT_times_J_);
124  if (llt_.info() != Eigen::Success)
125  {
126  ThrowPretty("Error during matrix decomposition of J^T * J (lambda=" << lambda_ << "):\n"
127  << JT_times_J_);
128  }
129  qd_.noalias() = cost_jacobian_.transpose() * yd_;
130  llt_.solveInPlace(qd_);
131 
132  if (parameters_.Alpha.size() == 1)
133  {
134  q_ -= qd_ * parameters_.Alpha[0];
135  }
136  else
137  {
138  q_ -= qd_.cwiseProduct(parameters_.Alpha);
139  }
140 
141  if (qd_.norm() < parameters_.Convergence)
142  {
143  if (debug_) HIGHLIGHT_NAMED("Levenberg-Marquardt", "Reached convergence (" << qd_.norm() << " < " << parameters_.Convergence << ")");
144  break;
145  }
146  }
147 
148  solution.row(0) = q_;
149  planning_time_ = timer.GetDuration();
150 }
151 } // namespace exotica
#define ThrowPretty(m)
#define REGISTER_MOTIONSOLVER_TYPE(TYPE, DERIV)
#define ThrowNamed(m)
#define HIGHLIGHT_NAMED(name, x)
double GetDuration() const
std::shared_ptr< PlanningProblem > PlanningProblemPtr


exotica_levenberg_marquardt_solver
Author(s):
autogenerated on Sat Apr 10 2021 02:35:37