GteGaussianElimination.h
Go to the documentation of this file.
1 // David Eberly, Geometric Tools, Redmond WA 98052
2 // Copyright (c) 1998-2017
3 // Distributed under the Boost Software License, Version 1.0.
4 // http://www.boost.org/LICENSE_1_0.txt
5 // http://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
6 // File Version: 3.0.0 (2016/06/19)
7 
8 #pragma once
9 
11 #include <LowLevel/GteLogger.h>
12 #include <LowLevel/GteWrapper.h>
13 #include <cmath>
14 #include <cstring>
15 #include <vector>
16 
17 // The input matrix M must be NxN. The storage convention for element lookup
18 // is determined by GTE_USE_ROW_MAJOR or GTE_USE_COL_MAJOR, whichever is
19 // active. If you want the inverse of M, pass a nonnull pointer inverseM;
20 // this matrix must also be NxN and use the same storage convention as M. If
21 // you do not want the inverse of M, pass a nullptr for inverseM. If you want
22 // to solve M*X = B for X, where X and B are Nx1, pass nonnull pointers for B
23 // and X. If you want to solve M*Y = C for Y, where X and C are NxK, pass
24 // nonnull pointers for C and Y and pass K to numCols. In all cases, pass
25 // N to numRows.
26 
27 namespace gte
28 {
29 
30 template <typename Real>
32 {
33 public:
34  bool operator()(int numRows,
35  Real const* M, Real* inverseM, Real& determinant,
36  Real const* B, Real* X,
37  Real const* C, int numCols, Real* Y) const;
38 
39 private:
40  // Support for copying source to target or to set target to zero. If
41  // source is nullptr, then target is set to zero; otherwise source is
42  // copied to target. This function hides the type traits used to
43  // determine whether Real is native floating-point or otherwise (such
44  // as BSNumber or BSRational).
45  void Set(int numElements, Real const* source, Real* target) const;
46 };
47 
48 
49 template <typename Real>
50 bool GaussianElimination<Real>::operator()(int numRows, Real const* M,
51  Real* inverseM, Real& determinant, Real const* B, Real* X, Real const* C,
52  int numCols, Real* Y) const
53 {
54  if (numRows <= 0 || !M
55  || ((B != nullptr) != (X != nullptr))
56  || ((C != nullptr) != (Y != nullptr))
57  || (C != nullptr && numCols < 1))
58  {
59  LogError("Invalid input.");
60  return false;
61  }
62 
63  int numElements = numRows * numRows;
64  bool wantInverse = (inverseM != nullptr);
65  std::vector<Real> localInverseM;
66  if (!wantInverse)
67  {
68  localInverseM.resize(numElements);
69  inverseM = localInverseM.data();
70  }
71  Set(numElements, M, inverseM);
72 
73  if (B)
74  {
75  Set(numRows, B, X);
76  }
77 
78  if (C)
79  {
80  Set(numRows * numCols, C, Y);
81  }
82 
83 #if defined(GTE_USE_ROW_MAJOR)
84  LexicoArray2<true, Real> matInvM(numRows, numRows, inverseM);
85  LexicoArray2<true, Real> matY(numRows, numCols, Y);
86 #else
87  LexicoArray2<false, Real> matInvM(numRows, numRows, inverseM);
88  LexicoArray2<false, Real> matY(numRows, numCols, Y);
89 #endif
90 
91  std::vector<int> colIndex(numRows), rowIndex(numRows), pivoted(numRows);
92  std::fill(pivoted.begin(), pivoted.end(), 0);
93 
94  Real const zero = (Real)0;
95  Real const one = (Real)1;
96  bool odd = false;
97  determinant = one;
98 
99  // Elimination by full pivoting.
100  int i1, i2, row = 0, col = 0;
101  for (int i0 = 0; i0 < numRows; ++i0)
102  {
103  // Search matrix (excluding pivoted rows) for maximum absolute entry.
104  Real maxValue = zero;
105  for (i1 = 0; i1 < numRows; ++i1)
106  {
107  if (!pivoted[i1])
108  {
109  for (i2 = 0; i2 < numRows; ++i2)
110  {
111  if (!pivoted[i2])
112  {
113  Real absValue = std::abs(matInvM(i1, i2));
114  if (absValue > maxValue)
115  {
116  maxValue = absValue;
117  row = i1;
118  col = i2;
119  }
120  }
121  }
122  }
123  }
124 
125  if (maxValue == zero)
126  {
127  // The matrix is not invertible.
128  if (wantInverse)
129  {
130  Set(numElements, nullptr, inverseM);
131  }
132  determinant = zero;
133 
134  if (B)
135  {
136  Set(numRows, nullptr, X);
137  }
138 
139  if (C)
140  {
141  Set(numRows * numCols, nullptr, Y);
142  }
143  return false;
144  }
145 
146  pivoted[col] = true;
147 
148  // Swap rows so that the pivot entry is in row 'col'.
149  if (row != col)
150  {
151  odd = !odd;
152  for (int i = 0; i < numRows; ++i)
153  {
154  std::swap(matInvM(row, i), matInvM(col, i));
155  }
156 
157  if (B)
158  {
159  std::swap(X[row], X[col]);
160  }
161 
162  if (C)
163  {
164  for (int i = 0; i < numCols; ++i)
165  {
166  std::swap(matY(row, i), matY(col, i));
167  }
168  }
169  }
170 
171  // Keep track of the permutations of the rows.
172  rowIndex[i0] = row;
173  colIndex[i0] = col;
174 
175  // Scale the row so that the pivot entry is 1.
176  Real diagonal = matInvM(col, col);
177  determinant *= diagonal;
178  Real inv = one / diagonal;
179  matInvM(col, col) = one;
180  for (i2 = 0; i2 < numRows; ++i2)
181  {
182  matInvM(col, i2) *= inv;
183  }
184 
185  if (B)
186  {
187  X[col] *= inv;
188  }
189 
190  if (C)
191  {
192  for (i2 = 0; i2 < numCols; ++i2)
193  {
194  matY(col, i2) *= inv;
195  }
196  }
197 
198  // Zero out the pivot column locations in the other rows.
199  for (i1 = 0; i1 < numRows; ++i1)
200  {
201  if (i1 != col)
202  {
203  Real save = matInvM(i1, col);
204  matInvM(i1, col) = zero;
205  for (i2 = 0; i2 < numRows; ++i2)
206  {
207  matInvM(i1, i2) -= matInvM(col, i2) * save;
208  }
209 
210  if (B)
211  {
212  X[i1] -= X[col] * save;
213  }
214 
215  if (C)
216  {
217  for (i2 = 0; i2 < numCols; ++i2)
218  {
219  matY(i1, i2) -= matY(col, i2) * save;
220  }
221  }
222  }
223  }
224  }
225 
226  if (wantInverse)
227  {
228  // Reorder rows to undo any permutations in Gaussian elimination.
229  for (i1 = numRows - 1; i1 >= 0; --i1)
230  {
231  if (rowIndex[i1] != colIndex[i1])
232  {
233  for (i2 = 0; i2 < numRows; ++i2)
234  {
235  std::swap(matInvM(i2, rowIndex[i1]),
236  matInvM(i2, colIndex[i1]));
237  }
238  }
239  }
240  }
241 
242  if (odd)
243  {
244  determinant = -determinant;
245  }
246 
247  return true;
248 }
249 
250 template <typename Real>
251 void GaussianElimination<Real>::Set(int numElements, Real const* source,
252  Real* target) const
253 {
254  if (std::is_floating_point<Real>() == std::true_type())
255  {
256  // Fast set/copy for native floating-point.
257  size_t numBytes = numElements * sizeof(Real);
258  if (source)
259  {
260  Memcpy(target, source, numBytes);
261  }
262  else
263  {
264  memset(target, 0, numBytes);
265  }
266  }
267  else
268  {
269  // The inputs are not std containers, so ensure assignment works
270  // correctly.
271  if (source)
272  {
273  for (int i = 0; i < numElements; ++i)
274  {
275  target[i] = source[i];
276  }
277  }
278  else
279  {
280  Real const zero = (Real)0;
281  for (int i = 0; i < numElements; ++i)
282  {
283  target[i] = zero;
284  }
285  }
286  }
287 }
288 
289 
290 }
gte::BSNumber< UIntegerType > abs(gte::BSNumber< UIntegerType > const &number)
Definition: GteBSNumber.h:966
GLenum target
Definition: glcorearb.h:1662
void Set(int numElements, Real const *source, Real *target) const
GLsizei GLsizei GLchar * source
Definition: glcorearb.h:798
GLenum GLenum GLsizei void * row
Definition: glext.h:2725
#define LogError(message)
Definition: GteLogger.h:92
void Memcpy(void *target, void const *source, size_t count)
Definition: GteWrapper.cpp:16
bool operator()(int numRows, Real const *M, Real *inverseM, Real &determinant, Real const *B, Real *X, Real const *C, int numCols, Real *Y) const


geometric_tools_engine
Author(s): Yijiang Huang
autogenerated on Thu Jul 18 2019 03:59:59