1
// Wild Magic Source Code
3
// http://www.geometrictools.com
4
// Copyright (c) 1998-2007
6
// This library is free software; you can redistribute it and/or modify it
7
// under the terms of the GNU Lesser General Public License as published by
8
// the Free Software Foundation; either version 2.1 of the License, or (at
9
// your option) any later version. The license is available for reading at
10
// either of the locations:
11
// http://www.gnu.org/copyleft/lgpl.html
12
// http://www.geometrictools.com/License/WildMagicLicense.pdf
13
// The license applies to versions 0 through 4 of Wild Magic.
15
// Version: 4.0.0 (2006/06/28)
17
#include "Wm4FoundationPCH.h"
18
#include "Wm4LinearSystem.h"
22
//----------------------------------------------------------------------------
24
LinearSystem<Real>::LinearSystem ()
26
ZeroTolerance = Math<Real>::ZERO_TOLERANCE;
28
//----------------------------------------------------------------------------
30
bool LinearSystem<Real>::Solve2 (const Real aafA[2][2], const Real afB[2],
33
Real fDet = aafA[0][0]*aafA[1][1]-aafA[0][1]*aafA[1][0];
34
if (Math<Real>::FAbs(fDet) < ZeroTolerance)
39
Real fInvDet = ((Real)1.0)/fDet;
40
afX[0] = (aafA[1][1]*afB[0]-aafA[0][1]*afB[1])*fInvDet;
41
afX[1] = (aafA[0][0]*afB[1]-aafA[1][0]*afB[0])*fInvDet;
44
//----------------------------------------------------------------------------
46
bool LinearSystem<Real>::Solve3 (const Real aafA[3][3], const Real afB[3],
50
aafAInv[0][0] = aafA[1][1]*aafA[2][2]-aafA[1][2]*aafA[2][1];
51
aafAInv[0][1] = aafA[0][2]*aafA[2][1]-aafA[0][1]*aafA[2][2];
52
aafAInv[0][2] = aafA[0][1]*aafA[1][2]-aafA[0][2]*aafA[1][1];
53
aafAInv[1][0] = aafA[1][2]*aafA[2][0]-aafA[1][0]*aafA[2][2];
54
aafAInv[1][1] = aafA[0][0]*aafA[2][2]-aafA[0][2]*aafA[2][0];
55
aafAInv[1][2] = aafA[0][2]*aafA[1][0]-aafA[0][0]*aafA[1][2];
56
aafAInv[2][0] = aafA[1][0]*aafA[2][1]-aafA[1][1]*aafA[2][0];
57
aafAInv[2][1] = aafA[0][1]*aafA[2][0]-aafA[0][0]*aafA[2][1];
58
aafAInv[2][2] = aafA[0][0]*aafA[1][1]-aafA[0][1]*aafA[1][0];
59
Real fDet = aafA[0][0]*aafAInv[0][0] + aafA[0][1]*aafAInv[1][0] +
60
aafA[0][2]*aafAInv[2][0];
62
if (Math<Real>::FAbs(fDet) < ZeroTolerance)
67
Real fInvDet = ((Real)1.0)/fDet;
68
for (int iRow = 0; iRow < 3; iRow++)
70
for (int iCol = 0; iCol < 3; iCol++)
72
aafAInv[iRow][iCol] *= fInvDet;
76
afX[0] = aafAInv[0][0]*afB[0]+aafAInv[0][1]*afB[1]+aafAInv[0][2]*afB[2];
77
afX[1] = aafAInv[1][0]*afB[0]+aafAInv[1][1]*afB[1]+aafAInv[1][2]*afB[2];
78
afX[2] = aafAInv[2][0]*afB[0]+aafAInv[2][1]*afB[1]+aafAInv[2][2]*afB[2];
81
//----------------------------------------------------------------------------
83
bool LinearSystem<Real>::Inverse (const GMatrix<Real>& rkA,
84
GMatrix<Real>& rkInvA)
86
// computations are performed in-place
87
assert(rkA.GetRows() == rkA.GetColumns());
88
int iSize = rkInvA.GetRows();
91
int* aiColIndex = WM4_NEW int[iSize];
92
int* aiRowIndex = WM4_NEW int[iSize];
93
bool* abPivoted = WM4_NEW bool[iSize];
94
memset(abPivoted,0,iSize*sizeof(bool));
96
int i1, i2, iRow = 0, iCol = 0;
99
// elimination by full pivoting
100
for (int i0 = 0; i0 < iSize; i0++)
102
// search matrix (excluding pivoted rows) for maximum absolute entry
104
for (i1 = 0; i1 < iSize; i1++)
108
for (i2 = 0; i2 < iSize; i2++)
112
Real fAbs = Math<Real>::FAbs(rkInvA[i1][i2]);
124
if (fMax == (Real)0.0)
126
// matrix is not invertible
127
WM4_DELETE[] aiColIndex;
128
WM4_DELETE[] aiRowIndex;
129
WM4_DELETE[] abPivoted;
133
abPivoted[iCol] = true;
135
// swap rows so that A[iCol][iCol] contains the pivot entry
138
rkInvA.SwapRows(iRow,iCol);
141
// keep track of the permutations of the rows
142
aiRowIndex[i0] = iRow;
143
aiColIndex[i0] = iCol;
145
// scale the row so that the pivot entry is 1
146
Real fInv = ((Real)1.0)/rkInvA[iCol][iCol];
147
rkInvA[iCol][iCol] = (Real)1.0;
148
for (i2 = 0; i2 < iSize; i2++)
150
rkInvA[iCol][i2] *= fInv;
153
// zero out the pivot column locations in the other rows
154
for (i1 = 0; i1 < iSize; i1++)
158
fSave = rkInvA[i1][iCol];
159
rkInvA[i1][iCol] = (Real)0.0;
160
for (i2 = 0; i2 < iSize; i2++)
162
rkInvA[i1][i2] -= rkInvA[iCol][i2]*fSave;
168
// reorder rows so that A[][] stores the inverse of the original matrix
169
for (i1 = iSize-1; i1 >= 0; i1--)
171
if (aiRowIndex[i1] != aiColIndex[i1])
173
for (i2 = 0; i2 < iSize; i2++)
175
fSave = rkInvA[i2][aiRowIndex[i1]];
176
rkInvA[i2][aiRowIndex[i1]] = rkInvA[i2][aiColIndex[i1]];
177
rkInvA[i2][aiColIndex[i1]] = fSave;
182
WM4_DELETE[] aiColIndex;
183
WM4_DELETE[] aiRowIndex;
184
WM4_DELETE[] abPivoted;
187
//----------------------------------------------------------------------------
188
template <class Real>
189
bool LinearSystem<Real>::Solve (const GMatrix<Real>& rkA, const Real* afB,
192
// computations are performed in-place
193
int iSize = rkA.GetColumns();
194
GMatrix<Real> kInvA = rkA;
195
size_t uiSize = iSize*sizeof(Real);
196
System::Memcpy(afX,uiSize,afB,uiSize);
198
int* aiColIndex = WM4_NEW int[iSize];
199
int* aiRowIndex = WM4_NEW int[iSize];
200
bool* abPivoted = WM4_NEW bool[iSize];
201
memset(abPivoted,0,iSize*sizeof(bool));
203
int i1, i2, iRow = 0, iCol = 0;
206
// elimination by full pivoting
207
for (int i0 = 0; i0 < iSize; i0++)
209
// search matrix (excluding pivoted rows) for maximum absolute entry
211
for (i1 = 0; i1 < iSize; i1++)
215
for (i2 = 0; i2 < iSize; i2++)
219
Real fAbs = Math<Real>::FAbs(kInvA[i1][i2]);
231
if (fMax == (Real)0.0)
233
// matrix is not invertible
234
WM4_DELETE[] aiColIndex;
235
WM4_DELETE[] aiRowIndex;
236
WM4_DELETE[] abPivoted;
240
abPivoted[iCol] = true;
242
// swap rows so that A[iCol][iCol] contains the pivot entry
245
kInvA.SwapRows(iRow,iCol);
248
afX[iRow] = afX[iCol];
252
// keep track of the permutations of the rows
253
aiRowIndex[i0] = iRow;
254
aiColIndex[i0] = iCol;
256
// scale the row so that the pivot entry is 1
257
Real fInv = ((Real)1.0)/kInvA[iCol][iCol];
258
kInvA[iCol][iCol] = (Real)1.0;
259
for (i2 = 0; i2 < iSize; i2++)
261
kInvA[iCol][i2] *= fInv;
265
// zero out the pivot column locations in the other rows
266
for (i1 = 0; i1 < iSize; i1++)
270
fSave = kInvA[i1][iCol];
271
kInvA[i1][iCol] = (Real)0.0;
272
for (i2 = 0; i2 < iSize; i2++)
273
kInvA[i1][i2] -= kInvA[iCol][i2]*fSave;
274
afX[i1] -= afX[iCol]*fSave;
279
// reorder rows so that A[][] stores the inverse of the original matrix
280
for (i1 = iSize-1; i1 >= 0; i1--)
282
if (aiRowIndex[i1] != aiColIndex[i1])
284
for (i2 = 0; i2 < iSize; i2++)
286
fSave = kInvA[i2][aiRowIndex[i1]];
287
kInvA[i2][aiRowIndex[i1]] = kInvA[i2][aiColIndex[i1]];
288
kInvA[i2][aiColIndex[i1]] = fSave;
293
WM4_DELETE[] aiColIndex;
294
WM4_DELETE[] aiRowIndex;
295
WM4_DELETE[] abPivoted;
298
//----------------------------------------------------------------------------
299
template <class Real>
300
bool LinearSystem<Real>::SolveTri (int iSize, Real* afA, Real* afB,
301
Real* afC, Real* afR, Real* afU)
303
if (afB[0] == (Real)0.0)
308
Real* afD = WM4_NEW Real[iSize-1];
310
Real fInvE = ((Real)1.0)/fE;
311
afU[0] = afR[0]*fInvE;
314
for (i0 = 0, i1 = 1; i1 < iSize; i0++, i1++)
316
afD[i0] = afC[i0]*fInvE;
317
fE = afB[i1] - afA[i0]*afD[i0];
323
fInvE = ((Real)1.0)/fE;
324
afU[i1] = (afR[i1] - afA[i0]*afU[i0])*fInvE;
327
for (i0 = iSize-1, i1 = iSize-2; i1 >= 0; i0--, i1--)
329
afU[i1] -= afD[i1]*afU[i0];
335
//----------------------------------------------------------------------------
336
template <class Real>
337
bool LinearSystem<Real>::SolveConstTri (int iSize, Real fA, Real fB, Real fC,
338
Real* afR, Real* afU)
345
Real* afD = WM4_NEW Real[iSize-1];
347
Real fInvE = ((Real)1.0)/fE;
348
afU[0] = afR[0]*fInvE;
351
for (i0 = 0, i1 = 1; i1 < iSize; i0++, i1++)
354
fE = fB - fA*afD[i0];
360
fInvE = ((Real)1.0)/fE;
361
afU[i1] = (afR[i1] - fA*afU[i0])*fInvE;
363
for (i0 = iSize-1, i1 = iSize-2; i1 >= 0; i0--, i1--)
365
afU[i1] -= afD[i1]*afU[i0];
371
//----------------------------------------------------------------------------
373
//----------------------------------------------------------------------------
374
// conjugate gradient methods
375
//----------------------------------------------------------------------------
376
template <class Real>
377
Real LinearSystem<Real>::Dot (int iSize, const Real* afU, const Real* afV)
379
Real fDot = (Real)0.0;
380
for (int i = 0; i < iSize; i++)
382
fDot += afU[i]*afV[i];
386
//----------------------------------------------------------------------------
387
template <class Real>
388
void LinearSystem<Real>::Multiply (const GMatrix<Real>& rkA, const Real* afX,
391
int iSize = rkA.GetRows();
392
memset(afProd,0,iSize*sizeof(Real));
393
for (int iRow = 0; iRow < iSize; iRow++)
395
for (int iCol = 0; iCol < iSize; iCol++)
397
afProd[iRow] += rkA[iRow][iCol]*afX[iCol];
401
//----------------------------------------------------------------------------
402
template <class Real>
403
void LinearSystem<Real>::Multiply (int iSize, const SparseMatrix& rkA,
404
const Real* afX, Real* afProd)
406
memset(afProd,0,iSize*sizeof(Real));
407
typename SparseMatrix::const_iterator pkIter = rkA.begin();
408
for (/**/; pkIter != rkA.end(); pkIter++)
410
int i = pkIter->first.first;
411
int j = pkIter->first.second;
412
Real fValue = pkIter->second;
413
afProd[i] += fValue*afX[j];
416
afProd[j] += fValue*afX[i];
420
//----------------------------------------------------------------------------
421
template <class Real>
422
void LinearSystem<Real>::UpdateX (int iSize, Real* afX, Real fAlpha,
425
for (int i = 0; i < iSize; i++)
427
afX[i] += fAlpha*afP[i];
430
//----------------------------------------------------------------------------
431
template <class Real>
432
void LinearSystem<Real>::UpdateR (int iSize, Real* afR, Real fAlpha,
435
for (int i = 0; i < iSize; i++)
437
afR[i] -= fAlpha*afW[i];
440
//----------------------------------------------------------------------------
441
template <class Real>
442
void LinearSystem<Real>::UpdateP (int iSize, Real* afP, Real fBeta,
445
for (int i = 0; i < iSize; i++)
447
afP[i] = afR[i] + fBeta*afP[i];
450
//----------------------------------------------------------------------------
451
template <class Real>
452
bool LinearSystem<Real>::SolveSymmetricCG (const GMatrix<Real>& rkA,
453
const Real* afB, Real* afX)
455
// based on the algorithm in "Matrix Computations" by Golum and Van Loan
456
assert(rkA.GetRows() == rkA.GetColumns());
457
int iSize = rkA.GetRows();
458
Real* afR = WM4_NEW Real[iSize];
459
Real* afP = WM4_NEW Real[iSize];
460
Real* afW = WM4_NEW Real[iSize];
463
size_t uiSize = iSize*sizeof(Real);
464
memset(afX,0,uiSize);
465
System::Memcpy(afR,uiSize,afB,uiSize);
466
Real fRho0 = Dot(iSize,afR,afR);
467
System::Memcpy(afP,uiSize,afR,uiSize);
468
Multiply(rkA,afP,afW);
469
Real fAlpha = fRho0/Dot(iSize,afP,afW);
470
UpdateX(iSize,afX,fAlpha,afP);
471
UpdateR(iSize,afR,fAlpha,afW);
472
Real fRho1 = Dot(iSize,afR,afR);
474
// remaining iterations
475
const int iMax = 1024;
477
for (i = 1; i < iMax; i++)
479
Real fRoot0 = Math<Real>::Sqrt(fRho1);
480
Real fNorm = Dot(iSize,afB,afB);
481
Real fRoot1 = Math<Real>::Sqrt(fNorm);
482
if (fRoot0 <= ZeroTolerance*fRoot1)
487
Real fBeta = fRho1/fRho0;
488
UpdateP(iSize,afP,fBeta,afR);
489
Multiply(rkA,afP,afW);
490
fAlpha = fRho1/Dot(iSize,afP,afW);
491
UpdateX(iSize,afX,fAlpha,afP);
492
UpdateR(iSize,afR,fAlpha,afW);
494
fRho1 = Dot(iSize,afR,afR);
503
//----------------------------------------------------------------------------
504
template <class Real>
505
bool LinearSystem<Real>::SolveSymmetricCG (int iSize,
506
const SparseMatrix& rkA, const Real* afB, Real* afX)
508
// based on the algorithm in "Matrix Computations" by Golum and Van Loan
509
Real* afR = WM4_NEW Real[iSize];
510
Real* afP = WM4_NEW Real[iSize];
511
Real* afW = WM4_NEW Real[iSize];
514
size_t uiSize = iSize*sizeof(Real);
515
memset(afX,0,uiSize);
516
System::Memcpy(afR,uiSize,afB,uiSize);
517
Real fRho0 = Dot(iSize,afR,afR);
518
System::Memcpy(afP,uiSize,afR,uiSize);
519
Multiply(iSize,rkA,afP,afW);
520
Real fAlpha = fRho0/Dot(iSize,afP,afW);
521
UpdateX(iSize,afX,fAlpha,afP);
522
UpdateR(iSize,afR,fAlpha,afW);
523
Real fRho1 = Dot(iSize,afR,afR);
525
// remaining iterations
526
const int iMax = 1024;
528
for (i = 1; i < iMax; i++)
530
Real fRoot0 = Math<Real>::Sqrt(fRho1);
531
Real fNorm = Dot(iSize,afB,afB);
532
Real fRoot1 = Math<Real>::Sqrt(fNorm);
533
if (fRoot0 <= ZeroTolerance*fRoot1)
538
Real fBeta = fRho1/fRho0;
539
UpdateP(iSize,afP,fBeta,afR);
540
Multiply(iSize,rkA,afP,afW);
541
fAlpha = fRho1/Dot(iSize,afP,afW);
542
UpdateX(iSize,afX,fAlpha,afP);
543
UpdateR(iSize,afR,fAlpha,afW);
545
fRho1 = Dot(iSize,afR,afR);
554
//----------------------------------------------------------------------------
556
//----------------------------------------------------------------------------
558
//----------------------------------------------------------------------------
559
template <class Real>
560
bool LinearSystem<Real>::ForwardEliminate (int iReduceRow,
561
BandedMatrix<Real>& rkA, Real* afB)
563
// the pivot must be nonzero in order to proceed
564
Real fDiag = rkA(iReduceRow,iReduceRow);
565
if (fDiag == (Real)0.0)
570
Real fInvDiag = ((Real)1.0)/fDiag;
571
rkA(iReduceRow,iReduceRow) = (Real)1.0;
573
// multiply row to be consistent with diagonal term of 1
574
int iColMin = iReduceRow + 1;
575
int iColMax = iColMin + rkA.GetUBands();
576
if (iColMax > rkA.GetSize())
578
iColMax = rkA.GetSize();
582
for (iCol = iColMin; iCol < iColMax; iCol++)
584
rkA(iReduceRow,iCol) *= fInvDiag;
587
afB[iReduceRow] *= fInvDiag;
589
// reduce remaining rows
590
int iRowMin = iReduceRow + 1;
591
int iRowMax = iRowMin + rkA.GetLBands();
592
if (iRowMax > rkA.GetSize())
594
iRowMax = rkA.GetSize();
597
for (int iRow = iRowMin; iRow < iRowMax; iRow++)
599
Real fMult = rkA(iRow,iReduceRow);
600
rkA(iRow,iReduceRow) = (Real)0.0;
601
for (iCol = iColMin; iCol < iColMax; iCol++)
603
rkA(iRow,iCol) -= fMult*rkA(iReduceRow,iCol);
605
afB[iRow] -= fMult*afB[iReduceRow];
610
//----------------------------------------------------------------------------
611
template <class Real>
612
bool LinearSystem<Real>::SolveBanded (const BandedMatrix<Real>& rkA,
613
const Real* afB, Real* afX)
615
BandedMatrix<Real> kTmp = rkA;
616
int iSize = rkA.GetSize();
617
size_t uiSize = iSize*sizeof(Real);
618
System::Memcpy(afX,uiSize,afB,uiSize);
620
// forward elimination
622
for (iRow = 0; iRow < iSize; iRow++)
624
if (!ForwardEliminate(iRow,kTmp,afX))
630
// backward substitution
631
for (iRow = iSize-2; iRow >= 0; iRow--)
633
int iColMin = iRow + 1;
634
int iColMax = iColMin + kTmp.GetUBands();
639
for (int iCol = iColMin; iCol < iColMax; iCol++)
641
afX[iRow] -= kTmp(iRow,iCol)*afX[iCol];
647
//----------------------------------------------------------------------------
648
template <class Real>
649
bool LinearSystem<Real>::ForwardEliminate (int iReduceRow,
650
BandedMatrix<Real>& rkA, GMatrix<Real>& rkB)
652
// the pivot must be nonzero in order to proceed
653
Real fDiag = rkA(iReduceRow,iReduceRow);
654
if (fDiag == (Real)0.0)
659
Real fInvDiag = ((Real)1.0)/fDiag;
660
rkA(iReduceRow,iReduceRow) = (Real)1.0;
662
// multiply row to be consistent with diagonal term of 1
663
int iColMin = iReduceRow + 1;
664
int iColMax = iColMin + rkA.GetUBands();
665
if (iColMax > rkA.GetSize())
667
iColMax = rkA.GetSize();
671
for (iCol = iColMin; iCol < iColMax; iCol++)
673
rkA(iReduceRow,iCol) *= fInvDiag;
675
for (iCol = 0; iCol <= iReduceRow; iCol++)
677
rkB(iReduceRow,iCol) *= fInvDiag;
680
// reduce remaining rows
681
int iRowMin = iReduceRow + 1;
682
int iRowMax = iRowMin + rkA.GetLBands();
683
if (iRowMax > rkA.GetSize())
685
iRowMax = rkA.GetSize();
688
for (int iRow = iRowMin; iRow < iRowMax; iRow++)
690
Real fMult = rkA(iRow,iReduceRow);
691
rkA(iRow,iReduceRow) = (Real)0.0;
692
for (iCol = iColMin; iCol < iColMax; iCol++)
694
rkA(iRow,iCol) -= fMult*rkA(iReduceRow,iCol);
696
for (iCol = 0; iCol <= iReduceRow; iCol++)
698
rkB(iRow,iCol) -= fMult*rkB(iReduceRow,iCol);
704
//----------------------------------------------------------------------------
705
template <class Real>
706
void LinearSystem<Real>::BackwardEliminate (int iReduceRow,
707
BandedMatrix<Real>& rkA, GMatrix<Real>& rkB)
709
int iRowMax = iReduceRow - 1;
710
int iRowMin = iReduceRow - rkA.GetUBands();
716
for (int iRow = iRowMax; iRow >= iRowMin; iRow--)
718
Real fMult = rkA(iRow,iReduceRow);
719
rkA(iRow,iReduceRow) = (Real)0.0;
720
for (int iCol = 0; iCol < rkB.GetColumns(); iCol++)
722
rkB(iRow,iCol) -= fMult*rkB(iReduceRow,iCol);
726
//----------------------------------------------------------------------------
727
template <class Real>
728
bool LinearSystem<Real>::Invert (const BandedMatrix<Real>& rkA,
729
GMatrix<Real>& rkInvA)
731
int iSize = rkA.GetSize();
732
BandedMatrix<Real> kTmp = rkA;
734
for (iRow = 0; iRow < iSize; iRow++)
736
for (int iCol = 0; iCol < iSize; iCol++)
740
rkInvA(iRow,iCol) = (Real)0.0;
744
rkInvA(iRow,iRow) = (Real)1.0;
749
// forward elimination
750
for (iRow = 0; iRow < iSize; iRow++)
752
if (!ForwardEliminate(iRow,kTmp,rkInvA))
758
// backward elimination
759
for (iRow = iSize-1; iRow >= 1; iRow--)
761
BackwardEliminate(iRow,kTmp,rkInvA);
766
//----------------------------------------------------------------------------
768
//----------------------------------------------------------------------------
769
// explicit instantiation
770
//----------------------------------------------------------------------------
771
template WM4_FOUNDATION_ITEM
772
class LinearSystem<float>;
774
template WM4_FOUNDATION_ITEM
775
class LinearSystem<double>;
776
//----------------------------------------------------------------------------