372 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			372 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
|  | /* Copyright (C) 2004-2013 MBSim Development Team
 | ||
|  | 
 | ||
|  | Code was converted for the Bullet Continuous Collision Detection and Physics Library | ||
|  | 
 | ||
|  | This software is provided 'as-is', without any express or implied warranty. | ||
|  | In no event will the authors be held liable for any damages arising from the use of this software. | ||
|  | Permission is granted to anyone to use this software for any purpose,  | ||
|  | including commercial applications, and to alter it and redistribute it freely,  | ||
|  | subject to the following restrictions: | ||
|  | 
 | ||
|  | 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. | ||
|  | 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. | ||
|  | 3. This notice may not be removed or altered from any source distribution. | ||
|  | */ | ||
|  | 
 | ||
|  | //The original version is here
 | ||
|  | //https://code.google.com/p/mbsim-env/source/browse/trunk/kernel/mbsim/numerics/linear_complementarity_problem/lemke_algorithm.cc
 | ||
|  | //This file is re-distributed under the ZLib license, with permission of the original author
 | ||
|  | //Math library was replaced from fmatvec to a the file src/LinearMath/btMatrixX.h
 | ||
|  | //STL/std::vector replaced by btAlignedObjectArray
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | #include "btLemkeAlgorithm.h"
 | ||
|  | 
 | ||
|  | #undef BT_DEBUG_OSTREAM
 | ||
|  | #ifdef BT_DEBUG_OSTREAM
 | ||
|  | using namespace std; | ||
|  | #endif //BT_DEBUG_OSTREAM
 | ||
|  | 
 | ||
|  | btScalar btMachEps() | ||
|  | { | ||
|  | 	static bool calculated=false; | ||
|  | 	static btScalar machEps = btScalar(1.); | ||
|  | 	if (!calculated) | ||
|  | 	{ | ||
|  | 		do { | ||
|  | 			machEps /= btScalar(2.0); | ||
|  | 			// If next epsilon yields 1, then break, because current
 | ||
|  | 			// epsilon is the machine epsilon.
 | ||
|  | 		} | ||
|  | 		while ((btScalar)(1.0 + (machEps/btScalar(2.0))) != btScalar(1.0)); | ||
|  | //		printf( "\nCalculated Machine epsilon: %G\n", machEps );
 | ||
|  | 		calculated=true; | ||
|  | 	} | ||
|  | 	return machEps; | ||
|  | } | ||
|  | 
 | ||
|  | btScalar btEpsRoot() { | ||
|  | 	 | ||
|  | 	static btScalar epsroot = 0.; | ||
|  | 	static bool alreadyCalculated = false; | ||
|  | 	 | ||
|  | 	if (!alreadyCalculated) { | ||
|  | 		epsroot = btSqrt(btMachEps()); | ||
|  | 		alreadyCalculated = true; | ||
|  | 	} | ||
|  | 	return epsroot; | ||
|  | } | ||
|  | 
 | ||
|  | 	  | ||
|  | 
 | ||
