// Wild Magic Source Code // David Eberly // http://www.geometrictools.com // Copyright (c) 1998-2007 // // This library is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation; either version 2.1 of the License, or (at // your option) any later version. The license is available for reading at // either of the locations: // http://www.gnu.org/copyleft/lgpl.html // http://www.geometrictools.com/License/WildMagicLicense.pdf // The license applies to versions 0 through 4 of Wild Magic. // // Version: 4.0.0 (2006/06/28) #include "Wm4FoundationPCH.h" #include "Wm4LinearSystem.h" namespace Wm4 { //---------------------------------------------------------------------------- template LinearSystem::LinearSystem () { ZeroTolerance = Math::ZERO_TOLERANCE; } //---------------------------------------------------------------------------- template bool LinearSystem::Solve2 (const Real aafA[2][2], const Real afB[2], Real afX[2]) { Real fDet = aafA[0][0]*aafA[1][1]-aafA[0][1]*aafA[1][0]; if (Math::FAbs(fDet) < ZeroTolerance) { return false; } Real fInvDet = ((Real)1.0)/fDet; afX[0] = (aafA[1][1]*afB[0]-aafA[0][1]*afB[1])*fInvDet; afX[1] = (aafA[0][0]*afB[1]-aafA[1][0]*afB[0])*fInvDet; return true; } //---------------------------------------------------------------------------- template bool LinearSystem::Solve3 (const Real aafA[3][3], const Real afB[3], Real afX[3]) { Real aafAInv[3][3]; aafAInv[0][0] = aafA[1][1]*aafA[2][2]-aafA[1][2]*aafA[2][1]; aafAInv[0][1] = aafA[0][2]*aafA[2][1]-aafA[0][1]*aafA[2][2]; aafAInv[0][2] = aafA[0][1]*aafA[1][2]-aafA[0][2]*aafA[1][1]; aafAInv[1][0] = aafA[1][2]*aafA[2][0]-aafA[1][0]*aafA[2][2]; aafAInv[1][1] = aafA[0][0]*aafA[2][2]-aafA[0][2]*aafA[2][0]; aafAInv[1][2] = aafA[0][2]*aafA[1][0]-aafA[0][0]*aafA[1][2]; aafAInv[2][0] = aafA[1][0]*aafA[2][1]-aafA[1][1]*aafA[2][0]; aafAInv[2][1] = aafA[0][1]*aafA[2][0]-aafA[0][0]*aafA[2][1]; aafAInv[2][2] = aafA[0][0]*aafA[1][1]-aafA[0][1]*aafA[1][0]; Real fDet = aafA[0][0]*aafAInv[0][0] + aafA[0][1]*aafAInv[1][0] + aafA[0][2]*aafAInv[2][0]; if (Math::FAbs(fDet) < ZeroTolerance) { return false; } Real fInvDet = ((Real)1.0)/fDet; for (int iRow = 0; iRow < 3; iRow++) { for (int iCol = 0; iCol < 3; iCol++) { aafAInv[iRow][iCol] *= fInvDet; } } afX[0] = aafAInv[0][0]*afB[0]+aafAInv[0][1]*afB[1]+aafAInv[0][2]*afB[2]; afX[1] = aafAInv[1][0]*afB[0]+aafAInv[1][1]*afB[1]+aafAInv[1][2]*afB[2]; afX[2] = aafAInv[2][0]*afB[0]+aafAInv[2][1]*afB[1]+aafAInv[2][2]*afB[2]; return true; } //---------------------------------------------------------------------------- template bool LinearSystem::Inverse (const GMatrix& rkA, GMatrix& rkInvA) { // computations are performed in-place assert(rkA.GetRows() == rkA.GetColumns()); int iSize = rkInvA.GetRows(); rkInvA = rkA; int* aiColIndex = WM4_NEW int[iSize]; int* aiRowIndex = WM4_NEW int[iSize]; bool* abPivoted = WM4_NEW bool[iSize]; memset(abPivoted,0,iSize*sizeof(bool)); int i1, i2, iRow = 0, iCol = 0; Real fSave; // elimination by full pivoting for (int i0 = 0; i0 < iSize; i0++) { // search matrix (excluding pivoted rows) for maximum absolute entry Real fMax = 0.0f; for (i1 = 0; i1 < iSize; i1++) { if (!abPivoted[i1]) { for (i2 = 0; i2 < iSize; i2++) { if (!abPivoted[i2]) { Real fAbs = Math::FAbs(rkInvA[i1][i2]); if (fAbs > fMax) { fMax = fAbs; iRow = i1; iCol = i2; } } } } } if (fMax == (Real)0.0) { // matrix is not invertible WM4_DELETE[] aiColIndex; WM4_DELETE[] aiRowIndex; WM4_DELETE[] abPivoted; return false; } abPivoted[iCol] = true; // swap rows so that A[iCol][iCol] contains the pivot entry if (iRow != iCol) { rkInvA.SwapRows(iRow,iCol); } // keep track of the permutations of the rows aiRowIndex[i0] = iRow; aiColIndex[i0] = iCol; // scale the row so that the pivot entry is 1 Real fInv = ((Real)1.0)/rkInvA[iCol][iCol]; rkInvA[iCol][iCol] = (Real)1.0; for (i2 = 0; i2 < iSize; i2++) { rkInvA[iCol][i2] *= fInv; } // zero out the pivot column locations in the other rows for (i1 = 0; i1 < iSize; i1++) { if (i1 != iCol) { fSave = rkInvA[i1][iCol]; rkInvA[i1][iCol] = (Real)0.0; for (i2 = 0; i2 < iSize; i2++) { rkInvA[i1][i2] -= rkInvA[iCol][i2]*fSave; } } } } // reorder rows so that A[][] stores the inverse of the original matrix for (i1 = iSize-1; i1 >= 0; i1--) { if (aiRowIndex[i1] != aiColIndex[i1]) { for (i2 = 0; i2 < iSize; i2++) { fSave = rkInvA[i2][aiRowIndex[i1]]; rkInvA[i2][aiRowIndex[i1]] = rkInvA[i2][aiColIndex[i1]]; rkInvA[i2][aiColIndex[i1]] = fSave; } } } WM4_DELETE[] aiColIndex; WM4_DELETE[] aiRowIndex; WM4_DELETE[] abPivoted; return true; } //---------------------------------------------------------------------------- template bool LinearSystem::Solve (const GMatrix& rkA, const Real* afB, Real* afX) { // computations are performed in-place int iSize = rkA.GetColumns(); GMatrix kInvA = rkA; size_t uiSize = iSize*sizeof(Real); System::Memcpy(afX,uiSize,afB,uiSize); int* aiColIndex = WM4_NEW int[iSize]; int* aiRowIndex = WM4_NEW int[iSize]; bool* abPivoted = WM4_NEW bool[iSize]; memset(abPivoted,0,iSize*sizeof(bool)); int i1, i2, iRow = 0, iCol = 0; Real fSave; // elimination by full pivoting for (int i0 = 0; i0 < iSize; i0++) { // search matrix (excluding pivoted rows) for maximum absolute entry Real fMax = 0.0f; for (i1 = 0; i1 < iSize; i1++) { if (!abPivoted[i1]) { for (i2 = 0; i2 < iSize; i2++) { if (!abPivoted[i2]) { Real fAbs = Math::FAbs(kInvA[i1][i2]); if (fAbs > fMax) { fMax = fAbs; iRow = i1; iCol = i2; } } } } } if (fMax == (Real)0.0) { // matrix is not invertible WM4_DELETE[] aiColIndex; WM4_DELETE[] aiRowIndex; WM4_DELETE[] abPivoted; return false; } abPivoted[iCol] = true; // swap rows so that A[iCol][iCol] contains the pivot entry if (iRow != iCol) { kInvA.SwapRows(iRow,iCol); fSave = afX[iRow]; afX[iRow] = afX[iCol]; afX[iCol] = fSave; } // keep track of the permutations of the rows aiRowIndex[i0] = iRow; aiColIndex[i0] = iCol; // scale the row so that the pivot entry is 1 Real fInv = ((Real)1.0)/kInvA[iCol][iCol]; kInvA[iCol][iCol] = (Real)1.0; for (i2 = 0; i2 < iSize; i2++) { kInvA[iCol][i2] *= fInv; } afX[iCol] *= fInv; // zero out the pivot column locations in the other rows for (i1 = 0; i1 < iSize; i1++) { if (i1 != iCol) { fSave = kInvA[i1][iCol]; kInvA[i1][iCol] = (Real)0.0; for (i2 = 0; i2 < iSize; i2++) kInvA[i1][i2] -= kInvA[iCol][i2]*fSave; afX[i1] -= afX[iCol]*fSave; } } } // reorder rows so that A[][] stores the inverse of the original matrix for (i1 = iSize-1; i1 >= 0; i1--) { if (aiRowIndex[i1] != aiColIndex[i1]) { for (i2 = 0; i2 < iSize; i2++) { fSave = kInvA[i2][aiRowIndex[i1]]; kInvA[i2][aiRowIndex[i1]] = kInvA[i2][aiColIndex[i1]]; kInvA[i2][aiColIndex[i1]] = fSave; } } } WM4_DELETE[] aiColIndex; WM4_DELETE[] aiRowIndex; WM4_DELETE[] abPivoted; return true; } //---------------------------------------------------------------------------- template bool LinearSystem::SolveTri (int iSize, Real* afA, Real* afB, Real* afC, Real* afR, Real* afU) { if (afB[0] == (Real)0.0) { return false; } Real* afD = WM4_NEW Real[iSize-1]; Real fE = afB[0]; Real fInvE = ((Real)1.0)/fE; afU[0] = afR[0]*fInvE; int i0, i1; for (i0 = 0, i1 = 1; i1 < iSize; i0++, i1++) { afD[i0] = afC[i0]*fInvE; fE = afB[i1] - afA[i0]*afD[i0]; if (fE == (Real)0.0) { WM4_DELETE[] afD; return false; } fInvE = ((Real)1.0)/fE; afU[i1] = (afR[i1] - afA[i0]*afU[i0])*fInvE; } for (i0 = iSize-1, i1 = iSize-2; i1 >= 0; i0--, i1--) { afU[i1] -= afD[i1]*afU[i0]; } WM4_DELETE[] afD; return true; } //---------------------------------------------------------------------------- template bool LinearSystem::SolveConstTri (int iSize, Real fA, Real fB, Real fC, Real* afR, Real* afU) { if (fB == (Real)0.0) { return false; } Real* afD = WM4_NEW Real[iSize-1]; Real fE = fB; Real fInvE = ((Real)1.0)/fE; afU[0] = afR[0]*fInvE; int i0, i1; for (i0 = 0, i1 = 1; i1 < iSize; i0++, i1++) { afD[i0] = fC*fInvE; fE = fB - fA*afD[i0]; if (fE == (Real)0.0) { WM4_DELETE[] afD; return false; } fInvE = ((Real)1.0)/fE; afU[i1] = (afR[i1] - fA*afU[i0])*fInvE; } for (i0 = iSize-1, i1 = iSize-2; i1 >= 0; i0--, i1--) { afU[i1] -= afD[i1]*afU[i0]; } WM4_DELETE[] afD; return true; } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // conjugate gradient methods //---------------------------------------------------------------------------- template Real LinearSystem::Dot (int iSize, const Real* afU, const Real* afV) { Real fDot = (Real)0.0; for (int i = 0; i < iSize; i++) { fDot += afU[i]*afV[i]; } return fDot; } //---------------------------------------------------------------------------- template void LinearSystem::Multiply (const GMatrix& rkA, const Real* afX, Real* afProd) { int iSize = rkA.GetRows(); memset(afProd,0,iSize*sizeof(Real)); for (int iRow = 0; iRow < iSize; iRow++) { for (int iCol = 0; iCol < iSize; iCol++) { afProd[iRow] += rkA[iRow][iCol]*afX[iCol]; } } } //---------------------------------------------------------------------------- template void LinearSystem::Multiply (int iSize, const SparseMatrix& rkA, const Real* afX, Real* afProd) { memset(afProd,0,iSize*sizeof(Real)); typename SparseMatrix::const_iterator pkIter = rkA.begin(); for (/**/; pkIter != rkA.end(); pkIter++) { int i = pkIter->first.first; int j = pkIter->first.second; Real fValue = pkIter->second; afProd[i] += fValue*afX[j]; if (i != j) { afProd[j] += fValue*afX[i]; } } } //---------------------------------------------------------------------------- template void LinearSystem::UpdateX (int iSize, Real* afX, Real fAlpha, const Real* afP) { for (int i = 0; i < iSize; i++) { afX[i] += fAlpha*afP[i]; } } //---------------------------------------------------------------------------- template void LinearSystem::UpdateR (int iSize, Real* afR, Real fAlpha, const Real* afW) { for (int i = 0; i < iSize; i++) { afR[i] -= fAlpha*afW[i]; } } //---------------------------------------------------------------------------- template void LinearSystem::UpdateP (int iSize, Real* afP, Real fBeta, const Real* afR) { for (int i = 0; i < iSize; i++) { afP[i] = afR[i] + fBeta*afP[i]; } } //---------------------------------------------------------------------------- template bool LinearSystem::SolveSymmetricCG (const GMatrix& rkA, const Real* afB, Real* afX) { // based on the algorithm in "Matrix Computations" by Golum and Van Loan assert(rkA.GetRows() == rkA.GetColumns()); int iSize = rkA.GetRows(); Real* afR = WM4_NEW Real[iSize]; Real* afP = WM4_NEW Real[iSize]; Real* afW = WM4_NEW Real[iSize]; // first iteration size_t uiSize = iSize*sizeof(Real); memset(afX,0,uiSize); System::Memcpy(afR,uiSize,afB,uiSize); Real fRho0 = Dot(iSize,afR,afR); System::Memcpy(afP,uiSize,afR,uiSize); Multiply(rkA,afP,afW); Real fAlpha = fRho0/Dot(iSize,afP,afW); UpdateX(iSize,afX,fAlpha,afP); UpdateR(iSize,afR,fAlpha,afW); Real fRho1 = Dot(iSize,afR,afR); // remaining iterations const int iMax = 1024; int i; for (i = 1; i < iMax; i++) { Real fRoot0 = Math::Sqrt(fRho1); Real fNorm = Dot(iSize,afB,afB); Real fRoot1 = Math::Sqrt(fNorm); if (fRoot0 <= ZeroTolerance*fRoot1) { break; } Real fBeta = fRho1/fRho0; UpdateP(iSize,afP,fBeta,afR); Multiply(rkA,afP,afW); fAlpha = fRho1/Dot(iSize,afP,afW); UpdateX(iSize,afX,fAlpha,afP); UpdateR(iSize,afR,fAlpha,afW); fRho0 = fRho1; fRho1 = Dot(iSize,afR,afR); } WM4_DELETE[] afW; WM4_DELETE[] afP; WM4_DELETE[] afR; return i < iMax; } //---------------------------------------------------------------------------- template bool LinearSystem::SolveSymmetricCG (int iSize, const SparseMatrix& rkA, const Real* afB, Real* afX) { // based on the algorithm in "Matrix Computations" by Golum and Van Loan Real* afR = WM4_NEW Real[iSize]; Real* afP = WM4_NEW Real[iSize]; Real* afW = WM4_NEW Real[iSize]; // first iteration size_t uiSize = iSize*sizeof(Real); memset(afX,0,uiSize); System::Memcpy(afR,uiSize,afB,uiSize); Real fRho0 = Dot(iSize,afR,afR); System::Memcpy(afP,uiSize,afR,uiSize); Multiply(iSize,rkA,afP,afW); Real fAlpha = fRho0/Dot(iSize,afP,afW); UpdateX(iSize,afX,fAlpha,afP); UpdateR(iSize,afR,fAlpha,afW); Real fRho1 = Dot(iSize,afR,afR); // remaining iterations const int iMax = 1024; int i; for (i = 1; i < iMax; i++) { Real fRoot0 = Math::Sqrt(fRho1); Real fNorm = Dot(iSize,afB,afB); Real fRoot1 = Math::Sqrt(fNorm); if (fRoot0 <= ZeroTolerance*fRoot1) { break; } Real fBeta = fRho1/fRho0; UpdateP(iSize,afP,fBeta,afR); Multiply(iSize,rkA,afP,afW); fAlpha = fRho1/Dot(iSize,afP,afW); UpdateX(iSize,afX,fAlpha,afP); UpdateR(iSize,afR,fAlpha,afW); fRho0 = fRho1; fRho1 = Dot(iSize,afR,afR); } WM4_DELETE[] afW; WM4_DELETE[] afP; WM4_DELETE[] afR; return i < iMax; } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // banded matrices //---------------------------------------------------------------------------- template bool LinearSystem::ForwardEliminate (int iReduceRow, BandedMatrix& rkA, Real* afB) { // the pivot must be nonzero in order to proceed Real fDiag = rkA(iReduceRow,iReduceRow); if (fDiag == (Real)0.0) { return false; } Real fInvDiag = ((Real)1.0)/fDiag; rkA(iReduceRow,iReduceRow) = (Real)1.0; // multiply row to be consistent with diagonal term of 1 int iColMin = iReduceRow + 1; int iColMax = iColMin + rkA.GetUBands(); if (iColMax > rkA.GetSize()) { iColMax = rkA.GetSize(); } int iCol; for (iCol = iColMin; iCol < iColMax; iCol++) { rkA(iReduceRow,iCol) *= fInvDiag; } afB[iReduceRow] *= fInvDiag; // reduce remaining rows int iRowMin = iReduceRow + 1; int iRowMax = iRowMin + rkA.GetLBands(); if (iRowMax > rkA.GetSize()) { iRowMax = rkA.GetSize(); } for (int iRow = iRowMin; iRow < iRowMax; iRow++) { Real fMult = rkA(iRow,iReduceRow); rkA(iRow,iReduceRow) = (Real)0.0; for (iCol = iColMin; iCol < iColMax; iCol++) { rkA(iRow,iCol) -= fMult*rkA(iReduceRow,iCol); } afB[iRow] -= fMult*afB[iReduceRow]; } return true; } //---------------------------------------------------------------------------- template bool LinearSystem::SolveBanded (const BandedMatrix& rkA, const Real* afB, Real* afX) { BandedMatrix kTmp = rkA; int iSize = rkA.GetSize(); size_t uiSize = iSize*sizeof(Real); System::Memcpy(afX,uiSize,afB,uiSize); // forward elimination int iRow; for (iRow = 0; iRow < iSize; iRow++) { if (!ForwardEliminate(iRow,kTmp,afX)) { return false; } } // backward substitution for (iRow = iSize-2; iRow >= 0; iRow--) { int iColMin = iRow + 1; int iColMax = iColMin + kTmp.GetUBands(); if (iColMax > iSize) { iColMax = iSize; } for (int iCol = iColMin; iCol < iColMax; iCol++) { afX[iRow] -= kTmp(iRow,iCol)*afX[iCol]; } } return true; } //---------------------------------------------------------------------------- template bool LinearSystem::ForwardEliminate (int iReduceRow, BandedMatrix& rkA, GMatrix& rkB) { // the pivot must be nonzero in order to proceed Real fDiag = rkA(iReduceRow,iReduceRow); if (fDiag == (Real)0.0) { return false; } Real fInvDiag = ((Real)1.0)/fDiag; rkA(iReduceRow,iReduceRow) = (Real)1.0; // multiply row to be consistent with diagonal term of 1 int iColMin = iReduceRow + 1; int iColMax = iColMin + rkA.GetUBands(); if (iColMax > rkA.GetSize()) { iColMax = rkA.GetSize(); } int iCol; for (iCol = iColMin; iCol < iColMax; iCol++) { rkA(iReduceRow,iCol) *= fInvDiag; } for (iCol = 0; iCol <= iReduceRow; iCol++) { rkB(iReduceRow,iCol) *= fInvDiag; } // reduce remaining rows int iRowMin = iReduceRow + 1; int iRowMax = iRowMin + rkA.GetLBands(); if (iRowMax > rkA.GetSize()) { iRowMax = rkA.GetSize(); } for (int iRow = iRowMin; iRow < iRowMax; iRow++) { Real fMult = rkA(iRow,iReduceRow); rkA(iRow,iReduceRow) = (Real)0.0; for (iCol = iColMin; iCol < iColMax; iCol++) { rkA(iRow,iCol) -= fMult*rkA(iReduceRow,iCol); } for (iCol = 0; iCol <= iReduceRow; iCol++) { rkB(iRow,iCol) -= fMult*rkB(iReduceRow,iCol); } } return true; } //---------------------------------------------------------------------------- template void LinearSystem::BackwardEliminate (int iReduceRow, BandedMatrix& rkA, GMatrix& rkB) { int iRowMax = iReduceRow - 1; int iRowMin = iReduceRow - rkA.GetUBands(); if (iRowMin < 0) { iRowMin = 0; } for (int iRow = iRowMax; iRow >= iRowMin; iRow--) { Real fMult = rkA(iRow,iReduceRow); rkA(iRow,iReduceRow) = (Real)0.0; for (int iCol = 0; iCol < rkB.GetColumns(); iCol++) { rkB(iRow,iCol) -= fMult*rkB(iReduceRow,iCol); } } } //---------------------------------------------------------------------------- template bool LinearSystem::Invert (const BandedMatrix& rkA, GMatrix& rkInvA) { int iSize = rkA.GetSize(); BandedMatrix kTmp = rkA; int iRow; for (iRow = 0; iRow < iSize; iRow++) { for (int iCol = 0; iCol < iSize; iCol++) { if (iRow != iCol) { rkInvA(iRow,iCol) = (Real)0.0; } else { rkInvA(iRow,iRow) = (Real)1.0; } } } // forward elimination for (iRow = 0; iRow < iSize; iRow++) { if (!ForwardEliminate(iRow,kTmp,rkInvA)) { return false; } } // backward elimination for (iRow = iSize-1; iRow >= 1; iRow--) { BackwardEliminate(iRow,kTmp,rkInvA); } return true; } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // explicit instantiation //---------------------------------------------------------------------------- template WM4_FOUNDATION_ITEM class LinearSystem; template WM4_FOUNDATION_ITEM class LinearSystem; //---------------------------------------------------------------------------- }