GteLCPSolver.h
Go to the documentation of this file.
1 // David Eberly, Geometric Tools, Redmond WA 98052
2 // Copyright (c) 1998-2017
3 // Distributed under the Boost Software License, Version 1.0.
4 // http://www.boost.org/LICENSE_1_0.txt
5 // http://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
6 // File Version: 3.4.2 (2016/11/24)
7 
8 #pragma once
9 
10 #include <algorithm>
11 #include <array>
12 #include <vector>
13 
14 // A class for solving the Linear Complementarity Problem (LCP)
15 // w = q + M * z, w^T * z = 0, w >= 0, z >= 0. The vectors q, w, and z are
16 // n-tuples and the matrix M is n-by-n. The inputs to Solve(...) are q and M.
17 // The outputs are w and z, which are valid when the returned bool is true but
18 // are invalid when the returned bool is false.
19 //
20 // The comments at the end of this file explain what the preprocessor symbol
21 // means regarding the LCP solver implementation. If the algorithm fails to
22 // converge within the specified maximum number of iterations, consider
23 // increasing the number and calling Solve(...) again.
24 //#define GTE_REPORT_FAILED_TO_CONVERGE
25 
26 namespace gte
27 {
28 
29 // Support templates for number of dimensions known at compile time or known
30 // only at run time.
31 template <typename Real, int... Dimensions>
32 class LCPSolver {};
33 
34 
35 template <typename Real>
37 {
38 protected:
39  // Abstract base class construction. A virtual destructor is not provided
40  // because there are no required side effects when destroying objects from
41  // the derived classes. The member mMaxIterations is set by this call to
42  // the default value of n*n.
43  LCPSolverShared(int n);
44 
45 public:
46  // Theoretically, when there is a solution the algorithm must converge
47  // in a finite number of iterations. The number of iterations depends
48  // on the problem at hand, but we need to guard against an infinite loop
49  // by limiting the number. The implementation uses a maximum number of
50  // n*n (chosen arbitrarily). You can set the number yourself, perhaps
51  // when a call to Solve fails--increase the number of iterations and call
52  // Solve again.
53  inline void SetMaxIterations(int maxIterations);
54  inline int GetMaxIterations() const;
55 
56  // Access the actual number of iterations used in a call to Solve.
57  inline int GetNumIterations() const;
58 
59  enum Result
60  {
65  INVALID_INPUT
66  };
67 
68 protected:
69  // Bookkeeping of variables during the iterations of the solver. The name
70  // is either 'w' or 'z' and is used for human-readable debugging help.
71  // The 'index' is that for the original variables w[index] or z[index].
72  // The 'complementary' index is the location of the complementary variable
73  // in mVarBasic[] or in mVarNonbasic[]. The 'tuple' is a pointer to
74  // &w[0] or &z[0], the choice based on name of 'w' or 'z', and is used to
75  // fill in the solution values (the variables are permuted during the
76  // pivoting algorithm).
77  struct Variable
78  {
79  char name;
80  int index;
82  Real* tuple;
83  };
84 
85  // The augmented problem is w = q + M*z + z[n]*U = 0, where U is an
86  // n-tuple of 1-values. We manipulate the augmented matrix [M | U | p(t)]
87  // where p(t) is a column vector of polynomials of at most degree n. If
88  // p[r](t) is the polynomial for row r, then p[r](0) = q[r]. These are
89  // perturbations of q[r] designed so that the algorithm avoids degeneracies
90  // (a q-term becomes zero during the iterations). The basic variables are
91  // w[0] through w[n-1] and the nonbasic variables are z[0] through z[n].
92  // The returned z consists only of z[0] through z[n-1].
93 
94  // The derived classes ensure that the pointers point to the correct
95  // of elements for each array. The matrix M must be stored in row-major
96  // order.
97  bool Solve(Real const* q, Real const* M, Real* w, Real* z, Result* result);
98 
99  // Access mAugmented as a 2-dimensional array.
100  inline Real const& Augmented(int row, int col) const;
101  inline Real& Augmented(int row, int col);
102 
103  // Support for polynomials with n+1 coefficients and degree no larger
104  // than n.
105  void MakeZero(Real* poly);
106  void Copy(Real const* poly0, Real* poly1);
107  bool LessThan(Real const* poly0, Real const* poly1);
108  bool LessThanZero(Real const* poly);
109  void Multiply(Real const* poly, Real scalar, Real* product);
110 
114 
115  // These pointers are set by the derived-class constructors to arrays
116  // that have the correct number of elements. The arrays mVarBasic,
117  // mVarNonbasic, mQMin, mMinRatio, and mRatio each have n+1 elements.
118  // The mAugmented array has n rows and 2*(n+1) columns stored in
119  // row-major order in a 1-dimensional array. The array of pointers
120  // mPoly has n elements.
123  int mNumCols;
124  Real* mAugmented;
125  Real* mQMin;
126  Real* mMinRatio;
127  Real* mRatio;
128  Real** mPoly;
129 };
130 
131 
132 template <typename Real, int n>
133 class LCPSolver<Real, n> : public LCPSolverShared<Real>
134 {
135 public:
136  // Construction. The member mMaxIterations is set by this call to the
137  // default value of n*n.
138  LCPSolver();
139 
140  // If you want to know specifically why 'true' or 'false' was returned,
141  // pass the address of a Result variable as the last parameter.
142  bool Solve(std::array<Real, n> const& q, std::array<std::array<Real, n>, n> const& M,
143  std::array<Real, n>& w, std::array<Real, n>& z,
144  typename LCPSolverShared<Real>::Result* result = nullptr);
145 
146 private:
147  std::array<typename LCPSolverShared<Real>::Variable, n + 1> mArrayVarBasic;
148  std::array<typename LCPSolverShared<Real>::Variable, n + 1> mArrayVarNonbasic;
149  std::array<Real, 2 * (n + 1) * n> mArrayAugmented;
150  std::array<Real, n + 1> mArrayQMin;
151  std::array<Real, n + 1> mArrayMinRatio;
152  std::array<Real, n + 1> mArrayRatio;
153  std::array<Real*, n> mArrayPoly;
154 };
155 
156 
157 template <typename Real>
158 class LCPSolver<Real> : public LCPSolverShared<Real>
159 {
160 public:
161  // Construction. The member mMaxIterations is set by this call to the
162  // default value of n*n.
163  LCPSolver(int n);
164 
165  // The input q must have n elements and the input M must be an n-by-n
166  // matrix stored in row-major order. The outputs w and z have n elements.
167  // If you want to know specifically why 'true' or 'false' was returned,
168  // pass the address of a Result variable as the last parameter.
169  bool Solve(std::vector<Real> const& q, std::vector<Real> const& M,
170  std::vector<Real>& w, std::vector<Real>& z,
171  typename LCPSolverShared<Real>::Result* result = nullptr);
172 
173 private:
174  std::vector<typename LCPSolverShared<Real>::Variable> mVectorVarBasic;
175  std::vector<typename LCPSolverShared<Real>::Variable> mVectorVarNonbasic;
176  std::vector<Real> mVectorAugmented;
177  std::vector<Real> mVectorQMin;
178  std::vector<Real> mVectorMinRatio;
179  std::vector<Real> mVectorRatio;
180  std::vector<Real*> mVectorPoly;
181 };
182 
183 
184 template <typename Real>
186  :
187  mNumIterations(0),
188  mVarBasic(nullptr),
189  mVarNonbasic(nullptr),
190  mNumCols(0),
191  mAugmented(nullptr),
192  mQMin(nullptr),
193  mMinRatio(nullptr),
194  mRatio(nullptr),
195  mPoly(nullptr)
196 {
197  if (n > 0)
198  {
199  mDimension = n;
200  mMaxIterations = n * n;
201  }
202  else
203  {
204  mDimension = 0;
205  mMaxIterations = 0;
206  }
207 }
208 
209 template <typename Real>
210 inline void LCPSolverShared<Real>::SetMaxIterations(int maxIterations)
211 {
212  mMaxIterations = (maxIterations > 0 ? maxIterations : mDimension * mDimension);
213 }
214 
215 template <typename Real>
217 {
218  return mMaxIterations;
219 }
220 
221 template <typename Real>
223 {
224  return mNumIterations;
225 }
226 
227 template <typename Real>
228 bool LCPSolverShared<Real>::Solve(Real const* q, Real const* M,
229  Real* w, Real* z, Result* result)
230 {
231  // Perturb the q[r] constants to be polynomials of degree r+1 represented
232  // as an array of n+1 coefficients. The coefficient with index r+1 is 1
233  // and the coefficients with indices larger than r+1 are 0.
234  for (int r = 0; r < mDimension; ++r)
235  {
236  mPoly[r] = &Augmented(r, mDimension + 1);
237  MakeZero(mPoly[r]);
238  mPoly[r][0] = q[r];
239  mPoly[r][r + 1] = (Real)1;
240  }
241 
242  // Determine whether there is the trivial solution w = z = 0.
243  Copy(mPoly[0], mQMin);
244  int basic = 0;
245  for (int r = 1; r < mDimension; ++r)
246  {
247  if (LessThan(mPoly[r], mQMin))
248  {
249  Copy(mPoly[r], mQMin);
250  basic = r;
251  }
252  }
253 
254  if (!LessThanZero(mQMin))
255  {
256  for (int r = 0; r < mDimension; ++r)
257  {
258  w[r] = q[r];
259  z[r] = (Real)0;
260  }
261 
262  if (result)
263  {
264  *result = HAS_TRIVIAL_SOLUTION;
265  }
266  return true;
267  }
268 
269  // Initialize the remainder of the augmented matrix with M and U.
270  for (int r = 0; r < mDimension; ++r)
271  {
272  for (int c = 0; c < mDimension; ++c)
273  {
274  Augmented(r, c) = M[c + mDimension * r];
275  }
276  Augmented(r, mDimension) = (Real)1;
277  }
278 
279  // Keep track of when the variables enter and exit the dictionary,
280  // including where complementary variables are relocated.
281  for (int i = 0; i <= mDimension; ++i)
282  {
283  mVarBasic[i].name = 'w';
284  mVarBasic[i].index = i;
285  mVarBasic[i].complementary = i;
286  mVarBasic[i].tuple = w;
287  mVarNonbasic[i].name = 'z';
288  mVarNonbasic[i].index = i;
290  mVarNonbasic[i].tuple = z;
291  }
292 
293  // The augmented variable z[n] is the initial driving variable for
294  // pivoting. The equation 'basic' is the one to solve for z[n] and
295  // pivoting with w[basic]. The last column of M remains all 1-values
296  // for this initial step, so no algebraic computations occur for M[r][n].
297  int driving = mDimension;
298  for (int r = 0; r < mDimension; ++r)
299  {
300  if (r != basic)
301  {
302  for (int c = 0; c < mNumCols; ++c)
303  {
304  if (c != mDimension)
305  {
306  Augmented(r, c) -= Augmented(basic, c);
307  }
308  }
309  }
310  }
311 
312  for (int c = 0; c < mNumCols; ++c)
313  {
314  if (c != mDimension)
315  {
316  Augmented(basic, c) = -Augmented(basic, c);
317  }
318  }
319 
320  mNumIterations = 0;
321  for (int i = 0; i < mMaxIterations; ++i, ++mNumIterations)
322  {
323  // The basic variable of equation 'basic' exited the dictionary, so
324  // its complementary (nonbasic) variable must become the next driving
325  // variable in order for it to enter the dictionary.
326  int nextDriving = mVarBasic[basic].complementary;
327  mVarNonbasic[nextDriving].complementary = driving;
328  std::swap(mVarBasic[basic], mVarNonbasic[driving]);
329  if (mVarNonbasic[driving].index == mDimension)
330  {
331  // The algorithm has converged.
332  for (int r = 0; r < mDimension; ++r)
333  {
334  mVarBasic[r].tuple[mVarBasic[r].index] = mPoly[r][0];
335  }
336  for (int c = 0; c <= mDimension; ++c)
337  {
338  int index = mVarNonbasic[c].index;
339  if (index < mDimension)
340  {
341  mVarNonbasic[c].tuple[index] = (Real)0;
342  }
343  }
344  if (result)
345  {
346  *result = HAS_NONTRIVIAL_SOLUTION;
347  }
348  return true;
349  }
350 
351  // Determine the 'basic' equation for which the ratio -q[r]/M(r,driving)
352  // is minimized among all equations r with M(r,driving) < 0.
353  driving = nextDriving;
354  basic = -1;
355  for (int r = 0; r < mDimension; ++r)
356  {
357  if (Augmented(r, driving) < (Real)0)
358  {
359  Real factor = (Real)-1 / Augmented(r, driving);
360  Multiply(mPoly[r], factor, mRatio);
361  if (basic == -1 || LessThan(mRatio, mMinRatio))
362  {
364  basic = r;
365  }
366  }
367  }
368 
369  if (basic == -1)
370  {
371  // The coefficients of z[driving] in all the equations are
372  // nonnegative, so the z[driving] variable cannot leave the
373  // dictionary. There is no solution to the LCP.
374  for (int r = 0; r < mDimension; ++r)
375  {
376  w[r] = (Real)0;
377  z[r] = (Real)0;
378  }
379 
380  if (result)
381  {
382  *result = NO_SOLUTION;
383  }
384  return false;
385  }
386 
387  // Solve the basic equation so that z[driving] enters the dictionary
388  // and w[basic] exits the dictionary.
389  Real invDenom = (Real)1 / Augmented(basic, driving);
390  for (int r = 0; r < mDimension; ++r)
391  {
392  if (r != basic && Augmented(r, driving) != (Real)0)
393  {
394  Real multiplier = Augmented(r, driving) * invDenom;
395  for (int c = 0; c < mNumCols; ++c)
396  {
397  if (c != driving)
398  {
399  Augmented(r, c) -= Augmented(basic, c) * multiplier;
400  }
401  else
402  {
403  Augmented(r, driving) = multiplier;
404  }
405  }
406  }
407  }
408 
409  for (int c = 0; c < mNumCols; ++c)
410  {
411  if (c != driving)
412  {
413  Augmented(basic, c) = -Augmented(basic, c) * invDenom;
414  }
415  else
416  {
417  Augmented(basic, driving) = invDenom;
418  }
419  }
420  }
421 
422  // Numerical round-off errors can cause the Lemke algorithm not to
423  // converge. In particular, the code above has a test
424  // if (mAugmented[r][driving] < (Real)0) { ... }
425  // to determine the 'basic' equation with which to pivot. It is
426  // possible that theoretically mAugmented[r][driving]is zero but
427  // rounding errors cause it to be slightly negative. If theoretically
428  // all mAugmented[r][driving] >= 0, there is no solution to the LCP.
429  // With the rounding errors, if the algorithm fails to converge within
430  // the specified number of iterations, NO_SOLUTION is returned, which
431  // is hopefully the correct result. It is also possible that the rounding
432  // errors lead to a NO_SOLUTION (returned from inside the loop) when in
433  // fact there is a solution. [When the LCP solver is used by intersection
434  // testing algorithms, the hope is that misclassifications occur only
435  // when the two objects are nearly in tangential contact.]
436  //
437  // To determine whether the rounding errors are the problem, you can
438  // execute the query using exact arithmetic with the following type
439  // used for 'Real' (replacing 'float' or 'double') of
440  // BSRational<UIntegerAP32> Rational.
441  //
442  // That said, if the algorithm fails to converge and you believe that
443  // the rounding errors are not causing this, please file a bug report
444  // and provide the input data to the solver.
445 
446 #if defined(GTE_REPORT_FAILED_TO_CONVERGE)
447  LogInformation("The algorithm failed to converge.");
448 #endif
449 
450  if (result)
451  {
452  *result = FAILED_TO_CONVERGE;
453  }
454  return false;
455 }
456 
457 template <typename Real>
458 inline Real const& LCPSolverShared<Real>::Augmented(int row, int col) const
459 {
460  return mAugmented[col + mNumCols * row];
461 }
462 
463 template <typename Real>
464 inline Real& LCPSolverShared<Real>::Augmented(int row, int col)
465 {
466  return mAugmented[col + mNumCols * row];
467 }
468 
469 
470 template <typename Real>
472 {
473  for (int i = 0; i <= mDimension; ++i)
474  {
475  poly[i] = (Real)0;
476  }
477 }
478 
479 template <typename Real>
480 void LCPSolverShared<Real>::Copy(Real const* poly0, Real* poly1)
481 {
482  for (int i = 0; i <= mDimension; ++i)
483  {
484  poly1[i] = poly0[i];
485  }
486 }
487 
488 template <typename Real>
489 bool LCPSolverShared<Real>::LessThan(Real const* poly0, Real const* poly1)
490 {
491  for (int i = 0; i <= mDimension; ++i)
492  {
493  if (poly0[i] < poly1[i])
494  {
495  return true;
496  }
497 
498  if (poly0[i] > poly1[i])
499  {
500  return false;
501  }
502  }
503 
504  return false;
505 }
506 
507 template <typename Real>
509 {
510  for (int i = 0; i <= mDimension; ++i)
511  {
512  if (poly[i] < (Real)0)
513  {
514  return true;
515  }
516 
517  if (poly[i] > (Real)0)
518  {
519  return false;
520  }
521  }
522 
523  return false;
524 }
525 
526 template <typename Real>
527 void LCPSolverShared<Real>::Multiply(Real const* poly, Real scalar, Real* product)
528 {
529  for (int i = 0; i <= mDimension; ++i)
530  {
531  product[i] = poly[i] * scalar;
532  }
533 }
534 
535 
536 template <typename Real, int n>
538  :
539  LCPSolverShared<Real>(n)
540 {
541  this->mVarBasic = mArrayVarBasic.data();
542  this->mVarNonbasic = mArrayVarNonbasic.data();
543  this->mNumCols = 2 * (n + 1);
544  this->mAugmented = mArrayAugmented.data();
545  this->mQMin = mArrayQMin.data();
546  this->mMinRatio = mArrayMinRatio.data();
547  this->mRatio = mArrayRatio.data();
548  this->mPoly = mArrayPoly.data();
549 }
550 
551 template <typename Real, int n>
553  std::array<Real, n> const& q, std::array<std::array<Real, n>, n> const& M,
554  std::array<Real, n>& w, std::array<Real, n>& z,
556 {
557  return LCPSolverShared<Real>::Solve(q.data(), M.front().data(), w.data(), z.data(), result);
558 }
559 
560 
561 template <typename Real>
563  :
564  LCPSolverShared<Real>(n)
565 {
566  if (n > 0)
567  {
568  mVectorVarBasic.resize(n + 1);
569  mVectorVarNonbasic.resize(n + 1);
570  mVectorAugmented.resize(2 * (n + 1) * n);
571  mVectorQMin.resize(n + 1);
572  mVectorMinRatio.resize(n + 1);
573  mVectorRatio.resize(n + 1);
574  mVectorPoly.resize(n);
575 
576  this->mVarBasic = mVectorVarBasic.data();
577  this->mVarNonbasic = mVectorVarNonbasic.data();
578  this->mNumCols = 2 * (n + 1);
579  this->mAugmented = mVectorAugmented.data();
580  this->mQMin = mVectorQMin.data();
581  this->mMinRatio = mVectorMinRatio.data();
582  this->mRatio = mVectorRatio.data();
583  this->mPoly = mVectorPoly.data();
584  }
585 }
586 
587 template <typename Real>
589  std::vector<Real> const& q, std::vector<Real> const& M,
590  std::vector<Real>& w, std::vector<Real>& z,
592 {
593  if (this->mDimension > static_cast<int>(q.size())
594  || this->mDimension * this->mDimension > static_cast<int>(M.size()))
595  {
596  if (result)
597  {
598  *result = this->INVALID_INPUT;
599  }
600  return false;
601  }
602 
603  if (this->mDimension > static_cast<int>(w.size()))
604  {
605  w.resize(this->mDimension);
606  }
607 
608  if (this->mDimension > static_cast<int>(z.size()))
609  {
610  z.resize(this->mDimension);
611  }
612 
613  return LCPSolverShared<Real>::Solve(q.data(), M.data(), w.data(), z.data(), result);
614 }
615 
616 }
void SetMaxIterations(int maxIterations)
Definition: GteLCPSolver.h:210
GLdouble n
Definition: glcorearb.h:2003
std::vector< Real * > mVectorPoly
Definition: GteLCPSolver.h:180
Variable * mVarNonbasic
Definition: GteLCPSolver.h:122
void Multiply(Real const *poly, Real scalar, Real *product)
Definition: GteLCPSolver.h:527
std::array< Real, n+1 > mArrayRatio
Definition: GteLCPSolver.h:152
int GetMaxIterations() const
Definition: GteLCPSolver.h:216
#define LogInformation(message)
Definition: GteLogger.h:98
std::array< Real, 2 *(n+1)*n > mArrayAugmented
Definition: GteLCPSolver.h:149
GLubyte GLubyte GLubyte GLubyte w
Definition: glcorearb.h:852
std::array< Real, n+1 > mArrayQMin
Definition: GteLCPSolver.h:150
std::vector< typename LCPSolverShared< Real >::Variable > mVectorVarBasic
Definition: GteLCPSolver.h:174
const GLubyte * c
Definition: glext.h:11671
std::array< Real *, n > mArrayPoly
Definition: GteLCPSolver.h:153
GLenum GLenum GLsizei void * row
Definition: glext.h:2725
bool Solve(Real const *q, Real const *M, Real *w, Real *z, Result *result)
Definition: GteLCPSolver.h:228
int GetNumIterations() const
Definition: GteLCPSolver.h:222
Real const & Augmented(int row, int col) const
Definition: GteLCPSolver.h:458
std::array< Real, n+1 > mArrayMinRatio
Definition: GteLCPSolver.h:151
std::array< typename LCPSolverShared< Real >::Variable, n+1 > mArrayVarNonbasic
Definition: GteLCPSolver.h:148
void Copy(Real const *poly0, Real *poly1)
Definition: GteLCPSolver.h:480
std::vector< Real > mVectorMinRatio
Definition: GteLCPSolver.h:178
std::array< typename LCPSolverShared< Real >::Variable, n+1 > mArrayVarBasic
Definition: GteLCPSolver.h:147
GLdouble GLdouble GLdouble z
Definition: glcorearb.h:843
void MakeZero(Real *poly)
Definition: GteLCPSolver.h:471
GLenum array
Definition: glext.h:6669
GLboolean r
Definition: glcorearb.h:1217
std::vector< Real > mVectorRatio
Definition: GteLCPSolver.h:179
std::vector< Real > mVectorQMin
Definition: GteLCPSolver.h:177
GLuint index
Definition: glcorearb.h:781
std::vector< Real > mVectorAugmented
Definition: GteLCPSolver.h:176
GLdouble GLdouble GLdouble GLdouble q
Definition: glext.h:255
bool LessThanZero(Real const *poly)
Definition: GteLCPSolver.h:508
std::vector< typename LCPSolverShared< Real >::Variable > mVectorVarNonbasic
Definition: GteLCPSolver.h:175
GLuint64EXT * result
Definition: glext.h:10003
bool LessThan(Real const *poly0, Real const *poly1)
Definition: GteLCPSolver.h:489


geometric_tools_engine
Author(s): Yijiang Huang
autogenerated on Thu Jul 18 2019 04:00:00