|  |   btVectorXu btLemkeAlgorithm::solve(unsigned int maxloops /* = 0*/) | ||
|  | { | ||
|  |    | ||
|  |      | ||
|  |     steps = 0; | ||
|  | 
 | ||
|  |     int dim = m_q.size(); | ||
|  | #ifdef BT_DEBUG_OSTREAM
 | ||
|  |     if(DEBUGLEVEL >= 1) { | ||
|  |       cout << "Dimension = " << dim << endl; | ||
|  |     } | ||
|  | #endif //BT_DEBUG_OSTREAM
 | ||
|  | 
 | ||
|  | 	btVectorXu solutionVector(2 * dim); | ||
|  | 	solutionVector.setZero(); | ||
|  | 	   | ||
|  | 	  //, INIT, 0.);
 | ||
|  | 
 | ||
|  | 	btMatrixXu ident(dim, dim); | ||
|  | 	ident.setIdentity(); | ||
|  | #ifdef BT_DEBUG_OSTREAM
 | ||
|  | 	cout << m_M << std::endl; | ||
|  | #endif
 | ||
|  | 
 | ||
|  | 	btMatrixXu mNeg = m_M.negative(); | ||
|  | 	   | ||
|  |     btMatrixXu A(dim, 2 * dim + 2); | ||
|  | 	//
 | ||
|  | 	A.setSubMatrix(0, 0, dim - 1, dim - 1,ident); | ||
|  | 	A.setSubMatrix(0, dim, dim - 1, 2 * dim - 1,mNeg); | ||
|  | 	A.setSubMatrix(0, 2 * dim, dim - 1, 2 * dim, -1.f); | ||
|  | 	A.setSubMatrix(0, 2 * dim + 1, dim - 1, 2 * dim + 1,m_q); | ||
|  | 
 | ||
|  | #ifdef BT_DEBUG_OSTREAM
 | ||
|  | 	cout << A << std::endl; | ||
|  | #endif //BT_DEBUG_OSTREAM
 | ||
|  | 
 | ||
|  | 
 | ||
|  |  //   btVectorXu q_;
 | ||
|  |  //   q_ >> A(0, 2 * dim + 1, dim - 1, 2 * dim + 1);
 | ||
|  | 
 | ||
|  |     btAlignedObjectArray<int> basis; | ||
|  |     //At first, all w-values are in the basis
 | ||
|  |     for (int i = 0; i < dim; i++) | ||
|  |       basis.push_back(i); | ||
|  | 
 | ||
|  | 	int pivotRowIndex = -1; | ||
|  | 	btScalar minValue = 1e30f; | ||
|  | 	bool greaterZero = true; | ||
|  | 	for (int i=0;i<dim;i++) | ||
|  | 	{ | ||
|  | 		btScalar v =A(i,2*dim+1); | ||
|  | 		if (v<minValue) | ||
|  | 		{ | ||
|  | 			minValue=v; | ||
|  | 			pivotRowIndex = i; | ||
|  | 		} | ||
|  | 		if (v<0) | ||
|  | 			greaterZero = false; | ||
|  | 	} | ||
|  | 	 | ||
|  | 
 | ||
|  | 	 | ||
|  |   //  int pivotRowIndex = q_.minIndex();//minIndex(q_);     // first row is that with lowest q-value
 | ||
|  |     int z0Row = pivotRowIndex;           // remember the col of z0 for ending algorithm afterwards
 | ||
|  |     int pivotColIndex = 2 * dim;         // first col is that of z0
 | ||
|  | 
 | ||
|  | #ifdef BT_DEBUG_OSTREAM
 | ||
|  |     if (DEBUGLEVEL >= 3) | ||
|  | 	{ | ||
|  |     //  cout << "A: " << A << endl;
 | ||
|  |       cout << "pivotRowIndex " << pivotRowIndex << endl; | ||
|  |       cout << "pivotColIndex " << pivotColIndex << endl; | ||
|  |       cout << "Basis: "; | ||
|  |       for (int i = 0; i < basis.size(); i++) | ||
|  |         cout << basis[i] << " "; | ||
|  |       cout << endl; | ||
|  |     } | ||
|  | #endif //BT_DEBUG_OSTREAM
 | ||
|  | 
 | ||
|  | 	if (!greaterZero) | ||
|  | 	{ | ||
|  | 
 | ||
|  |       if (maxloops == 0) { | ||
|  | 		  maxloops = 100; | ||
|  | //        maxloops = UINT_MAX; //TODO: not a really nice way, problem is: maxloops should be 2^dim (=1<<dim), but this could exceed UINT_MAX and thus the result would be 0 and therefore the lemke algorithm wouldn't start but probably would find a solution within less then UINT_MAX steps. Therefore this constant is used as a upper border right now...
 | ||
|  |       } | ||
|  | 
 | ||
|  |       /*start looping*/ | ||
|  |       for(steps = 0; steps < maxloops; steps++) { | ||
|  | 
 | ||
|  |         GaussJordanEliminationStep(A, pivotRowIndex, pivotColIndex, basis); | ||
|  | #ifdef BT_DEBUG_OSTREAM
 | ||
|  |         if (DEBUGLEVEL >= 3) { | ||
|  |         //  cout << "A: " << A << endl;
 | ||
|  |           cout << "pivotRowIndex " << pivotRowIndex << endl; | ||
|  |           cout << "pivotColIndex " << pivotColIndex << endl; | ||
|  |           cout << "Basis: "; | ||
|  |           for (int i = 0; i < basis.size(); i++) | ||
|  |             cout << basis[i] << " "; | ||
|  |           cout << endl; | ||
|  |         } | ||
|  | #endif //BT_DEBUG_OSTREAM
 | ||
|  | 
 | ||
|  |         int pivotColIndexOld = pivotColIndex; | ||
|  | 
 | ||
|  |         /*find new column index */ | ||
|  |         if (basis[pivotRowIndex] < dim) //if a w-value left the basis get in the correspondent z-value
 | ||
|  |           pivotColIndex = basis[pivotRowIndex] + dim; | ||
|  |         else | ||
|  |           //else do it the other way round and get in the corresponding w-value
 | ||
|  |           pivotColIndex = basis[pivotRowIndex] - dim; | ||
|  | 
 | ||
|  |         /*the column becomes part of the basis*/ | ||
|  |         basis[pivotRowIndex] = pivotColIndexOld; | ||
|  | 
 | ||
|  |         pivotRowIndex = findLexicographicMinimum(A, pivotColIndex); | ||
|  | 
 | ||
|  |         if(z0Row == pivotRowIndex) { //if z0 leaves the basis the solution is found --> one last elimination step is necessary
 | ||
|  |           GaussJordanEliminationStep(A, pivotRowIndex, pivotColIndex, basis); | ||
|  |           basis[pivotRowIndex] = pivotColIndex; //update basis
 | ||
|  |           break; | ||
|  |       } | ||
|  | 
 | ||
|  |       } | ||
|  | #ifdef BT_DEBUG_OSTREAM
 | ||
|  |       if(DEBUGLEVEL >= 1) { | ||
|  |         cout << "Number of loops: " << steps << endl; | ||
|  |         cout << "Number of maximal loops: " << maxloops << endl; | ||
|  |       } | ||
|  | #endif //BT_DEBUG_OSTREAM
 | ||
|  | 
 | ||
|  |       if(!validBasis(basis)) { | ||
|  |         info = -1; | ||
|  | #ifdef BT_DEBUG_OSTREAM
 | ||
|  |         if(DEBUGLEVEL >= 1) | ||
|  |           cerr << "Lemke-Algorithm ended with Ray-Termination (no valid solution)." << endl; | ||
|  | #endif //BT_DEBUG_OSTREAM
 | ||
|  | 
 | ||
|  |         return solutionVector; | ||
|  |       } | ||
|  | 
 | ||
|  |     } | ||
|  | #ifdef BT_DEBUG_OSTREAM
 | ||
|  |     if (DEBUGLEVEL >= 2) { | ||
|  |      // cout << "A: " << A << endl;
 | ||
|  |       cout << "pivotRowIndex " << pivotRowIndex << endl; | ||
|  |       cout << "pivotColIndex " << pivotColIndex << endl; | ||
|  |     } | ||
|  | #endif //BT_DEBUG_OSTREAM
 | ||
|  | 
 | ||
|  |     for (int i = 0; i < basis.size(); i++) | ||
|  | 	{ | ||
|  |       solutionVector[basis[i]] = A(i,2*dim+1);//q_[i];
 | ||
|  | 	} | ||
|  | 
 | ||
|  |     info = 0; | ||
|  | 
 | ||
|  |     return solutionVector; | ||
|  |   } | ||
|  | 
 | ||
