GteApprPolynomialSpecial4.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 <LowLevel/GteLogger.h>
11 #include <Mathematics/GteGMatrix.h>
13 #include <array>
14 
15 // Fit the data with a polynomial of the form
16 // w = sum_{i=0}^{n-1} c[i]*x^{p[i]}*y^{q[i]}*z^{r[i]}
17 // where <p[i],q[i],r[i]> are distinct triples of nonnegative powers provided
18 // by the caller. A least-squares fitting algorithm is used, but the input
19 // data is first mapped to (x,y,z,w) in [-1,1]^4 for numerical robustness.
20 
21 namespace gte
22 {
23 
24 template <typename Real>
26  :
27  public ApprQuery<Real, ApprPolynomialSpecial4<Real>, std::array<Real, 3>>
28 {
29 public:
30  // Initialize the model parameters to zero. The degrees must be
31  // nonnegative and strictly increasing.
32  ApprPolynomialSpecial4(std::vector<int> const& xDegrees,
33  std::vector<int> const& yDegrees, std::vector<int> const& zDegrees);
34 
35  // The minimum number of observations required to fit the model.
36  int GetMinimumRequired() const;
37 
38  // Estimate the model parameters for all observations specified by the
39  // indices. This function is called by the base-class Fit(...) functions.
40  bool Fit(std::vector<std::array<Real, 4>> const& observations,
41  std::vector<int> const& indices);
42 
43  // Compute the model error for the specified observation for the current
44  // model parameters. The returned value for observation (x0,w0) is
45  // |w(x0) - w0|, where w(x) is the fitted polynomial.
46  Real Error(std::array<Real, 4> const& observation) const;
47 
48  // Get the parameters of the model.
49  std::vector<Real> const& GetParameters() const;
50 
51  // Evaluate the polynomial. The domain interval is provided so you can
52  // interpolate ((x,y,z) in domain) or extrapolate ((x,y,z) not in domain).
53  std::array<Real, 2> const& GetXDomain() const;
54  std::array<Real, 2> const& GetYDomain() const;
55  std::array<Real, 2> const& GetZDomain() const;
56  Real Evaluate(Real x, Real y, Real z) const;
57 
58 private:
59  // Transform the (x,y,z,w) values to (x',y',z',w') in [-1,1]^4.
60  void Transform(std::vector<std::array<Real, 4>> const& observations,
61  std::vector<int> const& indices,
62  std::vector<std::array<Real, 4>>& transformed);
63 
64  // The least-squares fitting algorithm for the transformed data.
65  bool DoLeastSquares(std::vector<std::array<Real, 4>>& transformed);
66 
67  std::vector<int> mXDegrees, mYDegrees, mZDegrees;
68  std::vector<Real> mParameters;
69 
70  // Support for evaluation. The coefficients were generated for the
71  // samples mapped to [-1,1]^4. The Evaluate() function must transform
72  // (x,y,z) to (x',y',z') in [-1,1]^3, compute w' in [-1,1], then transform
73  // w' to w.
74  std::array<Real, 2> mXDomain, mYDomain, mZDomain, mWDomain;
75  std::array<Real, 4> mScale;
77 
78  // This array is used by Evaluate() to avoid reallocation of the 'vector's
79  // for each call. The members are mutable because, to the user, the call
80  // to Evaluate does not modify the polynomial.
81  mutable std::vector<Real> mXPowers, mYPowers, mZPowers;
82 };
83 
84 
85 template <typename Real>
87  std::vector<int> const& xDegrees, std::vector<int> const& yDegrees,
88  std::vector<int> const& zDegrees)
89  :
90  mXDegrees(xDegrees),
91  mYDegrees(yDegrees),
92  mZDegrees(zDegrees),
94 {
95 #if !defined(GTE_NO_LOGGER)
96  LogAssert(mXDegrees.size() == mYDegrees.size()
97  && mXDegrees.size() == mZDegrees.size(),
98  "The input arrays must have the same size.");
99 
100  LogAssert(mXDegrees.size() > 0, "The input array must have elements.");
101  int lastDegree = -1;
102  for (auto degree : mXDegrees)
103  {
104  LogAssert(degree > lastDegree, "Degrees must be increasing.");
105  lastDegree = degree;
106  }
107 
108  LogAssert(mYDegrees.size() > 0, "The input array must have elements.");
109  lastDegree = -1;
110  for (auto degree : mYDegrees)
111  {
112  LogAssert(degree > lastDegree, "Degrees must be increasing.");
113  lastDegree = degree;
114  }
115 
116  LogAssert(mZDegrees.size() > 0, "The input array must have elements.");
117  lastDegree = -1;
118  for (auto degree : mZDegrees)
119  {
120  LogAssert(degree > lastDegree, "Degrees must be increasing.");
121  lastDegree = degree;
122  }
123 #endif
124 
125  mXDomain[0] = std::numeric_limits<Real>::max();
126  mXDomain[1] = -mXDomain[0];
127  mYDomain[0] = std::numeric_limits<Real>::max();
128  mYDomain[1] = -mYDomain[0];
129  mZDomain[0] = std::numeric_limits<Real>::max();
130  mZDomain[1] = -mZDomain[0];
131  mWDomain[0] = std::numeric_limits<Real>::max();
132  mWDomain[1] = -mWDomain[0];
133  std::fill(mParameters.begin(), mParameters.end(), (Real)0);
134 
135  mScale[0] = (Real)0;
136  mScale[1] = (Real)0;
137  mScale[2] = (Real)0;
138  mScale[3] = (Real)0;
139  mInvTwoWScale = (Real)0;
140 
141  // Powers of x, y, and z are computed up to twice the powers when
142  // constructing the fitted polynomial. Powers of x, y, and z are
143  // computed up to the powers for the evaluation of the fitted polynomial.
144  mXPowers.resize(2 * mXDegrees.back() + 1);
145  mXPowers[0] = (Real)1;
146  mYPowers.resize(2 * mYDegrees.back() + 1);
147  mYPowers[0] = (Real)1;
148  mZPowers.resize(2 * mZDegrees.back() + 1);
149  mZPowers[0] = (Real)1;
150 }
151 
152 template <typename Real>
154 {
155  return static_cast<int>(mParameters.size());
156 }
157 
158 template <typename Real>
160  std::vector<std::array<Real, 4>> const& observations,
161  std::vector<int> const& indices)
162 {
163  if (indices.size() > 0)
164  {
165  // Transform the observations to [-1,1]^4 for numerical robustness.
166  std::vector<std::array<Real, 4>> transformed;
167  Transform(observations, indices, transformed);
168 
169  // Fit the transformed data using a least-squares algorithm.
170  return DoLeastSquares(transformed);
171  }
172 
173  std::fill(mParameters.begin(), mParameters.end(), (Real)0);
174  return false;
175 }
176 
177 template <typename Real>
179  std::array<Real, 4> const& observation) const
180 {
181  Real w = Evaluate(observation[0], observation[1], observation[2]);
182  Real error = std::abs(w - observation[3]);
183  return error;
184 }
185 
186 template <typename Real>
187 std::vector<Real> const& ApprPolynomialSpecial4<Real>::GetParameters() const
188 {
189  return mParameters;
190 }
191 
192 template <typename Real>
193 std::array<Real, 2> const& ApprPolynomialSpecial4<Real>::GetXDomain() const
194 {
195  return mXDomain;
196 }
197 
198 template <typename Real>
199 std::array<Real, 2> const& ApprPolynomialSpecial4<Real>::GetYDomain() const
200 {
201  return mYDomain;
202 }
203 
204 template <typename Real>
205 std::array<Real, 2> const& ApprPolynomialSpecial4<Real>::GetZDomain() const
206 {
207  return mZDomain;
208 }
209 
210 template <typename Real>
211 Real ApprPolynomialSpecial4<Real>::Evaluate(Real x, Real y, Real z) const
212 {
213  // Transform (x,y,z) to (x',y',z') in [-1,1]^3.
214  x = (Real)-1 + ((Real)2) * mScale[0] * (x - mXDomain[0]);
215  y = (Real)-1 + ((Real)2) * mScale[1] * (y - mYDomain[0]);
216  z = (Real)-1 + ((Real)2) * mScale[2] * (z - mZDomain[0]);
217 
218  // Compute relevant powers of x, y, and z.
219  int jmax = mXDegrees.back();;
220  for (int j = 1; j <= jmax; ++j)
221  {
222  mXPowers[j] = mXPowers[j - 1] * x;
223  }
224 
225  jmax = mYDegrees.back();;
226  for (int j = 1; j <= jmax; ++j)
227  {
228  mYPowers[j] = mYPowers[j - 1] * y;
229  }
230 
231  jmax = mZDegrees.back();;
232  for (int j = 1; j <= jmax; ++j)
233  {
234  mZPowers[j] = mZPowers[j - 1] * z;
235  }
236 
237  Real w = (Real)0;
238  int isup = static_cast<int>(mXDegrees.size());
239  for (int i = 0; i < isup; ++i)
240  {
241  Real xp = mXPowers[mXDegrees[i]];
242  Real yp = mYPowers[mYDegrees[i]];
243  Real zp = mYPowers[mZDegrees[i]];
244  w += mParameters[i] * xp * yp * zp;
245  }
246 
247  // Transform w from [-1,1] back to the original space.
248  w = (w + (Real)1) * mInvTwoWScale + mWDomain[0];
249  return w;
250 }
251 
252 template <typename Real>
254  std::vector<std::array<Real, 4>> const& observations,
255  std::vector<int> const& indices,
256  std::vector<std::array<Real, 4>>& transformed)
257 {
258  int numSamples = static_cast<int>(indices.size());
259  transformed.resize(numSamples);
260 
261  std::array<Real, 4> omin = observations[indices[0]];
262  std::array<Real, 4> omax = omin;
263  std::array<Real, 4> obs;
264  int s, i;
265  for (s = 1; s < numSamples; ++s)
266  {
267  obs = observations[indices[s]];
268  for (i = 0; i < 4; ++i)
269  {
270  if (obs[i] < omin[i])
271  {
272  omin[i] = obs[i];
273  }
274  else if (obs[i] > omax[i])
275  {
276  omax[i] = obs[i];
277  }
278  }
279  }
280 
281  mXDomain[0] = omin[0];
282  mXDomain[1] = omax[0];
283  mYDomain[0] = omin[1];
284  mYDomain[1] = omax[1];
285  mZDomain[0] = omin[2];
286  mZDomain[1] = omax[2];
287  mWDomain[0] = omin[3];
288  mWDomain[1] = omax[3];
289  for (i = 0; i < 4; ++i)
290  {
291  mScale[i] = ((Real)1) / (omax[i] - omin[i]);
292  }
293 
294  for (s = 0; s < numSamples; ++s)
295  {
296  obs = observations[indices[s]];
297  for (i = 0; i < 4; ++i)
298  {
299  transformed[s][i] = (Real)-1 + ((Real)2) * mScale[i] *
300  (obs[i] - omin[i]);
301  }
302  }
303  mInvTwoWScale = ((Real)0.5) / mScale[3];
304 }
305 
306 template <typename Real>
308  std::vector<std::array<Real, 4>>& transformed)
309 {
310  // Set up a linear system A*X = B, where X are the polynomial
311  // coefficients.
312  int size = static_cast<int>(mXDegrees.size());
313  GMatrix<Real> A(size, size);
314  A.MakeZero();
315  GVector<Real> B(size);
316  B.MakeZero();
317 
318  int numSamples = static_cast<int>(transformed.size());
319  int twoMaxXDegree = 2 * mXDegrees.back();
320  int twoMaxYDegree = 2 * mYDegrees.back();
321  int twoMaxZDegree = 2 * mZDegrees.back();
322  int row, col;
323  for (int i = 0; i < numSamples; ++i)
324  {
325  // Compute relevant powers of x, y, and z.
326  Real x = transformed[i][0];
327  Real y = transformed[i][1];
328  Real z = transformed[i][2];
329  Real w = transformed[i][3];
330  for (int j = 1; j <= twoMaxXDegree; ++j)
331  {
332  mXPowers[j] = mXPowers[j - 1] * x;
333  }
334  for (int j = 1; j <= twoMaxYDegree; ++j)
335  {
336  mYPowers[j] = mYPowers[j - 1] * y;
337  }
338  for (int j = 1; j <= twoMaxZDegree; ++j)
339  {
340  mZPowers[j] = mZPowers[j - 1] * z;
341  }
342 
343  for (row = 0; row < size; ++row)
344  {
345  // Update the upper-triangular portion of the symmetric matrix.
346  Real xp, yp, zp;
347  for (col = row; col < size; ++col)
348  {
349  xp = mXPowers[mXDegrees[row] + mXDegrees[col]];
350  yp = mYPowers[mYDegrees[row] + mYDegrees[col]];
351  zp = mZPowers[mZDegrees[row] + mZDegrees[col]];
352  A(row, col) += xp * yp * zp;
353  }
354 
355  // Update the right-hand side of the system.
356  xp = mXPowers[mXDegrees[row]];
357  yp = mYPowers[mYDegrees[row]];
358  zp = mZPowers[mZDegrees[row]];
359  B[row] += xp * yp * zp * w;
360  }
361  }
362 
363  // Copy the upper-triangular portion of the symmetric matrix to the
364  // lower-triangular portion.
365  for (row = 0; row < size; ++row)
366  {
367  for (col = 0; col < row; ++col)
368  {
369  A(row, col) = A(col, row);
370  }
371  }
372 
373  // Precondition by normalizing the sums.
374  Real invNumSamples = ((Real)1) / (Real)numSamples;
375  A *= invNumSamples;
376  B *= invNumSamples;
377 
378  // Solve for the polynomial coefficients.
379  GVector<Real> coefficients = Inverse(A) * B;
380  bool hasNonzero = false;
381  for (int i = 0; i < size; ++i)
382  {
383  mParameters[i] = coefficients[i];
384  if (coefficients[i] != (Real)0)
385  {
386  hasNonzero = true;
387  }
388  }
389  return hasNonzero;
390 }
391 
392 
393 }
gte::BSNumber< UIntegerType > abs(gte::BSNumber< UIntegerType > const &number)
Definition: GteBSNumber.h:966
std::array< Real, 2 > const & GetYDomain() const
#define LogAssert(condition, message)
Definition: GteLogger.h:86
Real Error(std::array< Real, 4 > const &observation) const
std::array< Real, 2 > const & GetXDomain() const
GLint GLenum GLint x
Definition: glcorearb.h:404
GLsizeiptr size
Definition: glcorearb.h:659
GLubyte GLubyte GLubyte GLubyte w
Definition: glcorearb.h:852
bool Fit(std::vector< std::array< Real, 4 >> const &observations, std::vector< int > const &indices)
std::vector< Real > const & GetParameters() const
GLenum GLenum GLsizei void * row
Definition: glext.h:2725
GLsizei GLenum const void * indices
Definition: glcorearb.h:401
Real Evaluate(Real x, Real y, Real z) const
void MakeZero()
Definition: GteGVector.h:245
bool DoLeastSquares(std::vector< std::array< Real, 4 >> &transformed)
std::array< Real, 2 > const & GetZDomain() const
void Transform(std::vector< std::array< Real, 4 >> const &observations, std::vector< int > const &indices, std::vector< std::array< Real, 4 >> &transformed)
GLdouble GLdouble GLdouble z
Definition: glcorearb.h:843
GLdouble s
Definition: glext.h:231
Quaternion< Real > Inverse(Quaternion< Real > const &d)
ApprPolynomialSpecial4(std::vector< int > const &xDegrees, std::vector< int > const &yDegrees, std::vector< int > const &zDegrees)
GLint y
Definition: glcorearb.h:98
void MakeZero()
Definition: GteGMatrix.h:387


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