123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204 |
- /* ----------------------------------------------------------------------------
- * GTSAM Copyright 2010, Georgia Tech Research Corporation,
- * Atlanta, Georgia 30332-0415
- * All Rights Reserved
- * Authors: Frank Dellaert, et al. (see THANKS for the full author list)
- * See LICENSE for the license information
- * -------------------------------------------------------------------------- */
- /**
- * @file ActiveSetSolver.h
- * @brief Active set method for solving LP, QP problems
- * @author Ivan Dario Jimenez
- * @author Duy Nguyen Ta
- * @date 1/25/16
- */
- #pragma once
- #include <gtsam/linear/GaussianFactorGraph.h>
- #include <gtsam_unstable/linear/InequalityFactorGraph.h>
- #include <boost/range/adaptor/map.hpp>
- namespace gtsam {
- /**
- * This class implements the active set algorithm for solving convex
- * Programming problems.
- *
- * @tparam PROBLEM Type of the problem to solve, e.g. LP (linear program) or
- * QP (quadratic program).
- * @tparam POLICY specific detail policy tailored for the particular program
- * @tparam INITSOLVER Solver for an initial feasible solution of this problem.
- */
- template <class PROBLEM, class POLICY, class INITSOLVER>
- class ActiveSetSolver {
- public:
- /// This struct contains the state information for a single iteration
- struct State {
- VectorValues values; //!< current best values at each step
- VectorValues duals; //!< current values of dual variables at each step
- InequalityFactorGraph workingSet; /*!< keep track of current active/inactive
- inequality constraints */
- bool converged; //!< True if the algorithm has converged to a solution
- size_t iterations; /*!< Number of iterations. Incremented at the end of
- each iteration. */
- /// Default constructor
- State()
- : values(), duals(), workingSet(), converged(false), iterations(0) {}
- /// Constructor with initial values
- State(const VectorValues& initialValues, const VectorValues& initialDuals,
- const InequalityFactorGraph& initialWorkingSet, bool _converged,
- size_t _iterations)
- : values(initialValues),
- duals(initialDuals),
- workingSet(initialWorkingSet),
- converged(_converged),
- iterations(_iterations) {}
- };
- protected:
- const PROBLEM& problem_; //!< the particular [convex] problem to solve
- VariableIndex equalityVariableIndex_,
- inequalityVariableIndex_; /*!< index to corresponding factors to build
- dual graphs */
- KeySet constrainedKeys_; /*!< all constrained keys, will become factors in
- dual graphs */
- /// Vector of key matrix pairs. Matrices are usually the A term for a factor.
- typedef std::vector<std::pair<Key, Matrix> > TermsContainer;
- public:
- /// Constructor
- ActiveSetSolver(const PROBLEM& problem) : problem_(problem) {
- equalityVariableIndex_ = VariableIndex(problem_.equalities);
- inequalityVariableIndex_ = VariableIndex(problem_.inequalities);
- constrainedKeys_ = problem_.equalities.keys();
- constrainedKeys_.merge(problem_.inequalities.keys());
- }
- /**
- * Optimize with provided initial values
- * For this version, it is the responsibility of the caller to provide
- * a feasible initial value, otherwise, an exception will be thrown.
- * @return a pair of <primal, dual> solutions
- */
- std::pair<VectorValues, VectorValues> optimize(
- const VectorValues& initialValues,
- const VectorValues& duals = VectorValues(),
- bool useWarmStart = false) const;
- /**
- * For this version the caller will not have to provide an initial value
- * @return a pair of <primal, dual> solutions
- */
- std::pair<VectorValues, VectorValues> optimize() const;
- protected:
- /**
- * Compute minimum step size alpha to move from the current point @p xk to the
- * next feasible point along a direction @p p: x' = xk + alpha*p,
- * where alpha \in [0,maxAlpha].
- *
- * For QP, maxAlpha = 1. For LP: maxAlpha = Inf.
- *
- * @return a tuple of (minAlpha, closestFactorIndex) where closestFactorIndex
- * is the closest inactive inequality constraint that blocks xk to move
- * further and that has the minimum alpha, or (-1, maxAlpha) if there is no
- * such inactive blocking constraint.
- *
- * If there is a blocking constraint, the closest one will be added to the
- * working set and become active in the next iteration.
- */
- boost::tuple<double, int> computeStepSize(
- const InequalityFactorGraph& workingSet, const VectorValues& xk,
- const VectorValues& p, const double& maxAlpha) const;
- /**
- * Finds the active constraints in the given factor graph and returns the
- * Dual Jacobians used to build a dual factor graph.
- */
- template<typename FACTOR>
- TermsContainer collectDualJacobians(Key key, const FactorGraph<FACTOR>& graph,
- const VariableIndex& variableIndex) const {
- /*
- * Iterates through each factor in the factor graph and checks
- * whether it's active. If the factor is active it reutrns the A
- * term of the factor.
- */
- TermsContainer Aterms;
- if (variableIndex.find(key) != variableIndex.end()) {
- for (size_t factorIx : variableIndex[key]) {
- typename FACTOR::shared_ptr factor = graph.at(factorIx);
- if (!factor->active())
- continue;
- Matrix Ai = factor->getA(factor->find(key)).transpose();
- Aterms.push_back(std::make_pair(factor->dualKey(), Ai));
- }
- }
- return Aterms;
- }
- /**
- * Creates a dual factor from the current workingSet and the key of the
- * the variable used to created the dual factor.
- */
- JacobianFactor::shared_ptr createDualFactor(
- Key key, const InequalityFactorGraph& workingSet,
- const VectorValues& delta) const;
- public: /// Just for testing...
- /// Builds a dual graph from the current working set.
- GaussianFactorGraph buildDualGraph(const InequalityFactorGraph &workingSet,
- const VectorValues &delta) const;
- /**
- * Build a working graph of cost, equality and active inequality constraints
- * to solve at each iteration.
- * @param workingSet the collection of all cost and constrained factors
- * @param xk current solution, used to build a special quadratic cost in LP
- * @return a new better solution
- */
- GaussianFactorGraph buildWorkingGraph(
- const InequalityFactorGraph& workingSet,
- const VectorValues& xk = VectorValues()) const;
- /// Iterate 1 step, return a new state with a new workingSet and values
- State iterate(const State& state) const;
- /// Identify active constraints based on initial values.
- InequalityFactorGraph identifyActiveConstraints(
- const InequalityFactorGraph& inequalities,
- const VectorValues& initialValues,
- const VectorValues& duals = VectorValues(),
- bool useWarmStart = false) const;
- /// Identifies active constraints that shouldn't be active anymore.
- int identifyLeavingConstraint(const InequalityFactorGraph& workingSet,
- const VectorValues& lambdas) const;
- };
- /**
- * Find the max key in a problem.
- * Useful to determine unique keys for additional slack variables
- */
- template <class PROBLEM>
- Key maxKey(const PROBLEM& problem) {
- auto keys = problem.cost.keys();
- Key maxKey = *std::max_element(keys.begin(), keys.end());
- if (!problem.equalities.empty())
- maxKey = std::max(maxKey, *problem.equalities.keys().rbegin());
- if (!problem.inequalities.empty())
- maxKey = std::max(maxKey, *problem.inequalities.keys().rbegin());
- return maxKey;
- }
- } // namespace gtsam
- #include <gtsam_unstable/linear/ActiveSetSolver-inl.h>
|