GteApprPolynomial4.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/GteArray2.h>
11 #include <Mathematics/GteGMatrix.h>
13 #include <array>
14 
15 // The samples are (x[i],y[i],z[i],w[i]) for 0 <= i < S. Think of w as a
16 // function of x, y, and z, say w = f(x,y,z). The function fits the samples
17 // with a polynomial of degree d0 in x, degree d1 in y, and degree d2 in z,
18 // say
19 // w = sum_{i=0}^{d0} sum_{j=0}^{d1} sum_{k=0}^{d2} c[i][j][k]*x^i*y^j*z^k
20 // The method is a least-squares fitting algorithm. The mParameters stores
21 // c[i][j][k] = mParameters[i+(d0+1)*(j+(d1+1)*k)] for a total of
22 // (d0+1)*(d1+1)*(d2+1) coefficients. The observation type is
23 // std::array<Real,4>, which represents a tuple (x,y,z,w).
24 //
25 // WARNING. The fitting algorithm for polynomial terms
26 // (1,x,x^2,...,x^d0), (1,y,y^2,...,y^d1), (1,z,z^2,...,z^d2)
27 // is known to be nonrobust for large degrees and for large magnitude data.
28 // One alternative is to use orthogonal polynomials
29 // (f[0](x),...,f[d0](x)), (g[0](y),...,g[d1](y)), (h[0](z),...,h[d2](z))
30 // and apply the least-squares algorithm to these. Another alternative is to
31 // transform
32 // (x',y',z',w') = ((x-xcen)/rng, (y-ycen)/rng, (z-zcen)/rng, w/rng)
33 // where xmin = min(x[i]), xmax = max(x[i]), xcen = (xmin+xmax)/2,
34 // ymin = min(y[i]), ymax = max(y[i]), ycen = (ymin+ymax)/2, zmin = min(z[i]),
35 // zmax = max(z[i]), zcen = (zmin+zmax)/2, and
36 // rng = max(xmax-xmin,ymax-ymin,zmax-zmin). Fit the (x',y',z',w') points,
37 // w' = sum_{i=0}^{d0} sum_{j=0}^{d1} sum_{k=0}^{d2} c'[i][j][k] *
38 // (x')^i*(y')^j*(z')^k
39 // The original polynomial is evaluated as
40 // w = rng * sum_{i=0}^{d0} sum_{j=0}^{d1} sum_{k=0}^{d2} c'[i][j][k] *
41 // ((x-xcen)/rng)^i * ((y-ycen)/rng)^j * ((z-zcen)/rng)^k
42 
43 namespace gte
44 {
45 
46 template <typename Real>
48  :
49  public ApprQuery<Real, ApprPolynomial4<Real>, std::array<Real, 4>>
50 {
51 public:
52  // Initialize the model parameters to zero.
53  ApprPolynomial4(int xDegree, int yDegree, int zDegree);
54 
55  // The minimum number of observations required to fit the model.
56  int GetMinimumRequired() const;
57 
58  // Estimate the model parameters for all observations specified by the
59  // indices. This function is called by the base-class Fit(...) functions.
60  bool Fit(std::vector<std::array<Real, 4>> const& observations,
61  std::vector<int> const& indices);
62 
63  // Compute the model error for the specified observation for the current
64  // model parameters. The returned value for observation (x0,y0,z0,w0) is
65  // |w(x0,y0,z0) - w0|, where w(x,y,z) is the fitted polynomial.
66  Real Error(std::array<Real, 4> const& observation) const;
67 
68  // Get the parameters of the model.
69  std::vector<Real> const& GetParameters() const;
70 
71  // Evaluate the polynomial. The domain intervals are provided so you can
72  // interpolate ((x,y,z) in domain) or extrapolate ((x,y,z) not in domain).
73  std::array<Real, 2> const& GetXDomain() const;
74  std::array<Real, 2> const& GetYDomain() const;
75  std::array<Real, 2> const& GetZDomain() const;
76  Real Evaluate(Real x, Real y, Real z) const;
77 
78 private:
81  std::array<Real, 2> mXDomain, mYDomain, mZDomain;
82  std::vector<Real> mParameters;
83 
84  // These arrays are used by Evaluate() to avoid reallocation of the
85  // 'vector's for each call. The member is mutable because, to the
86  // user, the call to Evaluate does not modify the polynomial.
87  mutable std::vector<Real> mYZCoefficient;
88  mutable std::vector<Real> mZCoefficient;
89 };
90 
91 
92 template <typename Real>
93 ApprPolynomial4<Real>::ApprPolynomial4(int xDegree, int yDegree, int zDegree)
94  :
95  mXDegree(xDegree),
96  mYDegree(yDegree),
97  mZDegree(zDegree),
98  mXDegreeP1(xDegree + 1),
99  mYDegreeP1(yDegree + 1),
100  mZDegreeP1(zDegree + 1),
105 {
106  mXDomain[0] = std::numeric_limits<Real>::max();
107  mXDomain[1] = -mXDomain[0];
108  mYDomain[0] = std::numeric_limits<Real>::max();
109  mYDomain[1] = -mYDomain[0];
110  mZDomain[0] = std::numeric_limits<Real>::max();
111  mZDomain[1] = -mZDomain[0];
112  std::fill(mParameters.begin(), mParameters.end(), (Real)0);
113  std::fill(mYZCoefficient.begin(), mYZCoefficient.end(), (Real)0);
114  std::fill(mZCoefficient.begin(), mZCoefficient.end(), (Real)0);
115 }
116 
117 template <typename Real>
119 {
120  return mSize;
121 }
122 
123 template <typename Real>
125  std::vector<std::array<Real, 4>> const& observations,
126  std::vector<int> const& indices)
127 {
128  if (indices.size() > 0)
129  {
130  int s, i0, j0, k0, n0, i1, j1, k1, n1;
131 
132  // Compute the powers of x, y, and z.
133  int numSamples = static_cast<int>(indices.size());
134  int twoXDegree = 2 * mXDegree;
135  int twoYDegree = 2 * mYDegree;
136  int twoZDegree = 2 * mZDegree;
137  Array2<Real> xPower(twoXDegree + 1, numSamples);
138  Array2<Real> yPower(twoYDegree + 1, numSamples);
139  Array2<Real> zPower(twoZDegree + 1, numSamples);
140  for (s = 0; s < numSamples; ++s)
141  {
142  Real x = observations[indices[s]][0];
143  Real y = observations[indices[s]][1];
144  Real z = observations[indices[s]][2];
145  mXDomain[0] = std::min(x, mXDomain[0]);
146  mXDomain[1] = std::max(x, mXDomain[1]);
147  mYDomain[0] = std::min(y, mYDomain[0]);
148  mYDomain[1] = std::max(y, mYDomain[1]);
149  mZDomain[0] = std::min(z, mZDomain[0]);
150  mZDomain[1] = std::max(z, mZDomain[1]);
151 
152  xPower[s][0] = (Real)1;
153  for (i0 = 1; i0 <= twoXDegree; ++i0)
154  {
155  xPower[s][i0] = x * xPower[s][i0 - 1];
156  }
157 
158  yPower[s][0] = (Real)1;
159  for (j0 = 1; j0 <= twoYDegree; ++j0)
160  {
161  yPower[s][j0] = y * yPower[s][j0 - 1];
162  }
163 
164  zPower[s][0] = (Real)1;
165  for (k0 = 1; k0 <= twoZDegree; ++k0)
166  {
167  zPower[s][k0] = z * zPower[s][k0 - 1];
168  }
169  }
170 
171  // Matrix A is the Vandermonde matrix and vector B is the right-hand
172  // side of the linear system A*X = B.
174  GVector<Real> B(mSize);
175  for (k0 = 0; k0 <= mZDegree; ++k0)
176  {
177  for (j0 = 0; j0 <= mYDegree; ++j0)
178  {
179  for (i0 = 0; i0 <= mXDegree; ++i0)
180  {
181  Real sum = (Real)0;
182  n0 = i0 + mXDegreeP1*(j0 + mYDegreeP1*k0);
183  for (s = 0; s < numSamples; ++s)
184  {
185  Real w = observations[indices[s]][3];
186  sum += w * xPower[s][i0] * yPower[s][j0] *
187  zPower[s][k0];
188  }
189 
190  B[n0] = sum;
191 
192  for (k1 = 0; k1 <= mZDegree; ++k1)
193  {
194  for (j1 = 0; j1 <= mYDegree; ++j1)
195  {
196  for (i1 = 0; i1 <= mXDegree; ++i1)
197  {
198  sum = (Real)0;
199  n1 = i1 + mXDegreeP1*(j1 + mYDegreeP1*k1);
200  for (s = 0; s < numSamples; ++s)
201  {
202  sum += xPower[s][i0 + i1] *
203  yPower[s][j0 + j1] *
204  zPower[s][k0 + k1];
205  }
206 
207  A(n0, n1) = sum;
208  }
209  }
210  }
211  }
212  }
213  }
214 
215  // Solve for the polynomial coefficients.
216  GVector<Real> coefficients = Inverse(A) * B;
217  bool hasNonzero = false;
218  for (int i = 0; i < mSize; ++i)
219  {
220  mParameters[i] = coefficients[i];
221  if (coefficients[i] != (Real)0)
222  {
223  hasNonzero = true;
224  }
225  }
226  return hasNonzero;
227  }
228 
229  std::fill(mParameters.begin(), mParameters.end(), (Real)0);
230  return false;
231 }
232 
233 template <typename Real>
234 Real ApprPolynomial4<Real>::Error(std::array<Real, 4> const& observation)
235 const
236 {
237  Real w = Evaluate(observation[0], observation[1], observation[2]);
238  Real error = std::abs(w - observation[3]);
239  return error;
240 }
241 
242 template <typename Real>
243 std::vector<Real> const& ApprPolynomial4<Real>::GetParameters() const
244 {
245  return mParameters;
246 }
247 
248 template <typename Real>
249 std::array<Real, 2> const& ApprPolynomial4<Real>::GetXDomain() const
250 {
251  return mXDomain;
252 }
253 
254 template <typename Real>
255 std::array<Real, 2> const& ApprPolynomial4<Real>::GetYDomain() const
256 {
257  return mYDomain;
258 }
259 
260 template <typename Real>
261 std::array<Real, 2> const& ApprPolynomial4<Real>::GetZDomain() const
262 {
263  return mZDomain;
264 }
265 
266 template <typename Real>
267 Real ApprPolynomial4<Real>::Evaluate(Real x, Real y, Real z) const
268 {
269  int i0, i1, i2;
270  Real w;
271 
272  for (i2 = 0; i2 <= mZDegree; ++i2)
273  {
274  for (i1 = 0; i1 <= mYDegree; ++i1)
275  {
276  i0 = mXDegree;
277  w = mParameters[i0 + mXDegreeP1 * (i1 + mYDegreeP1 * i2)];
278  while (--i0 >= 0)
279  {
280  w = mParameters[i0 + mXDegreeP1 * (i1 + mYDegreeP1 * i2)]
281  + w * x;
282  }
283  mYZCoefficient[i1 + mYDegree * i2];
284  }
285  }
286 
287  for (i2 = 0; i2 <= mZDegree; ++i2)
288  {
289  i1 = mYDegree;
290  w = mYZCoefficient[i1 + mYDegreeP1 * i2];
291  while (--i1 >= 0)
292  {
293  w = mParameters[i1 + mYDegreeP1 * i2] + w * y;
294  }
295  mZCoefficient[i2] = w;
296  }
297 
298  i2 = mZDegree;
299  w = mZCoefficient[i2];
300  while (--i2 >= 0)
301  {
302  w = mZCoefficient[i2] + w * z;
303  }
304 
305  return w;
306 }
307 
308 
309 }
std::array< Real, 2 > mZDomain
std::vector< Real > mParameters
Real Evaluate(Real x, Real y, Real z) const
bool Fit(std::vector< std::array< Real, 4 >> const &observations, std::vector< int > const &indices)
gte::BSNumber< UIntegerType > abs(gte::BSNumber< UIntegerType > const &number)
Definition: GteBSNumber.h:966
std::array< Real, 2 > const & GetZDomain() const
ApprPolynomial4(int xDegree, int yDegree, int zDegree)
std::vector< Real > const & GetParameters() const
int GetMinimumRequired() const
GLint GLenum GLint x
Definition: glcorearb.h:404
Real Error(std::array< Real, 4 > const &observation) const
GLubyte GLubyte GLubyte GLubyte w
Definition: glcorearb.h:852
std::array< Real, 2 > const & GetYDomain() const
GLsizei GLenum const void * indices
Definition: glcorearb.h:401
GLdouble GLdouble GLdouble z
Definition: glcorearb.h:843
GLdouble s
Definition: glext.h:231
std::vector< Real > mYZCoefficient
Quaternion< Real > Inverse(Quaternion< Real > const &d)
std::array< Real, 2 > mXDomain
std::vector< Real > mZCoefficient
std::array< Real, 2 > mYDomain
GLint y
Definition: glcorearb.h:98
std::array< Real, 2 > const & GetXDomain() const


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