GteApprPolynomialSpecial3.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]}
17 // where <p[i],q[i]> are distinct pairs of nonnegative powers provided by the
18 // caller. A least-squares fitting algorithm is used, but the input data is
19 // first mapped to (x,y,w) in [-1,1]^3 for numerical robustness.
20 
21 namespace gte
22 {
23 
24 template <typename Real>
26  :
27  public ApprQuery<Real, ApprPolynomialSpecial3<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  ApprPolynomialSpecial3(std::vector<int> const& xDegrees,
33  std::vector<int> const& yDegrees);
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, 3>> 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, 3> 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) in domain) or extrapolate ((x,y) not in domain).
53  std::array<Real, 2> const& GetXDomain() const;
54  std::array<Real, 2> const& GetYDomain() const;
55  Real Evaluate(Real x, Real y) const;
56 
57 private:
58  // Transform the (x,y,w) values to (x',y',w') in [-1,1]^3.
59  void Transform(std::vector<std::array<Real, 3>> const& observations,
60  std::vector<int> const& indices,
61  std::vector<std::array<Real, 3>>& transformed);
62 
63  // The least-squares fitting algorithm for the transformed data.
64  bool DoLeastSquares(std::vector<std::array<Real, 3>>& transformed);
65 
66  std::vector<int> mXDegrees, mYDegrees;
67  std::vector<Real> mParameters;
68 
69  // Support for evaluation. The coefficients were generated for the
70  // samples mapped to [-1,1]^3. The Evaluate() function must transform
71  // (x,y) to (x',y') in [-1,1]^2, compute w' in [-1,1], then transform w'
72  // to w.
73  std::array<Real, 2> mXDomain, mYDomain, mWDomain;
74  std::array<Real, 3> mScale;
76 
77  // This array is used by Evaluate() to avoid reallocation of the 'vector's
78  // for each call. The members are mutable because, to the user, the call
79  // to Evaluate does not modify the polynomial.
80  mutable std::vector<Real> mXPowers, mYPowers;
81 };
82 
83 
84 template <typename Real>
86  std::vector<int> const& xDegrees, std::vector<int> const& yDegrees)
87  :
88  mXDegrees(xDegrees),
89  mYDegrees(yDegrees),
91 {
92 #if !defined(GTE_NO_LOGGER)
93  LogAssert(mXDegrees.size() == mYDegrees.size(),
94  "The input arrays must have the same size.");
95 
96  LogAssert(mXDegrees.size() > 0, "The input array must have elements.");
97  int lastDegree = -1;
98  for (auto degree : mXDegrees)
99  {
100  LogAssert(degree > lastDegree, "Degrees must be increasing.");
101  lastDegree = degree;
102  }
103 
104  LogAssert(mYDegrees.size() > 0, "The input array must have elements.");
105  lastDegree = -1;
106  for (auto degree : mYDegrees)
107  {
108  LogAssert(degree > lastDegree, "Degrees must be increasing.");
109  lastDegree = degree;
110  }
111 #endif
112 
113  mXDomain[0] = std::numeric_limits<Real>::max();
114  mXDomain[1] = -mXDomain[0];
115  mYDomain[0] = std::numeric_limits<Real>::max();
116  mYDomain[1] = -mYDomain[0];
117  mWDomain[0] = std::numeric_limits<Real>::max();
118  mWDomain[1] = -mWDomain[0];
119  std::fill(mParameters.begin(), mParameters.end(), (Real)0);
120 
121  mScale[0] = (Real)0;
122  mScale[1] = (Real)0;
123  mScale[2] = (Real)0;
124  mInvTwoWScale = (Real)0;
125 
126  // Powers of x and y are computed up to twice the powers when constructing
127  // the fitted polynomial. Powers of x and y are computed up to the powers
128  // for the evaluation of the fitted polynomial.
129  mXPowers.resize(2 * mXDegrees.back() + 1);
130  mXPowers[0] = (Real)1;
131  mYPowers.resize(2 * mYDegrees.back() + 1);
132  mYPowers[0] = (Real)1;
133 }
134 
135 template <typename Real>
137 {
138  return static_cast<int>(mParameters.size());
139 }
140 
141 template <typename Real>
143  std::vector<std::array<Real, 3>> const& observations,
144  std::vector<int> const& indices)
145 {
146  if (indices.size() > 0)
147  {
148  // Transform the observations to [-1,1]^3 for numerical robustness.
149  std::vector<std::array<Real, 3>> transformed;
150  Transform(observations, indices, transformed);
151 
152  // Fit the transformed data using a least-squares algorithm.
153  return DoLeastSquares(transformed);
154  }
155 
156  std::fill(mParameters.begin(), mParameters.end(), (Real)0);
157  return false;
158 }
159 
160 template <typename Real>
162  std::array<Real, 3> const& observation) const
163 {
164  Real w = Evaluate(observation[0], observation[1]);
165  Real error = std::abs(w - observation[2]);
166  return error;
167 }
168 
169 template <typename Real>
170 std::vector<Real> const& ApprPolynomialSpecial3<Real>::GetParameters() const
171 {
172  return mParameters;
173 }
174 
175 template <typename Real>
176 std::array<Real, 2> const& ApprPolynomialSpecial3<Real>::GetXDomain() const
177 {
178  return mXDomain;
179 }
180 
181 template <typename Real>
182 std::array<Real, 2> const& ApprPolynomialSpecial3<Real>::GetYDomain() const
183 {
184  return mYDomain;
185 }
186 
187 template <typename Real>
189 {
190  // Transform (x,y) to (x',y') in [-1,1]^2.
191  x = (Real)-1 + ((Real)2) * mScale[0] * (x - mXDomain[0]);
192  y = (Real)-1 + ((Real)2) * mScale[1] * (y - mYDomain[0]);
193 
194  // Compute relevant powers of x and y.
195  int jmax = mXDegrees.back();
196  for (int j = 1; j <= jmax; ++j)
197  {
198  mXPowers[j] = mXPowers[j - 1] * x;
199  }
200 
201  jmax = mYDegrees.back();
202  for (int j = 1; j <= jmax; ++j)
203  {
204  mYPowers[j] = mYPowers[j - 1] * y;
205  }
206 
207  Real w = (Real)0;
208  int isup = static_cast<int>(mXDegrees.size());
209  for (int i = 0; i < isup; ++i)
210  {
211  Real xp = mXPowers[mXDegrees[i]];
212  Real yp = mYPowers[mYDegrees[i]];
213  w += mParameters[i] * xp * yp;
214  }
215 
216  // Transform w from [-1,1] back to the original space.
217  w = (w + (Real)1) * mInvTwoWScale + mWDomain[0];
218  return w;
219 }
220 
221 template <typename Real>
223  std::vector<std::array<Real, 3>> const& observations,
224  std::vector<int> const& indices,
225  std::vector<std::array<Real, 3>>& transformed)
226 {
227  int numSamples = static_cast<int>(indices.size());
228  transformed.resize(numSamples);
229 
230  std::array<Real, 3> omin = observations[indices[0]];
231  std::array<Real, 3> omax = omin;
232  std::array<Real, 3> obs;
233  int s, i;
234  for (s = 1; s < numSamples; ++s)
235  {
236  obs = observations[indices[s]];
237  for (i = 0; i < 3; ++i)
238  {
239  if (obs[i] < omin[i])
240  {
241  omin[i] = obs[i];
242  }
243  else if (obs[i] > omax[i])
244  {
245  omax[i] = obs[i];
246  }
247  }
248  }
249 
250  mXDomain[0] = omin[0];
251  mXDomain[1] = omax[0];
252  mYDomain[0] = omin[1];
253  mYDomain[1] = omax[1];
254  mWDomain[0] = omin[2];
255  mWDomain[1] = omax[2];
256  for (i = 0; i < 3; ++i)
257  {
258  mScale[i] = ((Real)1) / (omax[i] - omin[i]);
259  }
260 
261  for (s = 0; s < numSamples; ++s)
262  {
263  obs = observations[indices[s]];
264  for (i = 0; i < 3; ++i)
265  {
266  transformed[s][i] = (Real)-1 + ((Real)2) * mScale[i] *
267  (obs[i] - omin[i]);
268  }
269  }
270  mInvTwoWScale = ((Real)0.5) / mScale[2];
271 }
272 
273 template <typename Real>
275  std::vector<std::array<Real, 3>>& transformed)
276 {
277  // Set up a linear system A*X = B, where X are the polynomial
278  // coefficients.
279  int size = static_cast<int>(mXDegrees.size());
280  GMatrix<Real> A(size, size);
281  A.MakeZero();
282  GVector<Real> B(size);
283  B.MakeZero();
284 
285  int numSamples = static_cast<int>(transformed.size());
286  int twoMaxXDegree = 2 * mXDegrees.back();
287  int twoMaxYDegree = 2 * mYDegrees.back();
288  int row, col;
289  for (int i = 0; i < numSamples; ++i)
290  {
291  // Compute relevant powers of x and y.
292  Real x = transformed[i][0];
293  Real y = transformed[i][1];
294  Real w = transformed[i][2];
295  for (int j = 1; j <= 2 * twoMaxXDegree; ++j)
296  {
297  mXPowers[j] = mXPowers[j - 1] * x;
298  }
299  for (int j = 1; j <= 2 * twoMaxYDegree; ++j)
300  {
301  mYPowers[j] = mYPowers[j - 1] * y;
302  }
303 
304  for (row = 0; row < size; ++row)
305  {
306  // Update the upper-triangular portion of the symmetric matrix.
307  Real xp, yp;
308  for (col = row; col < size; ++col)
309  {
310  xp = mXPowers[mXDegrees[row] + mXDegrees[col]];
311  yp = mYPowers[mYDegrees[row] + mYDegrees[col]];
312  A(row, col) += xp * yp;
313  }
314 
315  // Update the right-hand side of the system.
316  xp = mXPowers[mXDegrees[row]];
317  yp = mYPowers[mYDegrees[row]];
318  B[row] += xp * yp * w;
319  }
320  }
321 
322  // Copy the upper-triangular portion of the symmetric matrix to the
323  // lower-triangular portion.
324  for (row = 0; row < size; ++row)
325  {
326  for (col = 0; col < row; ++col)
327  {
328  A(row, col) = A(col, row);
329  }
330  }
331 
332  // Precondition by normalizing the sums.
333  Real invNumSamples = ((Real)1) / (Real)numSamples;
334  A *= invNumSamples;
335  B *= invNumSamples;
336 
337  // Solve for the polynomial coefficients.
338  GVector<Real> coefficients = Inverse(A) * B;
339  bool hasNonzero = false;
340  for (int i = 0; i < size; ++i)
341  {
342  mParameters[i] = coefficients[i];
343  if (coefficients[i] != (Real)0)
344  {
345  hasNonzero = true;
346  }
347  }
348  return hasNonzero;
349 }
350 
351 
352 }
std::array< Real, 2 > const & GetYDomain() const
void Transform(std::vector< std::array< Real, 3 >> const &observations, std::vector< int > const &indices, std::vector< std::array< Real, 3 >> &transformed)
gte::BSNumber< UIntegerType > abs(gte::BSNumber< UIntegerType > const &number)
Definition: GteBSNumber.h:966
#define LogAssert(condition, message)
Definition: GteLogger.h:86
GLint GLenum GLint x
Definition: glcorearb.h:404
GLsizeiptr size
Definition: glcorearb.h:659
GLubyte GLubyte GLubyte GLubyte w
Definition: glcorearb.h:852
Real Evaluate(Real x, Real y) const
GLenum GLenum GLsizei void * row
Definition: glext.h:2725
GLsizei GLenum const void * indices
Definition: glcorearb.h:401
bool DoLeastSquares(std::vector< std::array< Real, 3 >> &transformed)
void MakeZero()
Definition: GteGVector.h:245
std::array< Real, 2 > const & GetXDomain() const
ApprPolynomialSpecial3(std::vector< int > const &xDegrees, std::vector< int > const &yDegrees)
Real Error(std::array< Real, 3 > const &observation) const
std::vector< Real > const & GetParameters() const
GLdouble s
Definition: glext.h:231
Quaternion< Real > Inverse(Quaternion< Real > const &d)
bool Fit(std::vector< std::array< Real, 3 >> const &observations, std::vector< int > const &indices)
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