You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Conception/drake-master/solvers/unrevised_lemke_solver.h

285 lines
12 KiB

#pragma once
#include <algorithm>
#include <fstream>
#include <limits>
#include <map>
#include <string>
#include <utility>
#include <vector>
#include <Eigen/SparseCore>
#include "drake/common/drake_copyable.h"
#include "drake/solvers/mathematical_program.h"
#include "drake/solvers/solver_base.h"
namespace drake {
namespace solvers {
/// Non-template class for UnrevisedLemkeSolver<T> constants.
class UnrevisedLemkeSolverId {
public:
DRAKE_NO_COPY_NO_MOVE_NO_ASSIGN(UnrevisedLemkeSolverId);
UnrevisedLemkeSolverId() = delete;
/// @return same as SolverInterface::solver_id()
static SolverId id();
};
/// A class for the Unrevised Implementation of Lemke Algorithm's for solving
/// Linear Complementarity Problems (LCPs). See MobyLcpSolver for a description
/// of LCPs. This code makes extensive use of the following document:
/// [Dai 2018] Dai, H. and Drumwright, E. Computing the Principal Pivoting
/// Transform for Solving Linear Complementarity Problems with Lemke's
/// Algorithm. (2018, located in doc/pivot_column.pdf).
template <class T>
class UnrevisedLemkeSolver final : public SolverBase {
public:
DRAKE_NO_COPY_NO_MOVE_NO_ASSIGN(UnrevisedLemkeSolver)
UnrevisedLemkeSolver();
~UnrevisedLemkeSolver() final;
/// Calculates the zero tolerance that the solver would compute if the user
/// does not specify a tolerance.
template <class U>
static U ComputeZeroTolerance(const MatrixX<U>& M) {
return M.rows() * M.template lpNorm<Eigen::Infinity>() *
(2 * std::numeric_limits<double>::epsilon());
}
/// Checks whether a given candidate solution to the LCP Mz + q = w, z ≥ 0,
/// w ≥ 0, zᵀw = 0 is satisfied to a given tolerance. If the tolerance is
/// non-positive, this method computes a reasonable tolerance using M.
static bool IsSolution(const MatrixX<T>& M, const VectorX<T>& q,
const VectorX<T>& z, T zero_tol = -1);
/// Lemke's Algorithm for solving LCPs in the matrix class E, which contains
/// all strictly semimonotone matrices, all P-matrices, and all strictly
/// copositive matrices. The solver can be applied with occasional success to
/// problems outside of its guaranteed matrix classes. Lemke's Algorithm is
/// described in [Cottle 1992], Section 4.4.
///
/// The solver will denote failure on return if it exceeds a problem-size
/// dependent number of iterations.
/// @param[in] M the LCP matrix.
/// @param[in] q the LCP vector.
/// @param[in,out] z the solution to the LCP on return (if the solver
/// succeeds). If the length of z is equal to the length of q,
/// the solver will attempt to use the basis from the last
/// solution. This strategy can prove exceptionally
/// fast if solutions differ little between successive calls.
/// If the solver fails (returns `false`),
/// `z` will be set to the zero vector on return.
/// @param[out] num_pivots the number of pivots used, on return.
/// @param[in] zero_tol The tolerance for testing against zero. If the
/// tolerance is negative (default) the solver will determine a
/// generally reasonable tolerance.
/// @returns `true` if the solver computes a solution to floating point
/// tolerances (i.e., if IsSolution() returns `true` on the problem)
/// and `false` otherwise.
/// @throws std::exception if M is not square or the dimensions of M do not
/// match the length of q.
///
/// * [Cottle 1992] R. Cottle, J.-S. Pang, and R. Stone. The Linear
/// Complementarity Problem. Academic Press, 1992.
bool SolveLcpLemke(const MatrixX<T>& M, const VectorX<T>& q, VectorX<T>* z,
int* num_pivots, const T& zero_tol = T(-1)) const;
/// @name Static versions of the instance methods with similar names.
//@{
static SolverId id();
static bool is_available();
static bool is_enabled();
static bool ProgramAttributesSatisfied(const MathematicalProgram&);
//@}
// A using-declaration adds these methods into our class's Doxygen.
using SolverBase::Solve;
private:
friend class UnrevisedLemkePrivateTests;
friend class UnrevisedLemkePrivateTests_SelectSubMatrixWithCovering_Test;
friend class UnrevisedLemkePrivateTests_SelectSubColumnWithCovering_Test;
friend class UnrevisedLemkePrivateTests_SelectSubVector_Test;
friend class UnrevisedLemkePrivateTests_SetSubVector_Test;
friend class UnrevisedLemkePrivateTests_ValidateIndices_Test;
friend class UnrevisedLemkePrivateTests_IsEachUnique_Test;
friend class UnrevisedLemkePrivateTests_LemkePivot_Test;
friend class UnrevisedLemkePrivateTests_ConstructLemkeSolution_Test;
friend class UnrevisedLemkePrivateTests_DetermineIndexSets_Test;
friend class UnrevisedLemkePrivateTests_FindBlockingIndex_Test;
friend class UnrevisedLemkePrivateTests_FindBlockingIndexCycling_Test;
friend class UnrevisedLemkePrivateTests_FindComplementIndex_Test;
struct LemkeIndexSets {
std::vector<int> alpha, alpha_prime;
std::vector<int> alpha_bar, alpha_bar_prime;
std::vector<int> beta, beta_prime;
std::vector<int> beta_bar, beta_bar_prime;
};
// A structure for holding a linear complementarity problem variable.
class LCPVariable {
public:
DRAKE_DEFAULT_COPY_AND_MOVE_AND_ASSIGN(LCPVariable)
LCPVariable() {}
LCPVariable(bool z, int index) : index_{index}, z_{z} {}
bool is_z() const { return z_; }
int index() const { return index_; }
// Gets the complement of this variable.
LCPVariable Complement() const {
DRAKE_ASSERT(index_ >= 0);
LCPVariable comp;
comp.z_ = !z_;
comp.index_ = index_;
return comp;
}
// Compares two LCP variables for equality.
bool operator==(const LCPVariable& v) const {
DRAKE_ASSERT(index_ >= 0 && v.index_ >= 0);
return (z_ == v.z_ && index_ == v.index_);
}
// Comparison operator for using LCPVariable as a key.
bool operator<(const LCPVariable& v) const {
DRAKE_ASSERT(index_ >= 0 && v.index_ >= 0);
if (index_ < v.index_) {
return true;
} else {
if (index_ > v.index_) {
return false;
} else {
// If here, the indices are equal. We will arbitrarily order w before
// z (alphabetical ordering).
return (!z_ && v.z_);
}
}
}
private:
int index_{-1}; // Index of the variable in the problem, 0...n. n
// indicates that the variable is artificial. -1
// indicates that the index is uninitialized.
bool z_{true}; // Is this a z variable or a w variable?
};
void DoSolve(const MathematicalProgram&, const Eigen::VectorXd&,
const SolverOptions&, MathematicalProgramResult*) const final;
static void SelectSubMatrixWithCovering(const MatrixX<T>& in,
const std::vector<int>& rows,
const std::vector<int>& cols,
MatrixX<T>* out);
static void SelectSubColumnWithCovering(const MatrixX<T>& in,
const std::vector<int>& rows,
int column, VectorX<T>* out);
static void SelectSubVector(const VectorX<T>& in,
const std::vector<int>& rows, VectorX<T>* out);
static void SetSubVector(const VectorX<T>& v_sub,
const std::vector<int>& indices, VectorX<T>* v);
static bool ValidateIndices(const std::vector<int>& row_indices,
const std::vector<int>& col_indices, int num_rows,
int num_cols);
static bool ValidateIndices(const std::vector<int>& row_indices,
int vector_size);
static bool IsEachUnique(const std::vector<LCPVariable>& vars);
bool LemkePivot(const MatrixX<T>& M, const VectorX<T>& q, int driving_index,
T zero_tol, VectorX<T>* M_bar_col, VectorX<T>* q_bar) const;
bool ConstructLemkeSolution(const MatrixX<T>& M, const VectorX<T>& q,
int artificial_index, T zero_tol,
VectorX<T>* z) const;
int FindComplementIndex(const LCPVariable& query) const;
void DetermineIndexSets() const;
bool FindBlockingIndex(const T& zero_tol, const VectorX<T>& matrix_col,
const VectorX<T>& ratios, int* blocking_index) const;
bool IsArtificial(const LCPVariable& v) const;
typedef std::vector<LCPVariable> LCPVariableVector;
// Structure for mapping a vector of independent variables to a selection
// index.
class LCPVariableVectorComparator {
public:
// This does a lexicographic comparison.
bool operator()(const LCPVariableVector& v1,
const LCPVariableVector& v2) const {
DRAKE_DEMAND(v1.size() == v2.size());
// Copy the vectors.
sorted1_ = v1;
sorted2_ = v2;
// Determine the variables in sorted order because we want to consider
// all permutations of a set of variables as the same.
std::sort(sorted1_.begin(), sorted1_.end());
std::sort(sorted2_.begin(), sorted2_.end());
// Now do the lexicographic comparison.
for (int i = 0; i < static_cast<int>(v1.size()); ++i) {
if (sorted1_[i] < sorted2_[i]) {
return true;
} else {
if (sorted2_[i] < sorted1_[i]) return false;
}
}
// If still here, they're equal.
return false;
}
private:
// Two temporary vectors for storing sorted versions of vectors.
mutable LCPVariableVector sorted1_, sorted2_;
};
// Note: the mutable variables below are used in place of local variables both
// to minimize heap allocations during the LCP solution process and to
// facilitate warmstarting.
// Temporary variable for determining index sets (i.e., α, α', α̅, α̅', etc.
// from [Dai 2018]). The first int of each pair stores the
// variable's own "internal" index and the second stores the index of the
// variable in the requisite array ("independent w", "dependent w",
// "independent z", and "dependent z") in [Dai 2018].
mutable std::vector<std::pair<int, int>> variable_and_array_indices_;
// Mapping from an LCP variable to the index of that variable in
// indep_variables.
mutable std::map<LCPVariable, int> indep_variables_indices_;
// Maps tuples of independent variables to the variable selected for pivoting
// when multiple pivoting choices are possible. If the LCP algorithm pivots
// such that a tuple of independent variables is detected that has been seen
// before, we would call this "cycling". We eliminate cycling by never
// selecting the same variable for pivoting twice *from a given pivot*.
mutable std::map<LCPVariableVector, int, LCPVariableVectorComparator>
selections_;
// These temporary matrices and vectors are members to facilitate minimizing
// memory allocations/deallocations. Changing their value between invocations
// of the LCP solver will not change the resulting computation.
mutable MatrixX<T> M_alpha_beta_, M_alpha_bar_beta_;
mutable VectorX<T> q_alpha_, q_alpha_bar_, q_prime_beta_prime_,
q_prime_alpha_bar_prime_, e_, M_prime_driving_beta_prime_,
M_prime_driving_alpha_bar_prime_, g_alpha_, g_alpha_bar_;
// The index sets for the Lemke Algorithm and is a member variable to
// permit warmstarting. Changing the index set between invocations of the LCP
// solver will not change the resulting computation.
mutable LemkeIndexSets index_sets_;
// The partitions of independent and dependent variables (denoted z' and w',
// respectively, in [Dai 2018]. These have been made member
// variables to permit warmstarting. Changing these sets between invocations
// of the LCP solver will not change the resulting computation.
mutable std::vector<LCPVariable> indep_variables_, dep_variables_;
};
} // end namespace solvers
} // end namespace drake