1 /*
2  * Copyright (c) 2011-2021, The DART development contributors
3  * All rights reserved.
4  *
5  * The list of contributors can be found at:
6  *   https://github.com/dartsim/dart/blob/master/LICENSE
7  *
8  * This file is provided under the following "BSD-style" License:
9  *   Redistribution and use in source and binary forms, with or
10  *   without modification, are permitted provided that the following
11  *   conditions are met:
12  *   * Redistributions of source code must retain the above copyright
13  *     notice, this list of conditions and the following disclaimer.
14  *   * Redistributions in binary form must reproduce the above
15  *     copyright notice, this list of conditions and the following
16  *     disclaimer in the documentation and/or other materials provided
17  *     with the distribution.
18  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
19  *   CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
20  *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21  *   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  *   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23  *   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26  *   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  *   AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  *   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  *   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  *   POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include "dart/constraint/DantzigLCPSolver.hpp"
34 
35 #ifndef NDEBUG
36 #  include <iomanip>
37 #  include <iostream>
38 #endif
39 
40 #include "dart/external/odelcpsolver/lcp.h"
41 
42 #include "dart/common/Console.hpp"
43 #include "dart/constraint/ConstrainedGroup.hpp"
44 #include "dart/constraint/ConstraintBase.hpp"
45 #include "dart/lcpsolver/Lemke.hpp"
46 
47 namespace dart {
48 namespace constraint {
49 
50 //==============================================================================
DantzigLCPSolver(double _timestep)51 DantzigLCPSolver::DantzigLCPSolver(double _timestep) : LCPSolver(_timestep)
52 {
53   // Do nothing
54 }
55 
56 //==============================================================================
~DantzigLCPSolver()57 DantzigLCPSolver::~DantzigLCPSolver()
58 {
59   // Do nothing
60 }
61 
62 //==============================================================================
solve(ConstrainedGroup * _group)63 void DantzigLCPSolver::solve(ConstrainedGroup* _group)
64 {
65 
66   // Build LCP terms by aggregating them from constraints
67   std::size_t numConstraints = _group->getNumConstraints();
68   std::size_t n = _group->getTotalDimension();
69 
70   // If there is no constraint, then just return.
71   if (0u == n)
72     return;
73 
74   int nSkip = dPAD(n);
75   double* A = new double[n * nSkip];
76   double* x = new double[n];
77   double* b = new double[n];
78   double* w = new double[n];
79   double* lo = new double[n];
80   double* hi = new double[n];
81   int* findex = new int[n];
82 
83   // Set w to 0 and findex to -1
84 #ifndef NDEBUG
85   std::memset(A, 0.0, n * nSkip * sizeof(double));
86 #endif
87   std::memset(w, 0.0, n * sizeof(double));
88   std::memset(findex, -1, n * sizeof(int));
89 
90   // Compute offset indices
91   std::size_t* offset = new std::size_t[n];
92   offset[0] = 0;
93   //  std::cout << "offset[" << 0 << "]: " << offset[0] << std::endl;
94   for (std::size_t i = 1; i < numConstraints; ++i)
95   {
96     const ConstraintBasePtr& constraint = _group->getConstraint(i - 1);
97     assert(constraint->getDimension() > 0);
98     offset[i] = offset[i - 1] + constraint->getDimension();
99     //    std::cout << "offset[" << i << "]: " << offset[i] << std::endl;
100   }
101 
102   // For each constraint
103   ConstraintInfo constInfo;
104   constInfo.invTimeStep = 1.0 / mTimeStep;
105   for (std::size_t i = 0; i < numConstraints; ++i)
106   {
107     const ConstraintBasePtr& constraint = _group->getConstraint(i);
108 
109     constInfo.x = x + offset[i];
110     constInfo.lo = lo + offset[i];
111     constInfo.hi = hi + offset[i];
112     constInfo.b = b + offset[i];
113     constInfo.findex = findex + offset[i];
114     constInfo.w = w + offset[i];
115 
116     // Fill vectors: lo, hi, b, w
117     constraint->getInformation(&constInfo);
118 
119     // Fill a matrix by impulse tests: A
120     constraint->excite();
121     for (std::size_t j = 0; j < constraint->getDimension(); ++j)
122     {
123       // Adjust findex for global index
124       if (findex[offset[i] + j] >= 0)
125         findex[offset[i] + j] += offset[i];
126 
127       // Apply impulse for mipulse test
128       constraint->applyUnitImpulse(j);
129 
130       // Fill upper triangle blocks of A matrix
131       int index = nSkip * (offset[i] + j) + offset[i];
132       constraint->getVelocityChange(A + index, true);
133       for (std::size_t k = i + 1; k < numConstraints; ++k)
134       {
135         index = nSkip * (offset[i] + j) + offset[k];
136         _group->getConstraint(k)->getVelocityChange(A + index, false);
137       }
138 
139       // Filling symmetric part of A matrix
140       for (std::size_t k = 0; k < i; ++k)
141       {
142         for (std::size_t l = 0; l < _group->getConstraint(k)->getDimension();
143              ++l)
144         {
145           int index1 = nSkip * (offset[i] + j) + offset[k] + l;
146           int index2 = nSkip * (offset[k] + l) + offset[i] + j;
147 
148           A[index1] = A[index2];
149         }
150       }
151     }
152 
153     assert(isSymmetric(
154         n, A, offset[i], offset[i] + constraint->getDimension() - 1));
155 
156     constraint->unexcite();
157   }
158 
159   assert(isSymmetric(n, A));
160 
161   // Print LCP formulation
162   //  dtdbg << "Before solve:" << std::endl;
163   //  print(n, A, x, lo, hi, b, w, findex);
164   //  std::cout << std::endl;
165 
166   // Solve LCP using ODE's Dantzig algorithm
167   external::ode::dSolveLCP(n, A, x, b, w, 0, lo, hi, findex);
168 
169   // Print LCP formulation
170   //  dtdbg << "After solve:" << std::endl;
171   //  print(n, A, x, lo, hi, b, w, findex);
172   //  std::cout << std::endl;
173 
174   // Apply constraint impulses
175   for (std::size_t i = 0; i < numConstraints; ++i)
176   {
177     const ConstraintBasePtr& constraint = _group->getConstraint(i);
178     constraint->applyImpulse(x + offset[i]);
179     constraint->excite();
180   }
181 
182   delete[] offset;
183 
184   delete[] A;
185   delete[] x;
186   delete[] b;
187   delete[] w;
188   delete[] lo;
189   delete[] hi;
190   delete[] findex;
191 }
192 
193 //==============================================================================
194 #ifndef NDEBUG
isSymmetric(std::size_t _n,double * _A)195 bool DantzigLCPSolver::isSymmetric(std::size_t _n, double* _A)
196 {
197   std::size_t nSkip = dPAD(_n);
198   for (std::size_t i = 0; i < _n; ++i)
199   {
200     for (std::size_t j = 0; j < _n; ++j)
201     {
202       if (std::abs(_A[nSkip * i + j] - _A[nSkip * j + i]) > 1e-6)
203       {
204         std::cout << "A: " << std::endl;
205         for (std::size_t k = 0; k < _n; ++k)
206         {
207           for (std::size_t l = 0; l < nSkip; ++l)
208           {
209             std::cout << std::setprecision(4) << _A[k * nSkip + l] << " ";
210           }
211           std::cout << std::endl;
212         }
213 
214         std::cout << "A(" << i << ", " << j << "): " << _A[nSkip * i + j]
215                   << std::endl;
216         std::cout << "A(" << j << ", " << i << "): " << _A[nSkip * j + i]
217                   << std::endl;
218         return false;
219       }
220     }
221   }
222 
223   return true;
224 }
225 
226 //==============================================================================
isSymmetric(std::size_t _n,double * _A,std::size_t _begin,std::size_t _end)227 bool DantzigLCPSolver::isSymmetric(
228     std::size_t _n, double* _A, std::size_t _begin, std::size_t _end)
229 {
230   std::size_t nSkip = dPAD(_n);
231   for (std::size_t i = _begin; i <= _end; ++i)
232   {
233     for (std::size_t j = _begin; j <= _end; ++j)
234     {
235       if (std::abs(_A[nSkip * i + j] - _A[nSkip * j + i]) > 1e-6)
236       {
237         std::cout << "A: " << std::endl;
238         for (std::size_t k = 0; k < _n; ++k)
239         {
240           for (std::size_t l = 0; l < nSkip; ++l)
241           {
242             std::cout << std::setprecision(4) << _A[k * nSkip + l] << " ";
243           }
244           std::cout << std::endl;
245         }
246 
247         std::cout << "A(" << i << ", " << j << "): " << _A[nSkip * i + j]
248                   << std::endl;
249         std::cout << "A(" << j << ", " << i << "): " << _A[nSkip * j + i]
250                   << std::endl;
251         return false;
252       }
253     }
254   }
255 
256   return true;
257 }
258 
259 //==============================================================================
print(std::size_t _n,double * _A,double * _x,double *,double *,double * b,double * w,int * findex)260 void DantzigLCPSolver::print(
261     std::size_t _n,
262     double* _A,
263     double* _x,
264     double* /*lo*/,
265     double* /*hi*/,
266     double* b,
267     double* w,
268     int* findex)
269 {
270   std::size_t nSkip = dPAD(_n);
271   std::cout << "A: " << std::endl;
272   for (std::size_t i = 0; i < _n; ++i)
273   {
274     for (std::size_t j = 0; j < nSkip; ++j)
275     {
276       std::cout << std::setprecision(4) << _A[i * nSkip + j] << " ";
277     }
278     std::cout << std::endl;
279   }
280 
281   std::cout << "b: ";
282   for (std::size_t i = 0; i < _n; ++i)
283   {
284     std::cout << std::setprecision(4) << b[i] << " ";
285   }
286   std::cout << std::endl;
287 
288   std::cout << "w: ";
289   for (std::size_t i = 0; i < _n; ++i)
290   {
291     std::cout << w[i] << " ";
292   }
293   std::cout << std::endl;
294 
295   std::cout << "x: ";
296   for (std::size_t i = 0; i < _n; ++i)
297   {
298     std::cout << _x[i] << " ";
299   }
300   std::cout << std::endl;
301 
302   //  std::cout << "lb: ";
303   //  for (int i = 0; i < dim; ++i)
304   //  {
305   //    std::cout << lb[i] << " ";
306   //  }
307   //  std::cout << std::endl;
308 
309   //  std::cout << "ub: ";
310   //  for (int i = 0; i < dim; ++i)
311   //  {
312   //    std::cout << ub[i] << " ";
313   //  }
314   //  std::cout << std::endl;
315 
316   std::cout << "frictionIndex: ";
317   for (std::size_t i = 0; i < _n; ++i)
318   {
319     std::cout << findex[i] << " ";
320   }
321   std::cout << std::endl;
322 
323   double* Ax = new double[_n];
324 
325   for (std::size_t i = 0; i < _n; ++i)
326   {
327     Ax[i] = 0.0;
328   }
329 
330   for (std::size_t i = 0; i < _n; ++i)
331   {
332     for (std::size_t j = 0; j < _n; ++j)
333     {
334       Ax[i] += _A[i * nSkip + j] * _x[j];
335     }
336   }
337 
338   std::cout << "Ax   : ";
339   for (std::size_t i = 0; i < _n; ++i)
340   {
341     std::cout << Ax[i] << " ";
342   }
343   std::cout << std::endl;
344 
345   std::cout << "b + w: ";
346   for (std::size_t i = 0; i < _n; ++i)
347   {
348     std::cout << b[i] + w[i] << " ";
349   }
350   std::cout << std::endl;
351 
352   delete[] Ax;
353 }
354 #endif
355 
356 } // namespace constraint
357 } // namespace dart
358