forked from RobotLocomotion/drake
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmoby_lcp_solver.h
314 lines (286 loc) · 15.2 KB
/
moby_lcp_solver.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
// Adapted with permission from code by Evan Drumwright
// (https://github.com/edrumwri).
#pragma once
#include <fstream>
#include <limits>
#include <string>
#include <vector>
#include <Eigen/SparseCore>
#include "drake/common/drake_copyable.h"
#include "drake/solvers/mathematical_program.h"
#include "drake/solvers/solver_base.h"
// TODO(jwnimmer-tri): This class should be renamed MobyLcpSolver to comply with
// style guide.
namespace drake {
namespace solvers {
/// Non-template class for MobyLcpSolver<T> constants.
class MobyLcpSolverId {
public:
DRAKE_NO_COPY_NO_MOVE_NO_ASSIGN(MobyLcpSolverId);
MobyLcpSolverId() = delete;
/// @return same as SolverInterface::solver_id()
static SolverId id();
};
/// A class for solving Linear Complementarity Problems (LCPs). Solving a LCP
/// requires finding a solution to the problem:<pre>
/// Mz + q = w
/// z ≥ 0
/// w ≥ 0
/// zᵀw = 0
/// </pre>
/// (where M ∈ ℝⁿˣⁿ and q ∈ ℝⁿ are problem inputs and z ∈ ℝⁿ and w ∈ ℝⁿ are
/// unknown vectors) or correctly reporting that such a solution does not exist.
/// In spite of their linear structure, solving LCPs is NP-Hard [Cottle 1992].
/// However, some LCPs are significantly easier to solve. For instance, it can
/// be seen that the LCP is solvable in worst-case polynomial time for the case
/// of symmetric positive-semi-definite M by formulating it as the following
/// convex quadratic program:<pre>
/// minimize: f(z) = zᵀw = zᵀ(Mz + q)
/// subject to: z ≥ 0
/// Mz + q ≥ 0
/// </pre>
/// Note that this quadratic program's (QP) objective function at the minimum
/// z cannot be less than zero, and the LCP is only solved if the objective
/// function at the minimum is equal to zero. Since the seminal result of
/// Karmarkar, it has been known that convex QPs are solvable in polynomial
/// time [Karmarkar 1984].
///
/// The difficulty of solving an LCP is characterized by the properties of its
/// particular matrix, namely the class of matrices it belongs to. Classes
/// include, for example, Q, Q₀, P, P₀, copositive, and Z matrices.
/// [Cottle 1992] and [Murty 1998] (see pp. 224-230 in the latter) describe
/// relevant matrix classes in more detail.
///
/// * [Cottle 1992] R. Cottle, J.-S. Pang, and R. Stone. The Linear
/// Complementarity Problem. Academic Press, 1992.
/// * [Karmarkar 1984] N. Karmarkar. A New Polynomial-Time Algorithm for
/// Linear Programming. Combinatorica, 4(4), pp. 373-395.
/// * [Murty 1988] K. Murty. Linear Complementarity, Linear and Nonlinear
/// Programming. Heldermann Verlag, 1988.
template <class T>
class MobyLCPSolver final : public SolverBase {
public:
DRAKE_NO_COPY_NO_MOVE_NO_ASSIGN(MobyLCPSolver)
MobyLCPSolver();
~MobyLCPSolver() final;
void SetLoggingEnabled(bool enabled);
/// 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>() *
(10 * std::numeric_limits<double>::epsilon());
}
/// Fast pivoting algorithm for LCPs of the form M = PAPᵀ, q = Pb, where
/// b ∈ ℝᵐ, P ∈ ℝⁿˣᵐ, and A ∈ ℝᵐˣᵐ (where A is positive definite). Therefore,
/// q is in the range of P and M is positive semi-definite. An LCP of this
/// form is also guaranteed to have a solution [Cottle 1992].
///
/// This particular implementation focuses on the case where the solution
/// requires few nonzero nonbasic variables, meaning that few z variables
/// need be nonzero to find a solution to Mz + q = w. This algorithm, which is
/// based off of Dantzig's Principle Pivoting Method I [Cottle 1992] is
/// described in [Drumwright 2015]. This algorithm is able to use "warm"
/// starting- a solution to a "nearby" LCP can be used to find the solution to
/// a given LCP more quickly.
///
/// Although this solver is theoretically guaranteed to give a solution to
/// the LCPs described above, accumulated floating point error from pivoting
/// operations could cause the solver to fail. Additionally, the solver can be
/// applied with some success to problems outside of its guaranteed matrix
/// class. For these reasons, the solver returns a flag indicating
/// success/failure.
/// @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 z's value as a starting
/// solution. If the solver fails (returns `false`), `z` will
/// be set to the zero vector.
/// @param[in] zero_tol The tolerance for testing against zero. If the
/// tolerance is negative (default) the solver will determine a
/// generally reasonable tolerance.
/// @throws std::logic_error if M is non-square or M's dimensions do not
/// equal q's dimension.
/// @returns `true` if the solver succeeded and `false` otherwise.
///
/// * [Cottle 1992] R. Cottle, J.-S. Pang, and R. Stone. The Linear
/// Complementarity Problem. Academic Press, 1992.
/// * [Drumwright 2015] E. Drumwright. Rapidly computable viscous friction
/// and no-slip rigid contact models. arXiv:
/// 1504.00719v1. 2015.
bool SolveLcpFast(const MatrixX<T>& M, const VectorX<T>& q,
VectorX<T>* z, const T& zero_tol = T(-1)) const;
/// Regularized version of the fast pivoting algorithm for LCPs of the form
/// M = PAPᵀ, q = Pb, where b ∈ ℝᵐ, P ∈ ℝⁿˣᵐ, and A ∈ ℝᵐˣᵐ (where A is
/// positive definite). Therefore, q is in the range of P and M is positive
/// semi-definite. Please see SolveLcpFast() for more documentation about the
/// particular algorithm.
///
/// This implementation wraps that algorithm with a Tikhonov-type
/// regularization approach. Specifically, this implementation repeatedly
/// attempts to solve the LCP:<pre>
/// (M + Iα)z + q = w
/// z ≥ 0
/// w ≥ 0
/// zᵀw = 0
/// </pre>
/// where I is the identity matrix and α ≪ 1, using geometrically increasing
/// values of α, until the LCP is solved. Cottle et al. describe how, for
/// sufficiently large α, the LCP will always be solvable [Cottle 1992], p.
/// 493.
///
/// Although this solver is theoretically guaranteed to give a solution to
/// the LCPs described above, accumulated floating point error from pivoting
/// operations could cause the solver to fail. Additionally, the solver can be
/// applied with some success to problems outside of its guaranteed matrix
/// class. For these reasons, the solver returns a flag indicating
/// success/failure.
/// @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 z's value as a starting
/// solution.
/// @param[in] min_exp The minimum exponent for computing α over [10ᵝ, 10ᵞ] in
/// steps of 10ᵟ, where β is the minimum exponent, γ is the
/// maximum exponent, and δ is the stepping exponent.
/// @param[in] step_exp The stepping exponent for computing α over [10ᵝ, 10ᵞ]
/// in steps of 10ᵟ, where β is the minimum exponent, γ is
/// the maximum exponent, and δ is the stepping exponent.
/// @param[in] max_exp The maximum exponent for computing α over [10ᵝ, 10ᵞ] in
/// steps of 10ᵟ, where β is the minimum exponent, γ is the
/// maximum exponent, and δ is the stepping exponent.
/// @param[in] zero_tol The tolerance for testing against zero. If the
/// tolerance is negative (default) the solver will determine a
/// generally reasonable tolerance.
/// @throws std::logic_error if M is non-square or M's dimensions do not
/// equal q's dimension.
/// @returns `true` if the solver succeeded and `false` if the solver did not
/// find a solution for α = 10ᵞ.
/// @sa SolveLcpFast()
///
/// * [Cottle, 1992] R. Cottle, J.-S. Pang, and R. Stone. The Linear
/// Complementarity Problem. Academic Press, 1992.
bool SolveLcpFastRegularized(const MatrixX<T>& M,
const VectorX<T>& q, VectorX<T>* z,
int min_exp = -20, unsigned step_exp = 4,
int max_exp = 20, const T& zero_tol = T(-1))
const;
/// 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. Lemke's Algorithm is described in [Cottle 1992],
/// Section 4.4. This implementation was adapted from the LEMKE Library
/// [LEMKE] for Matlab; this particular implementation fixes a bug
/// in LEMKE that could occur when multiple indices passed the minimum ratio
/// test.
///
/// Although this solver is theoretically guaranteed to give a solution to
/// the LCPs described above, accumulated floating point error from pivoting
/// operations could cause the solver to fail. Additionally, the solver can be
/// applied with some success to problems outside of its guaranteed matrix
/// classes. For these reasons, the solver returns a flag indicating
/// success/failure.
/// @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 z's value as a starting
/// solution. **This warmstarting is generally not
/// recommended**: it has a predisposition to lead to a failing
/// pivoting sequence. If the solver fails (returns `false`),
/// `z` will be set to the zero vector.
/// @param[in] zero_tol The tolerance for testing against zero. If the
/// tolerance is negative (default) the solver will determine a
/// generally reasonable tolerance.
/// @param[in] piv_tol The tolerance for testing against zero, specifically
/// used for the purpose of finding variables for pivoting. If the
/// tolerance is negative (default) the solver will determine a
/// generally reasonable tolerance.
/// @returns `true` if the solver **believes** it has computed a solution
/// (which it determines by the ability to "pivot out" the
/// "artificial" variable (see [Cottle 1992]) and `false` otherwise.
/// @warning The caller should verify that the algorithm has solved the LCP to
/// the desired tolerances on returns indicating success.
/// @throws std::logic_error 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.
/// * [LEMKE] P. Fackler and M. Miranda. LEMKE.
/// http://people.sc.fsu.edu/~burkardt/m\_src/lemke/lemke.m
bool SolveLcpLemke(const MatrixX<T>& M, const VectorX<T>& q,
VectorX<T>* z, const T& piv_tol = T(-1),
const T& zero_tol = T(-1)) const;
/// 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. Lemke's Algorithm is described in [Cottle 1992],
/// Section 4.4.
///
/// This implementation wraps that algorithm with a Tikhonov-type
/// regularization approach. Specifically, this implementation repeatedly
/// attempts to solve the LCP:<pre>
/// (M + Iα)z + q = w
/// z ≥ 0
/// w ≥ 0
/// zᵀw = 0
/// </pre>
/// where I is the identity matrix and α ≪ 1, using geometrically increasing
/// values of α, until the LCP is solved. See SolveLcpFastRegularized() for
/// description of the regularization process and the function parameters,
/// which are identical. See SolveLcpLemke() for a description of Lemke's
/// Algorithm. See SolveLcpFastRegularized() for a description of all
/// calling parameters other than @p z, which apply equally well to this
/// function.
/// @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 z's value as a starting
/// solution. **This warmstarting is generally not
/// recommended**: it has a predisposition to lead to a failing
/// pivoting sequence.
///
/// @sa SolveLcpFastRegularized()
/// @sa SolveLcpLemke()
///
/// * [Cottle 1992] R. Cottle, J.-S. Pang, and R. Stone. The Linear
/// Complementarity Problem. Academic Press, 1992.
bool SolveLcpLemkeRegularized(const MatrixX<T>& M,
const VectorX<T>& q, VectorX<T>* z,
int min_exp = -20, unsigned step_exp = 1,
int max_exp = 1, const T& piv_tol = T(-1),
const T& zero_tol = T(-1)) const;
/// Returns the number of pivoting operations made by the last LCP solve.
int get_num_pivots() const { return pivots_; }
/// Resets the number of pivoting operations made by the last LCP solver to
/// zero.
void reset_num_pivots() { pivots_ = 0; }
/// @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:
void DoSolve(const MathematicalProgram&, const Eigen::VectorXd&,
const SolverOptions&, MathematicalProgramResult*) const final;
void ClearIndexVectors() const;
template <typename MatrixType, typename Scalar>
void FinishLemkeSolution(const MatrixType& M, const VectorX<Scalar>& q,
const VectorX<Scalar>& x, VectorX<Scalar>* z) const;
// TODO(sammy-tri) replace this with a proper logging hookup
std::ostream& Log() const;
bool log_enabled_{false};
mutable std::ofstream null_stream_;
// Records the number of pivoting operations used during the last solve.
mutable unsigned pivots_{0};
// NOTE: The temporaries below are stored in the class to minimize
// allocations; all are marked 'mutable' as they do not affect the
// semantic const'ness of the class under its methods.
// Vectors which correspond to indices into other data.
mutable std::vector<unsigned> all_, tlist_, bas_, nonbas_, j_;
};
} // end namespace solvers
} // end namespace drake