/* ---------------------------------------------------------------------------- * 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 #include 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 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 > 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 solutions */ std::pair 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 solutions */ std::pair 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 \f$\in\f$ [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. */ std::tuple 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 TermsContainer collectDualJacobians(Key key, const FactorGraph& 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 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