GteApprPolynomialSpecial2.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]}
17 // where p[i] are distinct nonnegative powers provided by the caller. A
18 // least-squares fitting algorithm is used, but the input data is first
19 // mapped to (x,w) in [-1,1]^2 for numerical robustness.
20 
21 namespace gte
22 {
23 
24 template <typename Real>
26  :
27  public ApprQuery<Real, ApprPolynomialSpecial2<Real>, std::array<Real, 2>>
28 {
29 public:
30  // Initialize the model parameters to zero. The degrees must be
31  // nonnegative and strictly increasing.
32  ApprPolynomialSpecial2(std::vector<int> const& degrees);
33 
34  // The minimum number of observations required to fit the model.
35  int GetMinimumRequired() const;
36 
37  // Estimate the model parameters for all observations specified by the
38  // indices. This function is called by the base-class Fit(...) functions.
39  bool Fit(std::vector<std::array<Real, 2>> const& observations,
40  std::vector<int> const& indices);
41 
42  // Compute the model error for the specified observation for the current
43  // model parameters. The returned value for observation (x0,w0) is
44  // |w(x0) - w0|, where w(x) is the fitted polynomial.
45  Real Error(std::array<Real, 2> const& observation) const;
46 
47  // Get the parameters of the model.
48  std::vector<Real> const& GetParameters() const;
49 
50  // Evaluate the polynomial. The domain interval is provided so you can
51  // interpolate (x in domain) or extrapolate (x not in domain).
52  std::array<Real, 2> const& GetXDomain() const;
53  Real Evaluate(Real x) const;
54 
55 private:
56  // Transform the (x,w) values to (x',w') in [-1,1]^2.
57  void Transform(std::vector<std::array<Real, 2>> const& observations,
58  std::vector<int> const& indices,
59  std::vector<std::array<Real, 2>>& transformed);
60 
61  // The least-squares fitting algorithm for the transformed data.
62  bool DoLeastSquares(std::vector<std::array<Real, 2>>& transformed);
63 
64  std::vector<int> mDegrees;
65  std::vector<Real> mParameters;
66 
67  // Support for evaluation. The coefficients were generated for the
68  // samples mapped to [-1,1]^2. The Evaluate() function must transform
69  // x to x' in [-1,1], compute w' in [-1,1], then transform w' to w.
70  std::array<Real, 2> mXDomain, mWDomain;
71  std::array<Real, 2> mScale;
73 
74  // This array is used by Evaluate() to avoid reallocation of the 'vector'
75  // for each call. The member is mutable because, to the user, the call
76  // to Evaluate does not modify the polynomial.
77  mutable std::vector<Real> mXPowers;
78 };
79 
80 
81 template <typename Real>
83  std::vector<int> const& degrees)
84  :
85  mDegrees(degrees),
86  mParameters(degrees.size())
87 {
88 #if !defined(GTE_NO_LOGGER)
89  LogAssert(mDegrees.size() > 0, "The input array must have elements.");
90  int lastDegree = -1;
91  for (auto degree : mDegrees)
92  {
93  LogAssert(degree > lastDegree, "Degrees must be increasing.");
94  lastDegree = degree;
95  }
96 #endif
97 
98  mXDomain[0] = std::numeric_limits<Real>::max();
99  mXDomain[1] = -mXDomain[0];
100  mWDomain[0] = std::numeric_limits<Real>::max();
101  mWDomain[1] = -mWDomain[0];
102  std::fill(mParameters.begin(), mParameters.end(), (Real)0);
103 
104  mScale[0] = (Real)0;
105  mScale[1] = (Real)0;
106  mInvTwoWScale = (Real)0;
107 
108  // Powers of x are computed up to twice the powers when constructing the
109  // fitted polynomial. Powers of x are computed up to the powers for the
110  // evaluation of the fitted polynomial.
111  mXPowers.resize(2 * mDegrees.back() + 1);
112  mXPowers[0] = (Real)1;
113 }
114 
115 template <typename Real>
117 {
118  return static_cast<int>(mParameters.size());
119 }
120 
121 template <typename Real>
123  std::vector<std::array<Real, 2>> const& observations,
124  std::vector<int> const& indices)
125 {
126  if (indices.size() > 0)
127  {
128  // Transform the observations to [-1,1]^2 for numerical robustness.
129  std::vector<std::array<Real, 2>> transformed;
130  Transform(observations, indices, transformed);
131 
132  // Fit the transformed data using a least-squares algorithm.
133  return DoLeastSquares(transformed);
134  }
135 
136  std::fill(mParameters.begin(), mParameters.end(), (Real)0);
137  return false;
138 }
139 
140 template <typename Real>
142  std::array<Real, 2> const& observation) const
143 {
144  Real w = Evaluate(observation[0]);
145  Real error = std::abs(w - observation[1]);
146  return error;
147 }
148 
149 template <typename Real>
150 std::vector<Real> const& ApprPolynomialSpecial2<Real>::GetParameters() const
151 {
152  return mParameters;
153 }
154 
155 template <typename Real>
156 std::array<Real, 2> const& ApprPolynomialSpecial2<Real>::GetXDomain() const
157 {
158  return mXDomain;
159 }
160 
161 template <typename Real>
163 {
164  // Transform x to x' in [-1,1].
165  x = (Real)-1 + ((Real)2) * mScale[0] * (x - mXDomain[0]);
166 
167  // Compute relevant powers of x.
168  int jmax = mDegrees.back();
169  for (int j = 1; j <= jmax; ++j)
170  {
171  mXPowers[j] = mXPowers[j - 1] * x;
172  }
173 
174  Real w = (Real)0;
175  int isup = static_cast<int>(mDegrees.size());
176  for (int i = 0; i < isup; ++i)
177  {
178  Real xp = mXPowers[mDegrees[i]];
179  w += mParameters[i] * xp;
180  }
181 
182  // Transform w from [-1,1] back to the original space.
183  w = (w + (Real)1) * mInvTwoWScale + mWDomain[0];
184  return w;
185 }
186 
187 template <typename Real>
189  std::vector<std::array<Real, 2>> const& observations,
190  std::vector<int> const& indices,
191  std::vector<std::array<Real, 2>>& transformed)
192 {
193  int numSamples = static_cast<int>(indices.size());
194  transformed.resize(numSamples);
195 
196  std::array<Real, 2> omin = observations[indices[0]];
197  std::array<Real, 2> omax = omin;
198  std::array<Real, 2> obs;
199  int s, i;
200  for (s = 1; s < numSamples; ++s)
201  {
202  obs = observations[indices[s]];
203  for (i = 0; i < 2; ++i)
204  {
205  if (obs[i] < omin[i])
206  {
207  omin[i] = obs[i];
208  }
209  else if (obs[i] > omax[i])
210  {
211  omax[i] = obs[i];
212  }
213  }
214  }
215 
216  mXDomain[0] = omin[0];
217  mXDomain[1] = omax[0];
218  mWDomain[0] = omin[1];
219  mWDomain[1] = omax[1];
220  for (i = 0; i < 2; ++i)
221  {
222  mScale[i] = ((Real)1) / (omax[i] - omin[i]);
223  }
224 
225  for (s = 0; s < numSamples; ++s)
226  {
227  obs = observations[indices[s]];
228  for (i = 0; i < 2; ++i)
229  {
230  transformed[s][i] = (Real)-1 + ((Real)2) * mScale[i] *
231  (obs[i] - omin[i]);
232  }
233  }
234  mInvTwoWScale = ((Real)0.5) / mScale[1];
235 }
236 
237 template <typename Real>
239  std::vector<std::array<Real, 2>>& transformed)
240 {
241  // Set up a linear system A*X = B, where X are the polynomial
242  // coefficients.
243  int size = static_cast<int>(mDegrees.size());
244  GMatrix<Real> A(size, size);
245  A.MakeZero();
246  GVector<Real> B(size);
247  B.MakeZero();
248 
249  int numSamples = static_cast<int>(transformed.size());
250  int twoMaxXDegree = 2 * mDegrees.back();
251  int row, col;
252  for (int i = 0; i < numSamples; ++i)
253  {
254  // Compute relevant powers of x.
255  Real x = transformed[i][0];
256  Real w = transformed[i][1];
257  for (int j = 0; j <= twoMaxXDegree; ++j)
258  {
259  mXPowers[j] = mXPowers[j - 1] * x;
260  }
261 
262  for (row = 0; row < size; ++row)
263  {
264  // Update the upper-triangular portion of the symmetric matrix.
265  for (col = row; col < size; ++col)
266  {
267  A(row, col) += mXPowers[mDegrees[row] + mDegrees[col]];
268  }
269 
270  // Update the right-hand side of the system.
271  B[row] += mXPowers[mDegrees[row]] * w;
272  }
273  }
274 
275  // Copy the upper-triangular portion of the symmetric matrix to the
276  // lower-triangular portion.
277  for (row = 0; row < size; ++row)
278  {
279  for (col = 0; col < row; ++col)
280  {
281  A(row, col) = A(col, row);
282  }
283  }
284 
285  // Precondition by normalizing the sums.
286  Real invNumSamples = ((Real)1) / (Real)numSamples;
287  A *= invNumSamples;
288  B *= invNumSamples;
289 
290  // Solve for the polynomial coefficients.
291  GVector<Real> coefficients = Inverse(A) * B;
292  bool hasNonzero = false;
293  for (int i = 0; i < size; ++i)
294  {
295  mParameters[i] = coefficients[i];
296  if (coefficients[i] != (Real)0)
297  {
298  hasNonzero = true;
299  }
300  }
301  return hasNonzero;
302 }
303 
304 
305 }
std::vector< Real > const & GetParameters() const
bool DoLeastSquares(std::vector< std::array< Real, 2 >> &transformed)
gte::BSNumber< UIntegerType > abs(gte::BSNumber< UIntegerType > const &number)
Definition: GteBSNumber.h:966
#define LogAssert(condition, message)
Definition: GteLogger.h:86
void Transform(std::vector< std::array< Real, 2 >> const &observations, std::vector< int > const &indices, std::vector< std::array< Real, 2 >> &transformed)
GLint GLenum GLint x
Definition: glcorearb.h:404
GLsizeiptr size
Definition: glcorearb.h:659
bool Fit(std::vector< std::array< Real, 2 >> const &observations, std::vector< int > const &indices)
GLubyte GLubyte GLubyte GLubyte w
Definition: glcorearb.h:852
GLenum GLenum GLsizei void * row
Definition: glext.h:2725
GLsizei GLenum const void * indices
Definition: glcorearb.h:401
ApprPolynomialSpecial2(std::vector< int > const &degrees)
std::array< Real, 2 > const & GetXDomain() const
void MakeZero()
Definition: GteGVector.h:245
GLdouble s
Definition: glext.h:231
Quaternion< Real > Inverse(Quaternion< Real > const &d)
Real Error(std::array< Real, 2 > const &observation) const
void MakeZero()
Definition: GteGMatrix.h:387


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