| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * @file     LPSolver.cpp | 
					
						
							|  |  |  |  * @brief     | 
					
						
							| 
									
										
										
										
											2016-02-12 13:57:37 +08:00
										 |  |  |  * @author   Duy Nguyen Ta | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |  * @author   Ivan Dario Jimenez | 
					
						
							|  |  |  |  * @date     1/26/16 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <gtsam_unstable/linear/LPSolver.h>
 | 
					
						
							|  |  |  | #include <gtsam_unstable/linear/InfeasibleInitialValues.h>
 | 
					
						
							| 
									
										
										
										
											2016-01-30 01:07:14 +08:00
										 |  |  | #include <gtsam/linear/GaussianFactorGraph.h>
 | 
					
						
							| 
									
										
										
										
											2016-05-07 00:23:41 +08:00
										 |  |  | #include <gtsam_unstable/linear/LPInitSolver.h>
 | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | namespace gtsam { | 
					
						
							| 
									
										
										
										
											2016-02-16 02:53:22 +08:00
										 |  |  | LPSolver::LPSolver(const LP &lp) : | 
					
						
							| 
									
										
										
										
											2016-05-07 00:23:41 +08:00
										 |  |  |     lp_(lp), addedZeroPriorsIndex_() { | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |   // Push back factors that are the same in every iteration to the base graph.
 | 
					
						
							| 
									
										
										
										
											2016-01-30 01:07:14 +08:00
										 |  |  |   // Those include the equality constraints and zero priors for keys that are
 | 
					
						
							|  |  |  |   // not in the cost
 | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |   baseGraph_.push_back(lp_.equalities); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Variable index
 | 
					
						
							|  |  |  |   equalityVariableIndex_ = VariableIndex(lp_.equalities); | 
					
						
							|  |  |  |   inequalityVariableIndex_ = VariableIndex(lp_.inequalities); | 
					
						
							|  |  |  |   constrainedKeys_ = lp_.equalities.keys(); | 
					
						
							|  |  |  |   constrainedKeys_.merge(lp_.inequalities.keys()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | LPState LPSolver::iterate(const LPState &state) const { | 
					
						
							|  |  |  |   // Solve with the current working set
 | 
					
						
							| 
									
										
										
										
											2016-01-30 01:07:14 +08:00
										 |  |  |   // LP: project the objective neg. gradient to the constraint's null space
 | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |   // to find the direction to move
 | 
					
						
							| 
									
										
										
										
											2016-02-16 02:53:22 +08:00
										 |  |  |   VectorValues newValues = solveWithCurrentWorkingSet(state.values, | 
					
						
							|  |  |  |       state.workingSet); | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |   // If we CAN'T move further
 | 
					
						
							|  |  |  |   // LP: projection on the constraints' nullspace is zero: we are at a vertex
 | 
					
						
							|  |  |  |   if (newValues.equals(state.values, 1e-7)) { | 
					
						
							| 
									
										
										
										
											2016-01-30 01:07:14 +08:00
										 |  |  |     // Find and remove the bad inequality constraint by computing its lambda
 | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |     // Compute lambda from the dual graph
 | 
					
						
							| 
									
										
										
										
											2016-01-30 01:07:14 +08:00
										 |  |  |     // LP: project the objective's gradient onto each constraint gradient to
 | 
					
						
							|  |  |  |     // obtain the dual scaling factors
 | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |     //	is it true??
 | 
					
						
							| 
									
										
										
										
											2016-02-16 02:53:22 +08:00
										 |  |  |     GaussianFactorGraph::shared_ptr dualGraph = buildDualGraph(state.workingSet, | 
					
						
							|  |  |  |         newValues); | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |     VectorValues duals = dualGraph->optimize(); | 
					
						
							| 
									
										
										
										
											2016-01-30 01:07:14 +08:00
										 |  |  |     // LP: see which inequality constraint has wrong pulling direction, i.e., dual < 0
 | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |     int leavingFactor = identifyLeavingConstraint(state.workingSet, duals); | 
					
						
							|  |  |  |     // If all inequality constraints are satisfied: We have the solution!!
 | 
					
						
							|  |  |  |     if (leavingFactor < 0) { | 
					
						
							| 
									
										
										
										
											2016-01-30 01:07:14 +08:00
										 |  |  |       // TODO If we still have infeasible equality constraints: the problem is
 | 
					
						
							|  |  |  |       // over-constrained. No solution!
 | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |       // ...
 | 
					
						
							| 
									
										
										
										
											2016-02-16 02:53:22 +08:00
										 |  |  |       return LPState(newValues, duals, state.workingSet, true, | 
					
						
							|  |  |  |           state.iterations + 1); | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |     } else { | 
					
						
							|  |  |  |       // Inactivate the leaving constraint
 | 
					
						
							|  |  |  |       // LP: remove the bad ineq constraint out of the working set
 | 
					
						
							|  |  |  |       InequalityFactorGraph newWorkingSet = state.workingSet; | 
					
						
							|  |  |  |       newWorkingSet.at(leavingFactor)->inactivate(); | 
					
						
							| 
									
										
										
										
											2016-02-16 02:53:22 +08:00
										 |  |  |       return LPState(newValues, duals, newWorkingSet, false, | 
					
						
							|  |  |  |           state.iterations + 1); | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     // If we CAN make some progress, i.e. p_k != 0
 | 
					
						
							|  |  |  |     // Adapt stepsize if some inactive constraints complain about this move
 | 
					
						
							|  |  |  |     // LP: projection on nullspace is NOT zero:
 | 
					
						
							|  |  |  |     // 		find and put a blocking inactive constraint to the working set,
 | 
					
						
							|  |  |  |     // 		otherwise the problem is unbounded!!!
 | 
					
						
							|  |  |  |     double alpha; | 
					
						
							|  |  |  |     int factorIx; | 
					
						
							|  |  |  |     VectorValues p = newValues - state.values; | 
					
						
							| 
									
										
										
										
											2016-04-26 07:00:22 +08:00
										 |  |  | //    GTSAM_PRINT(p);
 | 
					
						
							| 
									
										
										
										
											2016-02-16 02:53:22 +08:00
										 |  |  |     boost::tie(alpha, factorIx) = // using 16.41
 | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |         computeStepSize(state.workingSet, state.values, p); | 
					
						
							|  |  |  |     // also add to the working set the one that complains the most
 | 
					
						
							|  |  |  |     InequalityFactorGraph newWorkingSet = state.workingSet; | 
					
						
							| 
									
										
										
										
											2016-02-16 02:53:22 +08:00
										 |  |  |     if (factorIx >= 0) | 
					
						
							|  |  |  |       newWorkingSet.at(factorIx)->activate(); | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |     // step!
 | 
					
						
							|  |  |  |     newValues = state.values + alpha * p; | 
					
						
							| 
									
										
										
										
											2016-02-16 02:53:22 +08:00
										 |  |  |     return LPState(newValues, state.duals, newWorkingSet, false, | 
					
						
							|  |  |  |         state.iterations + 1); | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | GaussianFactorGraph::shared_ptr LPSolver::createLeastSquareFactors( | 
					
						
							|  |  |  |     const LinearCost &cost, const VectorValues &xk) const { | 
					
						
							|  |  |  |   GaussianFactorGraph::shared_ptr graph(new GaussianFactorGraph()); | 
					
						
							|  |  |  |   for (LinearCost::const_iterator it = cost.begin(); it != cost.end(); ++it) { | 
					
						
							|  |  |  |     size_t dim = cost.getDim(it); | 
					
						
							|  |  |  |     Vector b = xk.at(*it) - cost.getA(it).transpose(); // b = xk-g
 | 
					
						
							| 
									
										
										
										
											2016-05-07 00:23:41 +08:00
										 |  |  |     graph->push_back(JacobianFactor(*it, Matrix::Identity(dim, dim), b)); | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-05-03 07:54:58 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-26 07:00:22 +08:00
										 |  |  |   KeySet allKeys = lp_.inequalities.keys(); | 
					
						
							|  |  |  |   allKeys.merge(lp_.equalities.keys()); | 
					
						
							|  |  |  |   allKeys.merge(KeySet(lp_.cost.keys())); | 
					
						
							| 
									
										
										
										
											2016-06-16 18:49:19 +08:00
										 |  |  |   // Add corresponding factors for all variables that are not explicitly in the
 | 
					
						
							|  |  |  |   // cost function. Gradients of the cost function wrt to these variables are 
 | 
					
						
							|  |  |  |   // zero (g=0), so b=xk
 | 
					
						
							| 
									
										
										
										
											2016-04-26 07:00:22 +08:00
										 |  |  |   if (cost.keys().size() != allKeys.size()) { | 
					
						
							|  |  |  |     KeySet difference; | 
					
						
							|  |  |  |     std::set_difference(allKeys.begin(), allKeys.end(), lp_.cost.begin(), | 
					
						
							| 
									
										
										
										
											2016-05-07 00:23:41 +08:00
										 |  |  |         lp_.cost.end(), std::inserter(difference, difference.end())); | 
					
						
							| 
									
										
										
										
											2016-04-26 07:00:22 +08:00
										 |  |  |     for (Key k : difference) { | 
					
						
							| 
									
										
										
										
											2016-06-16 18:49:19 +08:00
										 |  |  |       size_t dim = lp_.constrainedKeyDimMap().at(k); | 
					
						
							| 
									
										
										
										
											2016-06-14 10:58:36 +08:00
										 |  |  |       graph->push_back(JacobianFactor(k, Matrix::Identity(dim, dim), xk.at(k))); | 
					
						
							| 
									
										
										
										
											2016-04-26 07:00:22 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |   return graph; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-16 02:53:22 +08:00
										 |  |  | VectorValues LPSolver::solveWithCurrentWorkingSet(const VectorValues &xk, | 
					
						
							|  |  |  |     const InequalityFactorGraph &workingSet) const { | 
					
						
							|  |  |  |   GaussianFactorGraph workingGraph = baseGraph_; // || X - Xk + g ||^2
 | 
					
						
							| 
									
										
										
										
											2016-05-03 07:54:58 +08:00
										 |  |  | //  We remove the old zero priors from the base graph we are going to use to solve
 | 
					
						
							|  |  |  |   //This iteration's problem
 | 
					
						
							| 
									
										
										
										
											2016-05-06 23:24:15 +08:00
										 |  |  | //  for (size_t index : addedZeroPriorsIndex_) {
 | 
					
						
							|  |  |  | //    workingGraph.remove(index);
 | 
					
						
							|  |  |  | //  }
 | 
					
						
							| 
									
										
										
										
											2016-05-03 07:54:58 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |   workingGraph.push_back(*createLeastSquareFactors(lp_.cost, xk)); | 
					
						
							| 
									
										
										
										
											2016-02-16 02:53:22 +08:00
										 |  |  |   for (const LinearInequality::shared_ptr &factor : workingSet) { | 
					
						
							|  |  |  |     if (factor->active()) | 
					
						
							|  |  |  |       workingGraph.push_back(factor); | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |   } | 
					
						
							|  |  |  |   return workingGraph.optimize(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-16 02:53:22 +08:00
										 |  |  | boost::shared_ptr<JacobianFactor> LPSolver::createDualFactor(Key key, | 
					
						
							|  |  |  |     const InequalityFactorGraph &workingSet, const VectorValues &delta) const { | 
					
						
							| 
									
										
										
										
											2016-01-30 01:07:14 +08:00
										 |  |  |   // Transpose the A matrix of constrained factors to have the jacobian of the
 | 
					
						
							|  |  |  |   // dual key
 | 
					
						
							| 
									
										
										
										
											2016-06-14 10:58:36 +08:00
										 |  |  |   TermsContainer Aterms = collectDualJacobians < LinearEquality | 
					
						
							|  |  |  |       > (key, lp_.equalities, equalityVariableIndex_); | 
					
						
							|  |  |  |   TermsContainer AtermsInequalities = collectDualJacobians < LinearInequality | 
					
						
							|  |  |  |       > (key, workingSet, inequalityVariableIndex_); | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |   Aterms.insert(Aterms.end(), AtermsInequalities.begin(), | 
					
						
							| 
									
										
										
										
											2016-02-16 02:53:22 +08:00
										 |  |  |       AtermsInequalities.end()); | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Collect the gradients of unconstrained cost factors to the b vector
 | 
					
						
							|  |  |  |   if (Aterms.size() > 0) { | 
					
						
							| 
									
										
										
										
											2016-05-07 00:23:41 +08:00
										 |  |  |     Vector b = Vector::Zero(delta.at(key).size()); | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |     Factor::const_iterator it = lp_.cost.find(key); | 
					
						
							| 
									
										
										
										
											2016-02-16 02:53:22 +08:00
										 |  |  |     if (it != lp_.cost.end()) | 
					
						
							|  |  |  |       b = lp_.cost.getA(it).transpose(); | 
					
						
							| 
									
										
										
										
											2016-06-14 10:58:36 +08:00
										 |  |  |     return boost::make_shared < JacobianFactor > (Aterms, b); // compute the least-square approximation of dual variables
 | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |   } else { | 
					
						
							|  |  |  |     return boost::make_shared<JacobianFactor>(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | InequalityFactorGraph LPSolver::identifyActiveConstraints( | 
					
						
							|  |  |  |     const InequalityFactorGraph &inequalities, | 
					
						
							| 
									
										
										
										
											2016-01-30 01:07:14 +08:00
										 |  |  |     const VectorValues &initialValues, const VectorValues &duals) const { | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |   InequalityFactorGraph workingSet; | 
					
						
							| 
									
										
										
										
											2016-02-16 02:53:22 +08:00
										 |  |  |   for (const LinearInequality::shared_ptr &factor : inequalities) { | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |     LinearInequality::shared_ptr workingFactor(new LinearInequality(*factor)); | 
					
						
							| 
									
										
										
										
											2016-06-14 08:35:17 +08:00
										 |  |  |     GTSAM_PRINT(*workingFactor); | 
					
						
							|  |  |  |     GTSAM_PRINT(initialValues); | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |     double error = workingFactor->error(initialValues); | 
					
						
							|  |  |  |     // TODO: find a feasible initial point for LPSolver.
 | 
					
						
							|  |  |  |     // For now, we just throw an exception
 | 
					
						
							| 
									
										
										
										
											2016-02-16 02:53:22 +08:00
										 |  |  |     if (error > 0) | 
					
						
							|  |  |  |       throw InfeasibleInitialValues(); | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (fabs(error) < 1e-7) { | 
					
						
							|  |  |  |       workingFactor->activate(); | 
					
						
							| 
									
										
										
										
											2016-01-30 01:07:14 +08:00
										 |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |       workingFactor->inactivate(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     workingSet.push_back(workingFactor); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return workingSet; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::pair<VectorValues, VectorValues> LPSolver::optimize( | 
					
						
							| 
									
										
										
										
											2016-01-30 01:07:14 +08:00
										 |  |  |     const VectorValues &initialValues, const VectorValues &duals) const { | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |   { | 
					
						
							|  |  |  |     // Initialize workingSet from the feasible initialValues
 | 
					
						
							| 
									
										
										
										
											2016-02-16 02:53:22 +08:00
										 |  |  |     InequalityFactorGraph workingSet = identifyActiveConstraints( | 
					
						
							|  |  |  |         lp_.inequalities, initialValues, duals); | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |     LPState state(initialValues, duals, workingSet, false, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /// main loop of the solver
 | 
					
						
							| 
									
										
										
										
											2016-04-26 07:00:22 +08:00
										 |  |  |     while (!state.converged) { | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |       state = iterate(state); | 
					
						
							| 
									
										
										
										
											2016-04-26 07:00:22 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |     return make_pair(state.values, state.duals); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | boost::tuples::tuple<double, int> LPSolver::computeStepSize( | 
					
						
							| 
									
										
										
										
											2016-01-30 01:07:14 +08:00
										 |  |  |     const InequalityFactorGraph &workingSet, const VectorValues &xk, | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  |     const VectorValues &p) const { | 
					
						
							| 
									
										
										
										
											2016-02-16 02:53:22 +08:00
										 |  |  |   return ActiveSetSolver::computeStepSize(workingSet, xk, p, | 
					
						
							|  |  |  |       std::numeric_limits<double>::infinity()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | pair<VectorValues, VectorValues> LPSolver::optimize() const { | 
					
						
							| 
									
										
										
										
											2016-06-16 18:49:19 +08:00
										 |  |  |   LPInitSolver initSolver(lp_); | 
					
						
							| 
									
										
										
										
											2016-02-16 02:53:22 +08:00
										 |  |  |   VectorValues initValues = initSolver.solve(); | 
					
						
							|  |  |  |   return optimize(initValues); | 
					
						
							| 
									
										
										
										
											2016-01-26 22:34:05 +08:00
										 |  |  | } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-02-16 02:53:22 +08:00
										 |  |  | 
 |