1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645 |
- /*
- ===========================================================================
- Doom 3 GPL Source Code
- Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
- This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
- Doom 3 Source Code is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- Doom 3 Source Code is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
- In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
- If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
- ===========================================================================
- */
- #include "../precompiled.h"
- #pragma hdrstop
- static idCVar lcp_showFailures( "lcp_showFailures", "0", CVAR_SYSTEM | CVAR_BOOL, "show LCP solver failures" );
- const float LCP_BOUND_EPSILON = 1e-5f;
- const float LCP_ACCEL_EPSILON = 1e-5f;
- const float LCP_DELTA_ACCEL_EPSILON = 1e-9f;
- const float LCP_DELTA_FORCE_EPSILON = 1e-9f;
- #define IGNORE_UNSATISFIABLE_VARIABLES
- //===============================================================
- // M
- // idLCP_Square MrE
- // E
- //===============================================================
- class idLCP_Square : public idLCP {
- public:
- virtual bool Solve( const idMatX &o_m, idVecX &o_x, const idVecX &o_b, const idVecX &o_lo, const idVecX &o_hi, const int *o_boxIndex );
- private:
- idMatX m; // original matrix
- idVecX b; // right hand side
- idVecX lo, hi; // low and high bounds
- idVecX f, a; // force and acceleration
- idVecX delta_f, delta_a; // delta force and delta acceleration
- idMatX clamped; // LU factored sub matrix for clamped variables
- idVecX diagonal; // reciprocal of diagonal of U of the LU factored sub matrix for clamped variables
- int numUnbounded; // number of unbounded variables
- int numClamped; // number of clamped variables
- float ** rowPtrs; // pointers to the rows of m
- int * boxIndex; // box index
- int * side; // tells if a variable is at the low boundary = -1, high boundary = 1 or inbetween = 0
- int * permuted; // index to keep track of the permutation
- bool padded; // set to true if the rows of the initial matrix are 16 byte padded
- private:
- bool FactorClamped( void );
- void SolveClamped( idVecX &x, const float *b );
- void Swap( int i, int j );
- void AddClamped( int r );
- void RemoveClamped( int r );
- void CalcForceDelta( int d, float dir );
- void CalcAccelDelta( int d );
- void ChangeForce( int d, float step );
- void ChangeAccel( int d, float step );
- void GetMaxStep( int d, float dir, float &maxStep, int &limit, int &limitSide ) const;
- };
- /*
- ============
- idLCP_Square::FactorClamped
- ============
- */
- bool idLCP_Square::FactorClamped( void ) {
- int i, j, k;
- float s, d;
- for ( i = 0; i < numClamped; i++ ) {
- memcpy( clamped[i], rowPtrs[i], numClamped * sizeof( float ) );
- }
- for ( i = 0; i < numClamped; i++ ) {
- s = idMath::Fabs( clamped[i][i] );
- if ( s == 0.0f ) {
- return false;
- }
- diagonal[i] = d = 1.0f / clamped[i][i];
- for ( j = i + 1; j < numClamped; j++ ) {
- clamped[j][i] *= d;
- }
- for ( j = i + 1; j < numClamped; j++ ) {
- d = clamped[j][i];
- for ( k = i + 1; k < numClamped; k++ ) {
- clamped[j][k] -= d * clamped[i][k];
- }
- }
- }
- return true;
- }
- /*
- ============
- idLCP_Square::SolveClamped
- ============
- */
- void idLCP_Square::SolveClamped( idVecX &x, const float *b ) {
- int i, j;
- float sum;
- // solve L
- for ( i = 0; i < numClamped; i++ ) {
- sum = b[i];
- for ( j = 0; j < i; j++ ) {
- sum -= clamped[i][j] * x[j];
- }
- x[i] = sum;
- }
- // solve U
- for ( i = numClamped - 1; i >= 0; i-- ) {
- sum = x[i];
- for ( j = i + 1; j < numClamped; j++ ) {
- sum -= clamped[i][j] * x[j];
- }
- x[i] = sum * diagonal[i];
- }
- }
- /*
- ============
- idLCP_Square::Swap
- ============
- */
- void idLCP_Square::Swap( int i, int j ) {
- if ( i == j ) {
- return;
- }
- idSwap( rowPtrs[i], rowPtrs[j] );
- m.SwapColumns( i, j );
- b.SwapElements( i, j );
- lo.SwapElements( i, j );
- hi.SwapElements( i, j );
- a.SwapElements( i, j );
- f.SwapElements( i, j );
- if ( boxIndex ) {
- idSwap( boxIndex[i], boxIndex[j] );
- }
- idSwap( side[i], side[j] );
- idSwap( permuted[i], permuted[j] );
- }
- /*
- ============
- idLCP_Square::AddClamped
- ============
- */
- void idLCP_Square::AddClamped( int r ) {
- int i, j;
- float sum;
- assert( r >= numClamped );
- // add a row at the bottom and a column at the right of the factored
- // matrix for the clamped variables
- Swap( numClamped, r );
- // add row to L
- for ( i = 0; i < numClamped; i++ ) {
- sum = rowPtrs[numClamped][i];
- for ( j = 0; j < i; j++ ) {
- sum -= clamped[numClamped][j] * clamped[j][i];
- }
- clamped[numClamped][i] = sum * diagonal[i];
- }
- // add column to U
- for ( i = 0; i <= numClamped; i++ ) {
- sum = rowPtrs[i][numClamped];
- for ( j = 0; j < i; j++ ) {
- sum -= clamped[i][j] * clamped[j][numClamped];
- }
- clamped[i][numClamped] = sum;
- }
- diagonal[numClamped] = 1.0f / clamped[numClamped][numClamped];
- numClamped++;
- }
- /*
- ============
- idLCP_Square::RemoveClamped
- ============
- */
- void idLCP_Square::RemoveClamped( int r ) {
- int i, j;
- float *y0, *y1, *z0, *z1;
- double diag, beta0, beta1, p0, p1, q0, q1, d;
- assert( r < numClamped );
- numClamped--;
- // no need to swap and update the factored matrix when the last row and column are removed
- if ( r == numClamped ) {
- return;
- }
- y0 = (float *) _alloca16( numClamped * sizeof( float ) );
- z0 = (float *) _alloca16( numClamped * sizeof( float ) );
- y1 = (float *) _alloca16( numClamped * sizeof( float ) );
- z1 = (float *) _alloca16( numClamped * sizeof( float ) );
- // the row/column need to be subtracted from the factorization
- for ( i = 0; i < numClamped; i++ ) {
- y0[i] = -rowPtrs[i][r];
- }
- memset( y1, 0, numClamped * sizeof( float ) );
- y1[r] = 1.0f;
- memset( z0, 0, numClamped * sizeof( float ) );
- z0[r] = 1.0f;
- for ( i = 0; i < numClamped; i++ ) {
- z1[i] = -rowPtrs[r][i];
- }
- // swap the to be removed row/column with the last row/column
- Swap( r, numClamped );
- // the swapped last row/column need to be added to the factorization
- for ( i = 0; i < numClamped; i++ ) {
- y0[i] += rowPtrs[i][r];
- }
- for ( i = 0; i < numClamped; i++ ) {
- z1[i] += rowPtrs[r][i];
- }
- z1[r] = 0.0f;
- // update the beginning of the to be updated row and column
- for ( i = 0; i < r; i++ ) {
- p0 = y0[i];
- beta1 = z1[i] * diagonal[i];
- clamped[i][r] += p0;
- for ( j = i+1; j < numClamped; j++ ) {
- z1[j] -= beta1 * clamped[i][j];
- }
- for ( j = i+1; j < numClamped; j++ ) {
- y0[j] -= p0 * clamped[j][i];
- }
- clamped[r][i] += beta1;
- }
- // update the lower right corner starting at r,r
- for ( i = r; i < numClamped; i++ ) {
- diag = clamped[i][i];
- p0 = y0[i];
- p1 = z0[i];
- diag += p0 * p1;
- if ( diag == 0.0f ) {
- idLib::common->Printf( "idLCP_Square::RemoveClamped: updating factorization failed\n" );
- return;
- }
- beta0 = p1 / diag;
- q0 = y1[i];
- q1 = z1[i];
- diag += q0 * q1;
- if ( diag == 0.0f ) {
- idLib::common->Printf( "idLCP_Square::RemoveClamped: updating factorization failed\n" );
- return;
- }
- d = 1.0f / diag;
- beta1 = q1 * d;
- clamped[i][i] = diag;
- diagonal[i] = d;
- for ( j = i+1; j < numClamped; j++ ) {
- d = clamped[i][j];
- d += p0 * z0[j];
- z0[j] -= beta0 * d;
- d += q0 * z1[j];
- z1[j] -= beta1 * d;
- clamped[i][j] = d;
- }
- for ( j = i+1; j < numClamped; j++ ) {
- d = clamped[j][i];
- y0[j] -= p0 * d;
- d += beta0 * y0[j];
- y1[j] -= q0 * d;
- d += beta1 * y1[j];
- clamped[j][i] = d;
- }
- }
- return;
- }
- /*
- ============
- idLCP_Square::CalcForceDelta
- modifies this->delta_f
- ============
- */
- ID_INLINE void idLCP_Square::CalcForceDelta( int d, float dir ) {
- int i;
- float *ptr;
- delta_f[d] = dir;
- if ( numClamped == 0 ) {
- return;
- }
- // get column d of matrix
- ptr = (float *) _alloca16( numClamped * sizeof( float ) );
- for ( i = 0; i < numClamped; i++ ) {
- ptr[i] = rowPtrs[i][d];
- }
- // solve force delta
- SolveClamped( delta_f, ptr );
- // flip force delta based on direction
- if ( dir > 0.0f ) {
- ptr = delta_f.ToFloatPtr();
- for ( i = 0; i < numClamped; i++ ) {
- ptr[i] = - ptr[i];
- }
- }
- }
- /*
- ============
- idLCP_Square::CalcAccelDelta
- modifies this->delta_a and uses this->delta_f
- ============
- */
- ID_INLINE void idLCP_Square::CalcAccelDelta( int d ) {
- int j;
- float dot;
- // only the not clamped variables, including the current variable, can have a change in acceleration
- for ( j = numClamped; j <= d; j++ ) {
- // only the clamped variables and the current variable have a force delta unequal zero
- SIMDProcessor->Dot( dot, rowPtrs[j], delta_f.ToFloatPtr(), numClamped );
- delta_a[j] = dot + rowPtrs[j][d] * delta_f[d];
- }
- }
- /*
- ============
- idLCP_Square::ChangeForce
- modifies this->f and uses this->delta_f
- ============
- */
- ID_INLINE void idLCP_Square::ChangeForce( int d, float step ) {
- // only the clamped variables and current variable have a force delta unequal zero
- SIMDProcessor->MulAdd( f.ToFloatPtr(), step, delta_f.ToFloatPtr(), numClamped );
- f[d] += step * delta_f[d];
- }
- /*
- ============
- idLCP_Square::ChangeAccel
- modifies this->a and uses this->delta_a
- ============
- */
- ID_INLINE void idLCP_Square::ChangeAccel( int d, float step ) {
- // only the not clamped variables, including the current variable, can have an acceleration unequal zero
- SIMDProcessor->MulAdd( a.ToFloatPtr() + numClamped, step, delta_a.ToFloatPtr() + numClamped, d - numClamped + 1 );
- }
- /*
- ============
- idLCP_Square::GetMaxStep
- ============
- */
- void idLCP_Square::GetMaxStep( int d, float dir, float &maxStep, int &limit, int &limitSide ) const {
- int i;
- float s;
- // default to a full step for the current variable
- if ( idMath::Fabs( delta_a[d] ) > LCP_DELTA_ACCEL_EPSILON ) {
- maxStep = -a[d] / delta_a[d];
- } else {
- maxStep = 0.0f;
- }
- limit = d;
- limitSide = 0;
- // test the current variable
- if ( dir < 0.0f ) {
- if ( lo[d] != -idMath::INFINITY ) {
- s = ( lo[d] - f[d] ) / dir;
- if ( s < maxStep ) {
- maxStep = s;
- limitSide = -1;
- }
- }
- } else {
- if ( hi[d] != idMath::INFINITY ) {
- s = ( hi[d] - f[d] ) / dir;
- if ( s < maxStep ) {
- maxStep = s;
- limitSide = 1;
- }
- }
- }
- // test the clamped bounded variables
- for ( i = numUnbounded; i < numClamped; i++ ) {
- if ( delta_f[i] < -LCP_DELTA_FORCE_EPSILON ) {
- // if there is a low boundary
- if ( lo[i] != -idMath::INFINITY ) {
- s = ( lo[i] - f[i] ) / delta_f[i];
- if ( s < maxStep ) {
- maxStep = s;
- limit = i;
- limitSide = -1;
- }
- }
- } else if ( delta_f[i] > LCP_DELTA_FORCE_EPSILON ) {
- // if there is a high boundary
- if ( hi[i] != idMath::INFINITY ) {
- s = ( hi[i] - f[i] ) / delta_f[i];
- if ( s < maxStep ) {
- maxStep = s;
- limit = i;
- limitSide = 1;
- }
- }
- }
- }
- // test the not clamped bounded variables
- for ( i = numClamped; i < d; i++ ) {
- if ( side[i] == -1 ) {
- if ( delta_a[i] >= -LCP_DELTA_ACCEL_EPSILON ) {
- continue;
- }
- } else if ( side[i] == 1 ) {
- if ( delta_a[i] <= LCP_DELTA_ACCEL_EPSILON ) {
- continue;
- }
- } else {
- continue;
- }
- // ignore variables for which the force is not allowed to take any substantial value
- if ( lo[i] >= -LCP_BOUND_EPSILON && hi[i] <= LCP_BOUND_EPSILON ) {
- continue;
- }
- s = -a[i] / delta_a[i];
- if ( s < maxStep ) {
- maxStep = s;
- limit = i;
- limitSide = 0;
- }
- }
- }
- /*
- ============
- idLCP_Square::Solve
- ============
- */
- bool idLCP_Square::Solve( const idMatX &o_m, idVecX &o_x, const idVecX &o_b, const idVecX &o_lo, const idVecX &o_hi, const int *o_boxIndex ) {
- int i, j, n, limit, limitSide, boxStartIndex;
- float dir, maxStep, dot, s;
- char *failed;
- // true when the matrix rows are 16 byte padded
- padded = ((o_m.GetNumRows()+3)&~3) == o_m.GetNumColumns();
- assert( padded || o_m.GetNumRows() == o_m.GetNumColumns() );
- assert( o_x.GetSize() == o_m.GetNumRows() );
- assert( o_b.GetSize() == o_m.GetNumRows() );
- assert( o_lo.GetSize() == o_m.GetNumRows() );
- assert( o_hi.GetSize() == o_m.GetNumRows() );
- // allocate memory for permuted input
- f.SetData( o_m.GetNumRows(), VECX_ALLOCA( o_m.GetNumRows() ) );
- a.SetData( o_b.GetSize(), VECX_ALLOCA( o_b.GetSize() ) );
- b.SetData( o_b.GetSize(), VECX_ALLOCA( o_b.GetSize() ) );
- lo.SetData( o_lo.GetSize(), VECX_ALLOCA( o_lo.GetSize() ) );
- hi.SetData( o_hi.GetSize(), VECX_ALLOCA( o_hi.GetSize() ) );
- if ( o_boxIndex ) {
- boxIndex = (int *)_alloca16( o_x.GetSize() * sizeof( int ) );
- memcpy( boxIndex, o_boxIndex, o_x.GetSize() * sizeof( int ) );
- } else {
- boxIndex = NULL;
- }
- // we override the const on o_m here but on exit the matrix is unchanged
- m.SetData( o_m.GetNumRows(), o_m.GetNumColumns(), const_cast<float *>(o_m[0]) );
- f.Zero();
- a.Zero();
- b = o_b;
- lo = o_lo;
- hi = o_hi;
- // pointers to the rows of m
- rowPtrs = (float **) _alloca16( m.GetNumRows() * sizeof( float * ) );
- for ( i = 0; i < m.GetNumRows(); i++ ) {
- rowPtrs[i] = m[i];
- }
- // tells if a variable is at the low boundary, high boundary or inbetween
- side = (int *) _alloca16( m.GetNumRows() * sizeof( int ) );
- // index to keep track of the permutation
- permuted = (int *) _alloca16( m.GetNumRows() * sizeof( int ) );
- for ( i = 0; i < m.GetNumRows(); i++ ) {
- permuted[i] = i;
- }
- // permute input so all unbounded variables come first
- numUnbounded = 0;
- for ( i = 0; i < m.GetNumRows(); i++ ) {
- if ( lo[i] == -idMath::INFINITY && hi[i] == idMath::INFINITY ) {
- if ( numUnbounded != i ) {
- Swap( numUnbounded, i );
- }
- numUnbounded++;
- }
- }
- // permute input so all variables using the boxIndex come last
- boxStartIndex = m.GetNumRows();
- if ( boxIndex ) {
- for ( i = m.GetNumRows() - 1; i >= numUnbounded; i-- ) {
- if ( boxIndex[i] >= 0 && ( lo[i] != -idMath::INFINITY || hi[i] != idMath::INFINITY ) ) {
- boxStartIndex--;
- if ( boxStartIndex != i ) {
- Swap( boxStartIndex, i );
- }
- }
- }
- }
- // sub matrix for factorization
- clamped.SetData( m.GetNumRows(), m.GetNumColumns(), MATX_ALLOCA( m.GetNumRows() * m.GetNumColumns() ) );
- diagonal.SetData( m.GetNumRows(), VECX_ALLOCA( m.GetNumRows() ) );
- // all unbounded variables are clamped
- numClamped = numUnbounded;
- // if there are unbounded variables
- if ( numUnbounded ) {
- // factor and solve for unbounded variables
- if ( !FactorClamped() ) {
- idLib::common->Printf( "idLCP_Square::Solve: unbounded factorization failed\n" );
- return false;
- }
- SolveClamped( f, b.ToFloatPtr() );
- // if there are no bounded variables we are done
- if ( numUnbounded == m.GetNumRows() ) {
- o_x = f; // the vector is not permuted
- return true;
- }
- }
- #ifdef IGNORE_UNSATISFIABLE_VARIABLES
- int numIgnored = 0;
- #endif
- // allocate for delta force and delta acceleration
- delta_f.SetData( m.GetNumRows(), VECX_ALLOCA( m.GetNumRows() ) );
- delta_a.SetData( m.GetNumRows(), VECX_ALLOCA( m.GetNumRows() ) );
- // solve for bounded variables
- failed = NULL;
- for ( i = numUnbounded; i < m.GetNumRows(); i++ ) {
- // once we hit the box start index we can initialize the low and high boundaries of the variables using the box index
- if ( i == boxStartIndex ) {
- for ( j = 0; j < boxStartIndex; j++ ) {
- o_x[permuted[j]] = f[j];
- }
- for ( j = boxStartIndex; j < m.GetNumRows(); j++ ) {
- s = o_x[boxIndex[j]];
- if ( lo[j] != -idMath::INFINITY ) {
- lo[j] = - idMath::Fabs( lo[j] * s );
- }
- if ( hi[j] != idMath::INFINITY ) {
- hi[j] = idMath::Fabs( hi[j] * s );
- }
- }
- }
- // calculate acceleration for current variable
- SIMDProcessor->Dot( dot, rowPtrs[i], f.ToFloatPtr(), i );
- a[i] = dot - b[i];
- // if already at the low boundary
- if ( lo[i] >= -LCP_BOUND_EPSILON && a[i] >= -LCP_ACCEL_EPSILON ) {
- side[i] = -1;
- continue;
- }
- // if already at the high boundary
- if ( hi[i] <= LCP_BOUND_EPSILON && a[i] <= LCP_ACCEL_EPSILON ) {
- side[i] = 1;
- continue;
- }
- // if inside the clamped region
- if ( idMath::Fabs( a[i] ) <= LCP_ACCEL_EPSILON ) {
- side[i] = 0;
- AddClamped( i );
- continue;
- }
- // drive the current variable into a valid region
- for ( n = 0; n < maxIterations; n++ ) {
- // direction to move
- if ( a[i] <= 0.0f ) {
- dir = 1.0f;
- } else {
- dir = -1.0f;
- }
- // calculate force delta
- CalcForceDelta( i, dir );
- // calculate acceleration delta: delta_a = m * delta_f;
- CalcAccelDelta( i );
- // maximum step we can take
- GetMaxStep( i, dir, maxStep, limit, limitSide );
- if ( maxStep <= 0.0f ) {
- #ifdef IGNORE_UNSATISFIABLE_VARIABLES
- // ignore the current variable completely
- lo[i] = hi[i] = 0.0f;
- f[i] = 0.0f;
- side[i] = -1;
- numIgnored++;
- #else
- failed = va( "invalid step size %.4f", maxStep );
- #endif
- break;
- }
- // change force
- ChangeForce( i, maxStep );
- // change acceleration
- ChangeAccel( i, maxStep );
- // clamp/unclamp the variable that limited this step
- side[limit] = limitSide;
- switch( limitSide ) {
- case 0: {
- a[limit] = 0.0f;
- AddClamped( limit );
- break;
- }
- case -1: {
- f[limit] = lo[limit];
- if ( limit != i ) {
- RemoveClamped( limit );
- }
- break;
- }
- case 1: {
- f[limit] = hi[limit];
- if ( limit != i ) {
- RemoveClamped( limit );
- }
- break;
- }
- }
- // if the current variable limited the step we can continue with the next variable
- if ( limit == i ) {
- break;
- }
- }
- if ( n >= maxIterations ) {
- failed = va( "max iterations %d", maxIterations );
- break;
- }
- if ( failed ) {
- break;
- }
- }
- #ifdef IGNORE_UNSATISFIABLE_VARIABLES
- if ( numIgnored ) {
- if ( lcp_showFailures.GetBool() ) {
- idLib::common->Printf( "idLCP_Symmetric::Solve: %d of %d bounded variables ignored\n", numIgnored, m.GetNumRows() - numUnbounded );
- }
- }
- #endif
- // if failed clear remaining forces
- if ( failed ) {
- if ( lcp_showFailures.GetBool() ) {
- idLib::common->Printf( "idLCP_Square::Solve: %s (%d of %d bounded variables ignored)\n", failed, m.GetNumRows() - i, m.GetNumRows() - numUnbounded );
- }
- for ( j = i; j < m.GetNumRows(); j++ ) {
- f[j] = 0.0f;
- }
- }
- #if defined(_DEBUG) && 0
- if ( !failed ) {
- // test whether or not the solution satisfies the complementarity conditions
- for ( i = 0; i < m.GetNumRows(); i++ ) {
- a[i] = -b[i];
- for ( j = 0; j < m.GetNumRows(); j++ ) {
- a[i] += rowPtrs[i][j] * f[j];
- }
- if ( f[i] == lo[i] ) {
- if ( lo[i] != hi[i] && a[i] < -LCP_ACCEL_EPSILON ) {
- int bah1 = 1;
- }
- } else if ( f[i] == hi[i] ) {
- if ( lo[i] != hi[i] && a[i] > LCP_ACCEL_EPSILON ) {
- int bah2 = 1;
- }
- } else if ( f[i] < lo[i] || f[i] > hi[i] || idMath::Fabs( a[i] ) > 1.0f ) {
- int bah3 = 1;
- }
- }
- }
- #endif
- // unpermute result
- for ( i = 0; i < f.GetSize(); i++ ) {
- o_x[permuted[i]] = f[i];
- }
- // unpermute original matrix
- for ( i = 0; i < m.GetNumRows(); i++ ) {
- for ( j = 0; j < m.GetNumRows(); j++ ) {
- if ( permuted[j] == i ) {
- break;
- }
- }
- if ( i != j ) {
- m.SwapColumns( i, j );
- idSwap( permuted[i], permuted[j] );
- }
- }
- return true;
- }
- //===============================================================
- // M
- // idLCP_Symmetric MrE
- // E
- //===============================================================
- class idLCP_Symmetric : public idLCP {
- public:
- virtual bool Solve( const idMatX &o_m, idVecX &o_x, const idVecX &o_b, const idVecX &o_lo, const idVecX &o_hi, const int *o_boxIndex );
- private:
- idMatX m; // original matrix
- idVecX b; // right hand side
- idVecX lo, hi; // low and high bounds
- idVecX f, a; // force and acceleration
- idVecX delta_f, delta_a; // delta force and delta acceleration
- idMatX clamped; // LDLt factored sub matrix for clamped variables
- idVecX diagonal; // reciprocal of diagonal of LDLt factored sub matrix for clamped variables
- idVecX solveCache1; // intermediate result cached in SolveClamped
- idVecX solveCache2; // "
- int numUnbounded; // number of unbounded variables
- int numClamped; // number of clamped variables
- int clampedChangeStart; // lowest row/column changed in the clamped matrix during an iteration
- float ** rowPtrs; // pointers to the rows of m
- int * boxIndex; // box index
- int * side; // tells if a variable is at the low boundary = -1, high boundary = 1 or inbetween = 0
- int * permuted; // index to keep track of the permutation
- bool padded; // set to true if the rows of the initial matrix are 16 byte padded
- private:
- bool FactorClamped( void );
- void SolveClamped( idVecX &x, const float *b );
- void Swap( int i, int j );
- void AddClamped( int r, bool useSolveCache );
- void RemoveClamped( int r );
- void CalcForceDelta( int d, float dir );
- void CalcAccelDelta( int d );
- void ChangeForce( int d, float step );
- void ChangeAccel( int d, float step );
- void GetMaxStep( int d, float dir, float &maxStep, int &limit, int &limitSide ) const;
- };
- /*
- ============
- idLCP_Symmetric::FactorClamped
- ============
- */
- bool idLCP_Symmetric::FactorClamped( void ) {
- clampedChangeStart = 0;
- for ( int i = 0; i < numClamped; i++ ) {
- memcpy( clamped[i], rowPtrs[i], numClamped * sizeof( float ) );
- }
- return SIMDProcessor->MatX_LDLTFactor( clamped, diagonal, numClamped );
- }
- /*
- ============
- idLCP_Symmetric::SolveClamped
- ============
- */
- void idLCP_Symmetric::SolveClamped( idVecX &x, const float *b ) {
- // solve L
- SIMDProcessor->MatX_LowerTriangularSolve( clamped, solveCache1.ToFloatPtr(), b, numClamped, clampedChangeStart );
- // solve D
- SIMDProcessor->Mul( solveCache2.ToFloatPtr(), solveCache1.ToFloatPtr(), diagonal.ToFloatPtr(), numClamped );
- // solve Lt
- SIMDProcessor->MatX_LowerTriangularSolveTranspose( clamped, x.ToFloatPtr(), solveCache2.ToFloatPtr(), numClamped );
- clampedChangeStart = numClamped;
- }
- /*
- ============
- idLCP_Symmetric::Swap
- ============
- */
- void idLCP_Symmetric::Swap( int i, int j ) {
- if ( i == j ) {
- return;
- }
- idSwap( rowPtrs[i], rowPtrs[j] );
- m.SwapColumns( i, j );
- b.SwapElements( i, j );
- lo.SwapElements( i, j );
- hi.SwapElements( i, j );
- a.SwapElements( i, j );
- f.SwapElements( i, j );
- if ( boxIndex ) {
- idSwap( boxIndex[i], boxIndex[j] );
- }
- idSwap( side[i], side[j] );
- idSwap( permuted[i], permuted[j] );
- }
- /*
- ============
- idLCP_Symmetric::AddClamped
- ============
- */
- void idLCP_Symmetric::AddClamped( int r, bool useSolveCache ) {
- float d, dot;
- assert( r >= numClamped );
- if ( numClamped < clampedChangeStart ) {
- clampedChangeStart = numClamped;
- }
- // add a row at the bottom and a column at the right of the factored
- // matrix for the clamped variables
- Swap( numClamped, r );
- // solve for v in L * v = rowPtr[numClamped]
- if ( useSolveCache ) {
- // the lower triangular solve was cached in SolveClamped called by CalcForceDelta
- memcpy( clamped[numClamped], solveCache2.ToFloatPtr(), numClamped * sizeof( float ) );
- // calculate row dot product
- SIMDProcessor->Dot( dot, solveCache2.ToFloatPtr(), solveCache1.ToFloatPtr(), numClamped );
- } else {
- float *v = (float *) _alloca16( numClamped * sizeof( float ) );
- SIMDProcessor->MatX_LowerTriangularSolve( clamped, v, rowPtrs[numClamped], numClamped );
- // add bottom row to L
- SIMDProcessor->Mul( clamped[numClamped], v, diagonal.ToFloatPtr(), numClamped );
- // calculate row dot product
- SIMDProcessor->Dot( dot, clamped[numClamped], v, numClamped );
- }
- // update diagonal[numClamped]
- d = rowPtrs[numClamped][numClamped] - dot;
- if ( d == 0.0f ) {
- idLib::common->Printf( "idLCP_Symmetric::AddClamped: updating factorization failed\n" );
- numClamped++;
- return;
- }
- clamped[numClamped][numClamped] = d;
- diagonal[numClamped] = 1.0f / d;
- numClamped++;
- }
- /*
- ============
- idLCP_Symmetric::RemoveClamped
- ============
- */
- void idLCP_Symmetric::RemoveClamped( int r ) {
- int i, j, n;
- float *addSub, *original, *v, *ptr, *v1, *v2, dot;
- double sum, diag, newDiag, invNewDiag, p1, p2, alpha1, alpha2, beta1, beta2;
- assert( r < numClamped );
- if ( r < clampedChangeStart ) {
- clampedChangeStart = r;
- }
- numClamped--;
- // no need to swap and update the factored matrix when the last row and column are removed
- if ( r == numClamped ) {
- return;
- }
- // swap the to be removed row/column with the last row/column
- Swap( r, numClamped );
- // update the factored matrix
- addSub = (float *) _alloca16( numClamped * sizeof( float ) );
- if ( r == 0 ) {
- if ( numClamped == 1 ) {
- diag = rowPtrs[0][0];
- if ( diag == 0.0f ) {
- idLib::common->Printf( "idLCP_Symmetric::RemoveClamped: updating factorization failed\n" );
- return;
- }
- clamped[0][0] = diag;
- diagonal[0] = 1.0f / diag;
- return;
- }
- // calculate the row/column to be added to the lower right sub matrix starting at (r, r)
- original = rowPtrs[numClamped];
- ptr = rowPtrs[r];
- addSub[0] = ptr[0] - original[numClamped];
- for ( i = 1; i < numClamped; i++ ) {
- addSub[i] = ptr[i] - original[i];
- }
- } else {
- v = (float *) _alloca16( numClamped * sizeof( float ) );
- // solve for v in L * v = rowPtr[r]
- SIMDProcessor->MatX_LowerTriangularSolve( clamped, v, rowPtrs[r], r );
- // update removed row
- SIMDProcessor->Mul( clamped[r], v, diagonal.ToFloatPtr(), r );
- // if the last row/column of the matrix is updated
- if ( r == numClamped - 1 ) {
- // only calculate new diagonal
- SIMDProcessor->Dot( dot, clamped[r], v, r );
- diag = rowPtrs[r][r] - dot;
- if ( diag == 0.0f ) {
- idLib::common->Printf( "idLCP_Symmetric::RemoveClamped: updating factorization failed\n" );
- return;
- }
- clamped[r][r] = diag;
- diagonal[r] = 1.0f / diag;
- return;
- }
- // calculate the row/column to be added to the lower right sub matrix starting at (r, r)
- for ( i = 0; i < r; i++ ) {
- v[i] = clamped[r][i] * clamped[i][i];
- }
- for ( i = r; i < numClamped; i++ ) {
- if ( i == r ) {
- sum = clamped[r][r];
- } else {
- sum = clamped[r][r] * clamped[i][r];
- }
- ptr = clamped[i];
- for ( j = 0; j < r; j++ ) {
- sum += ptr[j] * v[j];
- }
- addSub[i] = rowPtrs[r][i] - sum;
- }
- }
- // add row/column to the lower right sub matrix starting at (r, r)
- v1 = (float *) _alloca16( numClamped * sizeof( float ) );
- v2 = (float *) _alloca16( numClamped * sizeof( float ) );
- diag = idMath::SQRT_1OVER2;
- v1[r] = ( 0.5f * addSub[r] + 1.0f ) * diag;
- v2[r] = ( 0.5f * addSub[r] - 1.0f ) * diag;
- for ( i = r+1; i < numClamped; i++ ) {
- v1[i] = v2[i] = addSub[i] * diag;
- }
- alpha1 = 1.0f;
- alpha2 = -1.0f;
- // simultaneous update/downdate of the sub matrix starting at (r, r)
- n = clamped.GetNumColumns();
- for ( i = r; i < numClamped; i++ ) {
- diag = clamped[i][i];
- p1 = v1[i];
- newDiag = diag + alpha1 * p1 * p1;
- if ( newDiag == 0.0f ) {
- idLib::common->Printf( "idLCP_Symmetric::RemoveClamped: updating factorization failed\n" );
- return;
- }
- alpha1 /= newDiag;
- beta1 = p1 * alpha1;
- alpha1 *= diag;
- diag = newDiag;
- p2 = v2[i];
- newDiag = diag + alpha2 * p2 * p2;
- if ( newDiag == 0.0f ) {
- idLib::common->Printf( "idLCP_Symmetric::RemoveClamped: updating factorization failed\n" );
- return;
- }
- clamped[i][i] = newDiag;
- diagonal[i] = invNewDiag = 1.0f / newDiag;
- alpha2 *= invNewDiag;
- beta2 = p2 * alpha2;
- alpha2 *= diag;
- // update column below diagonal (i,i)
- ptr = clamped.ToFloatPtr() + i;
- for ( j = i+1; j < numClamped - 1; j += 2 ) {
- float sum0 = ptr[(j+0)*n];
- float sum1 = ptr[(j+1)*n];
- v1[j+0] -= p1 * sum0;
- v1[j+1] -= p1 * sum1;
- sum0 += beta1 * v1[j+0];
- sum1 += beta1 * v1[j+1];
- v2[j+0] -= p2 * sum0;
- v2[j+1] -= p2 * sum1;
- sum0 += beta2 * v2[j+0];
- sum1 += beta2 * v2[j+1];
- ptr[(j+0)*n] = sum0;
- ptr[(j+1)*n] = sum1;
- }
- for ( ; j < numClamped; j++ ) {
- sum = ptr[j*n];
- v1[j] -= p1 * sum;
- sum += beta1 * v1[j];
- v2[j] -= p2 * sum;
- sum += beta2 * v2[j];
- ptr[j*n] = sum;
- }
- }
- }
- /*
- ============
- idLCP_Symmetric::CalcForceDelta
- modifies this->delta_f
- ============
- */
- ID_INLINE void idLCP_Symmetric::CalcForceDelta( int d, float dir ) {
- int i;
- float *ptr;
- delta_f[d] = dir;
- if ( numClamped == 0 ) {
- return;
- }
- // solve force delta
- SolveClamped( delta_f, rowPtrs[d] );
- // flip force delta based on direction
- if ( dir > 0.0f ) {
- ptr = delta_f.ToFloatPtr();
- for ( i = 0; i < numClamped; i++ ) {
- ptr[i] = - ptr[i];
- }
- }
- }
- /*
- ============
- idLCP_Symmetric::CalcAccelDelta
- modifies this->delta_a and uses this->delta_f
- ============
- */
- ID_INLINE void idLCP_Symmetric::CalcAccelDelta( int d ) {
- int j;
- float dot;
- // only the not clamped variables, including the current variable, can have a change in acceleration
- for ( j = numClamped; j <= d; j++ ) {
- // only the clamped variables and the current variable have a force delta unequal zero
- SIMDProcessor->Dot( dot, rowPtrs[j], delta_f.ToFloatPtr(), numClamped );
- delta_a[j] = dot + rowPtrs[j][d] * delta_f[d];
- }
- }
- /*
- ============
- idLCP_Symmetric::ChangeForce
- modifies this->f and uses this->delta_f
- ============
- */
- ID_INLINE void idLCP_Symmetric::ChangeForce( int d, float step ) {
- // only the clamped variables and current variable have a force delta unequal zero
- SIMDProcessor->MulAdd( f.ToFloatPtr(), step, delta_f.ToFloatPtr(), numClamped );
- f[d] += step * delta_f[d];
- }
- /*
- ============
- idLCP_Symmetric::ChangeAccel
- modifies this->a and uses this->delta_a
- ============
- */
- ID_INLINE void idLCP_Symmetric::ChangeAccel( int d, float step ) {
- // only the not clamped variables, including the current variable, can have an acceleration unequal zero
- SIMDProcessor->MulAdd( a.ToFloatPtr() + numClamped, step, delta_a.ToFloatPtr() + numClamped, d - numClamped + 1 );
- }
- /*
- ============
- idLCP_Symmetric::GetMaxStep
- ============
- */
- void idLCP_Symmetric::GetMaxStep( int d, float dir, float &maxStep, int &limit, int &limitSide ) const {
- int i;
- float s;
- // default to a full step for the current variable
- if ( idMath::Fabs( delta_a[d] ) > LCP_DELTA_ACCEL_EPSILON ) {
- maxStep = -a[d] / delta_a[d];
- } else {
- maxStep = 0.0f;
- }
- limit = d;
- limitSide = 0;
- // test the current variable
- if ( dir < 0.0f ) {
- if ( lo[d] != -idMath::INFINITY ) {
- s = ( lo[d] - f[d] ) / dir;
- if ( s < maxStep ) {
- maxStep = s;
- limitSide = -1;
- }
- }
- } else {
- if ( hi[d] != idMath::INFINITY ) {
- s = ( hi[d] - f[d] ) / dir;
- if ( s < maxStep ) {
- maxStep = s;
- limitSide = 1;
- }
- }
- }
- // test the clamped bounded variables
- for ( i = numUnbounded; i < numClamped; i++ ) {
- if ( delta_f[i] < -LCP_DELTA_FORCE_EPSILON ) {
- // if there is a low boundary
- if ( lo[i] != -idMath::INFINITY ) {
- s = ( lo[i] - f[i] ) / delta_f[i];
- if ( s < maxStep ) {
- maxStep = s;
- limit = i;
- limitSide = -1;
- }
- }
- } else if ( delta_f[i] > LCP_DELTA_FORCE_EPSILON ) {
- // if there is a high boundary
- if ( hi[i] != idMath::INFINITY ) {
- s = ( hi[i] - f[i] ) / delta_f[i];
- if ( s < maxStep ) {
- maxStep = s;
- limit = i;
- limitSide = 1;
- }
- }
- }
- }
- // test the not clamped bounded variables
- for ( i = numClamped; i < d; i++ ) {
- if ( side[i] == -1 ) {
- if ( delta_a[i] >= -LCP_DELTA_ACCEL_EPSILON ) {
- continue;
- }
- } else if ( side[i] == 1 ) {
- if ( delta_a[i] <= LCP_DELTA_ACCEL_EPSILON ) {
- continue;
- }
- } else {
- continue;
- }
- // ignore variables for which the force is not allowed to take any substantial value
- if ( lo[i] >= -LCP_BOUND_EPSILON && hi[i] <= LCP_BOUND_EPSILON ) {
- continue;
- }
- s = -a[i] / delta_a[i];
- if ( s < maxStep ) {
- maxStep = s;
- limit = i;
- limitSide = 0;
- }
- }
- }
- /*
- ============
- idLCP_Symmetric::Solve
- ============
- */
- bool idLCP_Symmetric::Solve( const idMatX &o_m, idVecX &o_x, const idVecX &o_b, const idVecX &o_lo, const idVecX &o_hi, const int *o_boxIndex ) {
- int i, j, n, limit, limitSide, boxStartIndex;
- float dir, maxStep, dot, s;
- char *failed;
- // true when the matrix rows are 16 byte padded
- padded = ((o_m.GetNumRows()+3)&~3) == o_m.GetNumColumns();
- assert( padded || o_m.GetNumRows() == o_m.GetNumColumns() );
- assert( o_x.GetSize() == o_m.GetNumRows() );
- assert( o_b.GetSize() == o_m.GetNumRows() );
- assert( o_lo.GetSize() == o_m.GetNumRows() );
- assert( o_hi.GetSize() == o_m.GetNumRows() );
- // allocate memory for permuted input
- f.SetData( o_m.GetNumRows(), VECX_ALLOCA( o_m.GetNumRows() ) );
- a.SetData( o_b.GetSize(), VECX_ALLOCA( o_b.GetSize() ) );
- b.SetData( o_b.GetSize(), VECX_ALLOCA( o_b.GetSize() ) );
- lo.SetData( o_lo.GetSize(), VECX_ALLOCA( o_lo.GetSize() ) );
- hi.SetData( o_hi.GetSize(), VECX_ALLOCA( o_hi.GetSize() ) );
- if ( o_boxIndex ) {
- boxIndex = (int *)_alloca16( o_x.GetSize() * sizeof( int ) );
- memcpy( boxIndex, o_boxIndex, o_x.GetSize() * sizeof( int ) );
- } else {
- boxIndex = NULL;
- }
- // we override the const on o_m here but on exit the matrix is unchanged
- m.SetData( o_m.GetNumRows(), o_m.GetNumColumns(), const_cast<float *>(o_m[0]) );
- f.Zero();
- a.Zero();
- b = o_b;
- lo = o_lo;
- hi = o_hi;
- // pointers to the rows of m
- rowPtrs = (float **) _alloca16( m.GetNumRows() * sizeof( float * ) );
- for ( i = 0; i < m.GetNumRows(); i++ ) {
- rowPtrs[i] = m[i];
- }
- // tells if a variable is at the low boundary, high boundary or inbetween
- side = (int *) _alloca16( m.GetNumRows() * sizeof( int ) );
- // index to keep track of the permutation
- permuted = (int *) _alloca16( m.GetNumRows() * sizeof( int ) );
- for ( i = 0; i < m.GetNumRows(); i++ ) {
- permuted[i] = i;
- }
- // permute input so all unbounded variables come first
- numUnbounded = 0;
- for ( i = 0; i < m.GetNumRows(); i++ ) {
- if ( lo[i] == -idMath::INFINITY && hi[i] == idMath::INFINITY ) {
- if ( numUnbounded != i ) {
- Swap( numUnbounded, i );
- }
- numUnbounded++;
- }
- }
- // permute input so all variables using the boxIndex come last
- boxStartIndex = m.GetNumRows();
- if ( boxIndex ) {
- for ( i = m.GetNumRows() - 1; i >= numUnbounded; i-- ) {
- if ( boxIndex[i] >= 0 && ( lo[i] != -idMath::INFINITY || hi[i] != idMath::INFINITY ) ) {
- boxStartIndex--;
- if ( boxStartIndex != i ) {
- Swap( boxStartIndex, i );
- }
- }
- }
- }
- // sub matrix for factorization
- clamped.SetData( m.GetNumRows(), m.GetNumColumns(), MATX_ALLOCA( m.GetNumRows() * m.GetNumColumns() ) );
- diagonal.SetData( m.GetNumRows(), VECX_ALLOCA( m.GetNumRows() ) );
- solveCache1.SetData( m.GetNumRows(), VECX_ALLOCA( m.GetNumRows() ) );
- solveCache2.SetData( m.GetNumRows(), VECX_ALLOCA( m.GetNumRows() ) );
- // all unbounded variables are clamped
- numClamped = numUnbounded;
- // if there are unbounded variables
- if ( numUnbounded ) {
- // factor and solve for unbounded variables
- if ( !FactorClamped() ) {
- idLib::common->Printf( "idLCP_Symmetric::Solve: unbounded factorization failed\n" );
- return false;
- }
- SolveClamped( f, b.ToFloatPtr() );
- // if there are no bounded variables we are done
- if ( numUnbounded == m.GetNumRows() ) {
- o_x = f; // the vector is not permuted
- return true;
- }
- }
- #ifdef IGNORE_UNSATISFIABLE_VARIABLES
- int numIgnored = 0;
- #endif
- // allocate for delta force and delta acceleration
- delta_f.SetData( m.GetNumRows(), VECX_ALLOCA( m.GetNumRows() ) );
- delta_a.SetData( m.GetNumRows(), VECX_ALLOCA( m.GetNumRows() ) );
- // solve for bounded variables
- failed = NULL;
- for ( i = numUnbounded; i < m.GetNumRows(); i++ ) {
- clampedChangeStart = 0;
- // once we hit the box start index we can initialize the low and high boundaries of the variables using the box index
- if ( i == boxStartIndex ) {
- for ( j = 0; j < boxStartIndex; j++ ) {
- o_x[permuted[j]] = f[j];
- }
- for ( j = boxStartIndex; j < m.GetNumRows(); j++ ) {
- s = o_x[boxIndex[j]];
- if ( lo[j] != -idMath::INFINITY ) {
- lo[j] = - idMath::Fabs( lo[j] * s );
- }
- if ( hi[j] != idMath::INFINITY ) {
- hi[j] = idMath::Fabs( hi[j] * s );
- }
- }
- }
- // calculate acceleration for current variable
- SIMDProcessor->Dot( dot, rowPtrs[i], f.ToFloatPtr(), i );
- a[i] = dot - b[i];
- // if already at the low boundary
- if ( lo[i] >= -LCP_BOUND_EPSILON && a[i] >= -LCP_ACCEL_EPSILON ) {
- side[i] = -1;
- continue;
- }
- // if already at the high boundary
- if ( hi[i] <= LCP_BOUND_EPSILON && a[i] <= LCP_ACCEL_EPSILON ) {
- side[i] = 1;
- continue;
- }
- // if inside the clamped region
- if ( idMath::Fabs( a[i] ) <= LCP_ACCEL_EPSILON ) {
- side[i] = 0;
- AddClamped( i, false );
- continue;
- }
- // drive the current variable into a valid region
- for ( n = 0; n < maxIterations; n++ ) {
- // direction to move
- if ( a[i] <= 0.0f ) {
- dir = 1.0f;
- } else {
- dir = -1.0f;
- }
- // calculate force delta
- CalcForceDelta( i, dir );
- // calculate acceleration delta: delta_a = m * delta_f;
- CalcAccelDelta( i );
- // maximum step we can take
- GetMaxStep( i, dir, maxStep, limit, limitSide );
- if ( maxStep <= 0.0f ) {
- #ifdef IGNORE_UNSATISFIABLE_VARIABLES
- // ignore the current variable completely
- lo[i] = hi[i] = 0.0f;
- f[i] = 0.0f;
- side[i] = -1;
- numIgnored++;
- #else
- failed = va( "invalid step size %.4f", maxStep );
- #endif
- break;
- }
- // change force
- ChangeForce( i, maxStep );
- // change acceleration
- ChangeAccel( i, maxStep );
- // clamp/unclamp the variable that limited this step
- side[limit] = limitSide;
- switch( limitSide ) {
- case 0: {
- a[limit] = 0.0f;
- AddClamped( limit, ( limit == i ) );
- break;
- }
- case -1: {
- f[limit] = lo[limit];
- if ( limit != i ) {
- RemoveClamped( limit );
- }
- break;
- }
- case 1: {
- f[limit] = hi[limit];
- if ( limit != i ) {
- RemoveClamped( limit );
- }
- break;
- }
- }
- // if the current variable limited the step we can continue with the next variable
- if ( limit == i ) {
- break;
- }
- }
- if ( n >= maxIterations ) {
- failed = va( "max iterations %d", maxIterations );
- break;
- }
- if ( failed ) {
- break;
- }
- }
- #ifdef IGNORE_UNSATISFIABLE_VARIABLES
- if ( numIgnored ) {
- if ( lcp_showFailures.GetBool() ) {
- idLib::common->Printf( "idLCP_Symmetric::Solve: %d of %d bounded variables ignored\n", numIgnored, m.GetNumRows() - numUnbounded );
- }
- }
- #endif
- // if failed clear remaining forces
- if ( failed ) {
- if ( lcp_showFailures.GetBool() ) {
- idLib::common->Printf( "idLCP_Symmetric::Solve: %s (%d of %d bounded variables ignored)\n", failed, m.GetNumRows() - i, m.GetNumRows() - numUnbounded );
- }
- for ( j = i; j < m.GetNumRows(); j++ ) {
- f[j] = 0.0f;
- }
- }
- #if defined(_DEBUG) && 0
- if ( !failed ) {
- // test whether or not the solution satisfies the complementarity conditions
- for ( i = 0; i < m.GetNumRows(); i++ ) {
- a[i] = -b[i];
- for ( j = 0; j < m.GetNumRows(); j++ ) {
- a[i] += rowPtrs[i][j] * f[j];
- }
- if ( f[i] == lo[i] ) {
- if ( lo[i] != hi[i] && a[i] < -LCP_ACCEL_EPSILON ) {
- int bah1 = 1;
- }
- } else if ( f[i] == hi[i] ) {
- if ( lo[i] != hi[i] && a[i] > LCP_ACCEL_EPSILON ) {
- int bah2 = 1;
- }
- } else if ( f[i] < lo[i] || f[i] > hi[i] || idMath::Fabs( a[i] ) > 1.0f ) {
- int bah3 = 1;
- }
- }
- }
- #endif
- // unpermute result
- for ( i = 0; i < f.GetSize(); i++ ) {
- o_x[permuted[i]] = f[i];
- }
- // unpermute original matrix
- for ( i = 0; i < m.GetNumRows(); i++ ) {
- for ( j = 0; j < m.GetNumRows(); j++ ) {
- if ( permuted[j] == i ) {
- break;
- }
- }
- if ( i != j ) {
- m.SwapColumns( i, j );
- idSwap( permuted[i], permuted[j] );
- }
- }
- return true;
- }
- //===============================================================
- //
- // idLCP
- //
- //===============================================================
- /*
- ============
- idLCP::AllocSquare
- ============
- */
- idLCP *idLCP::AllocSquare( void ) {
- idLCP *lcp = new idLCP_Square;
- lcp->SetMaxIterations( 32 );
- return lcp;
- }
- /*
- ============
- idLCP::AllocSymmetric
- ============
- */
- idLCP *idLCP::AllocSymmetric( void ) {
- idLCP *lcp = new idLCP_Symmetric;
- lcp->SetMaxIterations( 32 );
- return lcp;
- }
- /*
- ============
- idLCP::~idLCP
- ============
- */
- idLCP::~idLCP( void ) {
- }
- /*
- ============
- idLCP::SetMaxIterations
- ============
- */
- void idLCP::SetMaxIterations( int max ) {
- maxIterations = max;
- }
- /*
- ============
- idLCP::GetMaxIterations
- ============
- */
- int idLCP::GetMaxIterations( void ) {
- return maxIterations;
- }
|