GteQuarticRootsQR.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 
10 #include "GteCubicRootsQR.h"
11 
12 // An implementation of the QR algorithm described in "Matrix Computations,
13 // 2nd edition" by G. H. Golub and C. F. Van Loan, The Johns Hopkins
14 // University Press, Baltimore MD, Fourth Printing 1993. In particular,
15 // the implementation is based on Chapter 7 (The Unsymmetric Eigenvalue
16 // Problem), Section 7.5 (The Practical QR Algorithm). The algorithm is
17 // specialized for the companion matrix associated with a quartic polynomial.
18 
19 namespace gte
20 {
21 
22 template <typename Real>
24 {
25 public:
26  typedef std::array<std::array<Real, 4>, 4> Matrix;
27 
28  // Solve p(x) = c0 + c1 * x + c2 * x^2 + c3 * x^3 + x^4 = 0.
29  uint32_t operator() (uint32_t maxIterations, Real c0, Real c1, Real c2, Real c3,
30  uint32_t& numRoots, std::array<Real, 4>& roots);
31 
32  // Compute the real eigenvalues of the upper Hessenberg matrix A. The
33  // matrix is modified by in-place operations, so if you need to remember
34  // A, you must make your own copy before calling this function.
35  uint32_t operator() (uint32_t maxIterations, Matrix& A,
36  uint32_t& numRoots, std::array<Real, 4>& roots);
37 
38 private:
39  void DoIteration(std::array<Real, 3> const& V, Matrix& A);
40 
41  template <int N>
42  std::array<Real, N> House(std::array<Real, N> const& X);
43 
44  template <int N>
45  void RowHouse(int rmin, int rmax, int cmin, int cmax,
46  std::array<Real, N> const& V, std::array<Real, N> const& MV, Matrix& A);
47 
48  template <int N>
49  void ColHouse(int rmin, int rmax, int cmin, int cmax,
50  std::array<Real, N> const& V, std::array<Real, N> const& MV, Matrix& A);
51 
52  void GetQuadraticRoots(int i0, int i1, Matrix const& A,
53  uint32_t& numRoots, std::array<Real, 4>& roots);
54 };
55 
56 
57 template <typename Real>
58 uint32_t QuarticRootsQR<Real>::operator() (uint32_t maxIterations, Real c0, Real c1, Real c2, Real c3,
59  uint32_t& numRoots, std::array<Real, 4>& roots)
60 {
61  // Create the companion matrix for the polynomial. The matrix is in upper
62  // Hessenberg form.
63  Matrix A;
64  A[0][0] = (Real)0;
65  A[0][1] = (Real)0;
66  A[0][2] = (Real)0;
67  A[0][3] = -c0;
68  A[1][0] = (Real)1;
69  A[1][1] = (Real)0;
70  A[1][2] = (Real)0;
71  A[1][3] = -c1;
72  A[2][0] = (Real)0;
73  A[2][1] = (Real)1;
74  A[2][2] = (Real)0;
75  A[2][3] = -c2;
76  A[3][0] = (Real)0;
77  A[3][1] = (Real)0;
78  A[3][2] = (Real)1;
79  A[3][3] = -c3;
80 
81  // Avoid the QR-cycle when c1 = c2 = 0 and avoid the slow convergence
82  // when c1 and c2 are nearly zero.
83  std::array<Real, 3> V{
84  (Real)1,
85  (Real)0.36602540378443865,
86  (Real)0.36602540378443865 };
87  DoIteration(V, A);
88 
89  return operator()(maxIterations, A, numRoots, roots);
90 }
91 
92 template <typename Real>
93 uint32_t QuarticRootsQR<Real>::operator() (uint32_t maxIterations, Matrix& A,
94  uint32_t& numRoots, std::array<Real, 4>& roots)
95 {
96  numRoots = 0;
97  std::fill(roots.begin(), roots.end(), (Real)0);
98 
99  for (uint32_t numIterations = 0; numIterations < maxIterations; ++numIterations)
100  {
101  // Apply a Francis QR iteration.
102  Real tr = A[2][2] + A[3][3];
103  Real det = A[2][2] * A[3][3] - A[2][3] * A[3][2];
104  std::array<Real, 3> X{
105  A[0][0] * A[0][0] + A[0][1] * A[1][0] - tr * A[0][0] + det,
106  A[1][0] * (A[0][0] + A[1][1] - tr),
107  A[1][0] * A[2][1] };
108  std::array<Real, 3> V = House<3>(X);
109  DoIteration(V, A);
110 
111  // Test for uncoupling of A.
112  Real tr12 = A[1][1] + A[2][2];
113  if (tr12 + A[2][1] == tr12)
114  {
115  GetQuadraticRoots(0, 1, A, numRoots, roots);
116  GetQuadraticRoots(2, 3, A, numRoots, roots);
117  return numIterations;
118  }
119 
120  Real tr01 = A[0][0] + A[1][1];
121  if (tr01 + A[1][0] == tr01)
122  {
123  numRoots = 1;
124  roots[0] = A[0][0];
125 
126  // TODO: The cubic solver is not designed to process 3x3 submatrices
127  // of an NxN matrix, so the copy of a submatrix of A to B is a simple
128  // workaround for running the solver. Write general root-finding
129  // code that avoids such copying.
130  uint32_t subMaxIterations = maxIterations - numIterations;
131  typename CubicRootsQR<Real>::Matrix B;
132  for (int r = 0; r < 3; ++r)
133  {
134  for (int c = 0; c < 3; ++c)
135  {
136  B[r][c] = A[r + 1][c + 1];
137  }
138  }
139 
140  uint32_t numSubroots = 0;
141  std::array<Real, 3> subroots;
142  uint32_t numSubiterations = CubicRootsQR<Real>()(subMaxIterations, B,
143  numSubroots, subroots);
144  for (uint32_t i = 0; i < numSubroots; ++i)
145  {
146  roots[numRoots++] = subroots[i];
147  }
148  return numIterations + numSubiterations;
149  }
150 
151  Real tr23 = A[2][2] + A[3][3];
152  if (tr23 + A[3][2] == tr23)
153  {
154  numRoots = 1;
155  roots[0] = A[3][3];
156 
157  // TODO: The cubic solver is not designed to process 3x3 submatrices
158  // of an NxN matrix, so the copy of a submatrix of A to B is a simple
159  // workaround for running the solver. Write general root-finding
160  // code that avoids such copying.
161  uint32_t subMaxIterations = maxIterations - numIterations;
162  typename CubicRootsQR<Real>::Matrix B;
163  for (int r = 0; r < 3; ++r)
164  {
165  for (int c = 0; c < 3; ++c)
166  {
167  B[r][c] = A[r][c];
168  }
169  }
170 
171  uint32_t numSubroots = 0;
172  std::array<Real, 3> subroots;
173  uint32_t numSubiterations = CubicRootsQR<Real>()(subMaxIterations, B,
174  numSubroots, subroots);
175  for (uint32_t i = 0; i < numSubroots; ++i)
176  {
177  roots[numRoots++] = subroots[i];
178  }
179  return numIterations + numSubiterations;
180  }
181  }
182  return maxIterations;
183 }
184 
185 template <typename Real>
186 void QuarticRootsQR<Real>::DoIteration(std::array<Real, 3> const& V, Matrix& A)
187 {
188  Real multV = ((Real)-2) / (V[0] * V[0] + V[1] * V[1] + V[2] * V[2]);
189  std::array<Real, 3> MV{ multV * V[0], multV * V[1], multV * V[2] };
190  RowHouse<3>(0, 2, 0, 3, V, MV, A);
191  ColHouse<3>(0, 3, 0, 2, V, MV, A);
192 
193  std::array<Real, 3> X{ A[1][0], A[2][0], A[3][0] };
194  std::array<Real, 3> locV = House<3>(X);
195  multV = ((Real)-2) / (locV[0] * locV[0] + locV[1] * locV[1] + locV[2] * locV[2]);
196  MV = { multV * locV[0], multV * locV[1], multV * locV[2] };
197  RowHouse<3>(1, 3, 0, 3, locV, MV, A);
198  ColHouse<3>(0, 3, 1, 3, locV, MV, A);
199 
200  std::array<Real, 2> Y{ A[2][1], A[3][1] };
201  std::array<Real, 2> W = House<2>(Y);
202  Real multW = ((Real)-2) / (W[0] * W[0] + W[1] * W[1]);
203  std::array<Real, 2> MW = { multW * W[0], multW * W[1] };
204  RowHouse<2>(2, 3, 0, 3, W, MW, A);
205  ColHouse<2>(0, 3, 2, 3, W, MW, A);
206 }
207 
208 template <typename Real>
209 template <int N>
210 std::array<Real, N> QuarticRootsQR<Real>::House(std::array<Real, N> const & X)
211 {
212  std::array<Real, N> V;
213  Real length = (Real)0;
214  for (int i = 0; i < N; ++i)
215  {
216  length += X[i] * X[i];
217  }
218  length = sqrt(length);
219  if (length != (Real)0)
220  {
221  Real sign = (X[0] >= (Real)0 ? (Real)1 : (Real)-1);
222  Real denom = X[0] + sign * length;
223  for (int i = 1; i < N; ++i)
224  {
225  V[i] = X[i] / denom;
226  }
227  }
228  V[0] = (Real)1;
229  return V;
230 }
231 
232 template <typename Real>
233 template <int N>
234 void QuarticRootsQR<Real>::RowHouse(int rmin, int rmax, int cmin, int cmax,
235  std::array<Real, N> const& V, std::array<Real, N> const& MV, Matrix& A)
236 {
237  std::array<Real, 4> W; // only elements cmin through cmax are used
238 
239  for (int c = cmin; c <= cmax; ++c)
240  {
241  W[c] = (Real)0;
242  for (int r = rmin, k = 0; r <= rmax; ++r, ++k)
243  {
244  W[c] += V[k] * A[r][c];
245  }
246  }
247 
248  for (int r = rmin, k = 0; r <= rmax; ++r, ++k)
249  {
250  for (int c = cmin; c <= cmax; ++c)
251  {
252  A[r][c] += MV[k] * W[c];
253  }
254  }
255 }
256 
257 template <typename Real>
258 template <int N>
259 void QuarticRootsQR<Real>::ColHouse(int rmin, int rmax, int cmin, int cmax,
260  std::array<Real, N> const& V, std::array<Real, N> const& MV, Matrix& A)
261 {
262  std::array<Real, 4> W; // only elements rmin through rmax are used
263 
264  for (int r = rmin; r <= rmax; ++r)
265  {
266  W[r] = (Real)0;
267  for (int c = cmin, k = 0; c <= cmax; ++c, ++k)
268  {
269  W[r] += V[k] * A[r][c];
270  }
271  }
272 
273  for (int r = rmin; r <= rmax; ++r)
274  {
275  for (int c = cmin, k = 0; c <= cmax; ++c, ++k)
276  {
277  A[r][c] += W[r] * MV[k];
278  }
279  }
280 }
281 
282 template <typename Real>
283 void QuarticRootsQR<Real>::GetQuadraticRoots(int i0, int i1, Matrix const& A,
284  uint32_t& numRoots, std::array<Real, 4>& roots)
285 {
286  // Solve x^2 - t * x + d = 0, where t is the trace and d is the
287  // determinant of the 2x2 matrix defined by indices i0 and i1. The
288  // discriminant is D = (t/2)^2 - d. When D >= 0, the roots are real
289  // values t/2 - sqrt(D) and t/2 + sqrt(D). To avoid potential numerical
290  // issues with subtractive cancellation, the roots are computed as
291  // r0 = t/2 + sign(t/2)*sqrt(D), r1 = trace - r0.
292  Real trace = A[i0][i0] + A[i1][i1];
293  Real halfTrace = trace * (Real)0.5;
294  Real determinant = A[i0][i0] * A[i1][i1] - A[i0][i1] * A[i1][i0];
295  Real discriminant = halfTrace * halfTrace - determinant;
296  if (discriminant >= (Real)0)
297  {
298  Real sign = (trace >= (Real)0 ? (Real)1 : (Real)-1);
299  Real root = halfTrace + sign * sqrt(discriminant);
300  roots[numRoots++] = root;
301  roots[numRoots++] = trace - root;
302  }
303 }
304 
305 }
std::array< std::array< Real, 4 >, 4 > Matrix
void RowHouse(int rmin, int rmax, int cmin, int cmax, std::array< Real, N > const &V, std::array< Real, N > const &MV, Matrix &A)
void ColHouse(int rmin, int rmax, int cmin, int cmax, std::array< Real, N > const &V, std::array< Real, N > const &MV, Matrix &A)
uint32_t operator()(uint32_t maxIterations, Real c0, Real c1, Real c2, Real c3, uint32_t &numRoots, std::array< Real, 4 > &roots)
void DoIteration(std::array< Real, 3 > const &V, Matrix &A)
std::array< Real, N > House(std::array< Real, N > const &X)
void GetQuadraticRoots(int i0, int i1, Matrix const &A, uint32_t &numRoots, std::array< Real, 4 > &roots)
const GLubyte * c
Definition: glext.h:11671
GLuint GLsizei GLsizei * length
Definition: glcorearb.h:790
GLboolean r
Definition: glcorearb.h:1217
std::array< std::array< Real, 3 >, 3 > Matrix


geometric_tools_engine
Author(s): Yijiang Huang
autogenerated on Thu Jul 18 2019 04:00:01