|  |   int btLemkeAlgorithm::findLexicographicMinimum(const btMatrixXu& A, const int & pivotColIndex) { | ||
|  | 	  int RowIndex = 0; | ||
|  | 	  int dim = A.rows(); | ||
|  | 	  btAlignedObjectArray<btVectorXu> Rows; | ||
|  | 	  for (int row = 0; row < dim; row++)  | ||
|  | 	  { | ||
|  | 
 | ||
|  | 		  btVectorXu vec(dim + 1); | ||
|  | 		  vec.setZero();//, INIT, 0.)
 | ||
|  | 		  Rows.push_back(vec); | ||
|  | 		  btScalar a = A(row, pivotColIndex); | ||
|  | 		  if (a > 0) { | ||
|  | 			  Rows[row][0] = A(row, 2 * dim + 1) / a; | ||
|  | 			  Rows[row][1] = A(row, 2 * dim) / a; | ||
|  | 			  for (int j = 2; j < dim + 1; j++) | ||
|  | 				  Rows[row][j] = A(row, j - 1) / a; | ||
|  | 
 | ||
|  | #ifdef BT_DEBUG_OSTREAM
 | ||
|  | 		//		if (DEBUGLEVEL) {
 | ||
|  | 			//	  cout << "Rows(" << row << ") = " << Rows[row] << endl;
 | ||
|  | 				// }
 | ||
|  | #endif 
 | ||
|  | 		  } | ||
|  | 	  } | ||
|  | 
 | ||
|  | 	  for (int i = 0; i < Rows.size(); i++)  | ||
|  | 	  { | ||
|  | 		  if (Rows[i].nrm2() > 0.) { | ||
|  | 
 | ||
|  | 			  int j = 0; | ||
|  | 			  for (; j < Rows.size(); j++)  | ||
|  | 			  { | ||
|  | 				  if(i != j) | ||
|  | 				  { | ||
|  | 					  if(Rows[j].nrm2() > 0.) | ||
|  | 					  { | ||
|  | 						  btVectorXu test(dim + 1); | ||
|  | 						  for (int ii=0;ii<dim+1;ii++) | ||
|  | 						  { | ||
|  | 							  test[ii] = Rows[j][ii] - Rows[i][ii]; | ||
|  | 						  } | ||
|  | 
 | ||
|  | 						  //=Rows[j] - Rows[i]
 | ||
|  | 						  if (! LexicographicPositive(test)) | ||
|  | 							  break; | ||
|  | 					  } | ||
|  | 				  } | ||
|  | 			  } | ||
|  | 
 | ||
|  | 			  if (j == Rows.size())  | ||
|  | 			  { | ||
|  | 				  RowIndex += i; | ||
|  | 				  break; | ||
|  | 			  } | ||
|  | 		  } | ||
|  | 	  } | ||
|  | 
 | ||
|  | 	  return RowIndex; | ||
|  |   } | ||
|  | 
 | ||
|  |   bool btLemkeAlgorithm::LexicographicPositive(const btVectorXu & v) | ||
|  | { | ||
|  |     int i = 0; | ||
|  |   //  if (DEBUGLEVEL)
 | ||
|  |     //  cout << "v " << v << endl;
 | ||
|  | 
 | ||
|  |     while(i < v.size()-1 && fabs(v[i]) < btMachEps()) | ||
|  |       i++; | ||
|  |     if (v[i] > 0) | ||
|  |       return true; | ||
|  | 
 | ||
|  |     return false; | ||
|  |   } | ||
|  | 
 | ||
|  | void btLemkeAlgorithm::GaussJordanEliminationStep(btMatrixXu& A, int pivotRowIndex, int pivotColumnIndex, const btAlignedObjectArray<int>& basis)  | ||
|  | { | ||
|  | 
 | ||
|  | 	btScalar a = -1 / A(pivotRowIndex, pivotColumnIndex); | ||
|  | #ifdef BT_DEBUG_OSTREAM
 | ||
|  | 	cout << A << std::endl; | ||
|  | #endif
 | ||
|  | 
 | ||
|  |     for (int i = 0; i < A.rows(); i++) | ||
|  | 	{ | ||
|  |       if (i != pivotRowIndex) | ||
|  | 	  { | ||
|  |         for (int j = 0; j < A.cols(); j++) | ||
|  | 		{ | ||
|  |           if (j != pivotColumnIndex) | ||
|  | 		  { | ||
|  | 			  btScalar v = A(i, j); | ||
|  | 			  v += A(pivotRowIndex, j) * A(i, pivotColumnIndex) * a; | ||
|  |             A.setElem(i, j, v); | ||
|  | 		  } | ||
|  | 		} | ||
|  | 	  } | ||
|  | 	} | ||
|  | 
 | ||
|  | #ifdef BT_DEBUG_OSTREAM
 | ||
|  | 	cout << A << std::endl; | ||
|  | #endif //BT_DEBUG_OSTREAM
 | ||
|  |     for (int i = 0; i < A.cols(); i++)  | ||
|  | 	{ | ||
|  |       A.mulElem(pivotRowIndex, i,-a); | ||
|  |     } | ||
|  | #ifdef BT_DEBUG_OSTREAM
 | ||
|  | 	cout << A << std::endl; | ||
|  | #endif //#ifdef BT_DEBUG_OSTREAM
 | ||
|  | 
 | ||
|  |     for (int i = 0; i < A.rows(); i++) | ||
|  | 	{ | ||
|  |       if (i != pivotRowIndex) | ||
|  | 	  { | ||
|  |         A.setElem(i, pivotColumnIndex,0); | ||
|  | 	  } | ||
|  | 	} | ||
|  | #ifdef BT_DEBUG_OSTREAM
 | ||
|  | 	cout << A << std::endl; | ||
|  | #endif //#ifdef BT_DEBUG_OSTREAM
 | ||
|  |   } | ||
|  | 
 | ||
|  |   bool btLemkeAlgorithm::greaterZero(const btVectorXu & vector) | ||
|  | { | ||
|  |     bool isGreater = true; | ||
|  |     for (int i = 0; i < vector.size(); i++) { | ||
|  |       if (vector[i] < 0) { | ||
|  |         isGreater = false; | ||
|  |         break; | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |     return isGreater; | ||
|  |   } | ||
|  | 
 | ||
|  |   bool btLemkeAlgorithm::validBasis(const btAlignedObjectArray<int>& basis)  | ||
|  |   { | ||
|  |     bool isValid = true; | ||
|  |     for (int i = 0; i < basis.size(); i++) { | ||
|  |       if (basis[i] >= basis.size() * 2) { //then z0 is in the base
 | ||
|  |         isValid = false; | ||
|  |         break; | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |     return isValid; | ||
|  |   } | ||
|  | 
 | ||
|  | 
 |