G+Smo  24.08.0
Geometry + Simulation Modules
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
gsSolutionFitter.h
Go to the documentation of this file.
1 
14 #include <gsModeling/gsFitting.h>
15 #include <gsIO/gsOptionList.h>
16 
17 #pragma once
18 
19 namespace gismo
20 {
21 
22 template<class T>
23 class gsSolutionFitter
24 {
25 public:
26  gsSolutionFitter ( const std::vector<gsVector<T>> & data,
27  const gsVector<T> & times,
28  const index_t deg = 2)
29  :
30  m_deg(deg)
31  {
32  this->_defaultOptions();
33 
34  GISMO_ENSURE(data.size() != 0,"No data provided!");
35  GISMO_ENSURE(data.size() == (size_t)times.size(),"Solution coefs and times should have the same size");
36  // GISMO_ENSURE((size_t)m_ptimes.size() == (size_t)m_times.size(),"(Parametric)times should have the same size");
37 
38  for (index_t k=0; k!=times.size(); k++)
39  m_data.insert({times[k],data[k]});
40  }
41 
42  // gsSolutionFitter ( const std::vector<gsVector<T>> & data,
43  // const gsVector<T> & times,
44  // const gsVector<T> & ptimes,
45  // const index_t deg = 2)
46  // :
47  // {
48  // std::vector<gsVector<T>> tmp = data;
49  // GISMO_ENSURE(data.size() == (size_t)times.rows(),"Solution coefs and times should have the same size");
50  // for (size_t k = 0; k!=data.size(); k++)
51  // {
52  // tmp[k] = conservativeResize(tmp[k].rows()+1);
53  // tmp[k].tail() = times[k];
54  // }
55 
56  // gsSolutionFitter<T>(tmp,ptimes,deg);
57  // }
58 
59  void setDegree(index_t deg) {m_deg = deg;}
60 
61  void addDataPoint(gsVector<T> & point, T ptime)//, index_t continuity )
62  {
63  m_data.insert({ptime,point});
64  gsDebugVar(m_data.size());
65  }
66 
67  gsOptionList& options() {return m_options;}
68 
69 protected:
70 
71  void _defaultOptions()
72  {
73  m_options.addInt("Parameterization","Type of parameterization to be used: 0: uniform, 1: chord-length, 2: centripetal",1);
74  }
75 
76  gsBSplineBasis<T> _basis(const gsKnotVector<T> &kv, index_t nsteps)
77  {
78  gsBSplineBasis<T> tbasis(kv);
79  return tbasis;
80  }
81 
82 public:
83 
84  const gsBSpline<T> & fit() const {return m_fit; }
85 
86  gsVector<T> slice(T xi)
87  {
88  gsVector<T> pt(1);
89  pt<<xi;
90  return m_fit.eval(pt);
91  }
92 
93  T nearestParam(const gsMatrix<T> & solution, gsMatrix<T> & params, index_t nTrialPoints = 25, index_t nIterations = 100)
94  {
95  gsMatrix<T> supp = m_fit.support();
96  gsVector<T> start = supp.col(0), end = supp.col(1);
97  gsMatrix<T> trialPoints = uniformPointGrid(start, end, nTrialPoints);
98  gsMatrix<T> surfVal, surfDeriv, surfDeriv2;
99  gsMatrix<T> closestParam;
100  gsMatrix<T> u, du;
101  T tol = 1e-8;
102  T err = 1;
103  T closestSqDist(10e100);
104 
105  for(int idxTrial = 0; idxTrial < nTrialPoints; idxTrial++)
106  {
107  u = trialPoints.col(idxTrial);
108  // apply Newton's method to the function
109  // f(u) = ||p - x(u)||^2
110  // where x(u) is the parametrisation of the curve in space.
111  // (todo - also check the distances at the endpoints of the curve?
112  // although this is for splitting the curve and we would never want
113  // to split at the endpoint.)
114  for(int iteration = 0; iteration < nIterations; iteration++)
115  {
116  m_fit.eval_into(u, surfVal);
117  m_fit.jacobian_into(u, surfDeriv);
118  m_fit.deriv2_into(u, surfDeriv2);
119 
120  // evaluate derivative of f
121  gsMatrix<T> sqDistDeriv = -2 * (solution - surfVal).transpose() *
122  surfDeriv;
123  GISMO_ASSERT(sqDistDeriv.rows() == 1 && sqDistDeriv.cols() == 1, "Derivative should be 1x1");
124 
125  gsMatrix<T> sqDistDeriv2 = 2*surfDeriv.transpose() * surfDeriv - 2*(solution - surfVal).transpose() * surfDeriv2;
126  GISMO_ASSERT(sqDistDeriv2.rows() == 1 && sqDistDeriv2.cols() == 1, "Second derivative should be 1x1");
127 
128  du = sqDistDeriv / sqDistDeriv2(0, 0);
129  u -= du;
130 
131  if (du.norm()/u.norm() < tol)
132  break;
133 
134  u(0, 0) = (u(0, 0) < supp(0, 0))? supp(0, 0): ((u(0, 0) > supp(0, 1)? supp(0, 1): u(0, 0)));
135  }
136  // compute sqDist for the point found by the last iteration, and compare against the best seen so far
137  m_fit.eval_into(u, surfVal);
138  T sqDist = (solution - surfVal).squaredNorm();
139  if(idxTrial == 0 || sqDist < closestSqDist)
140  {
141  closestParam = u;
142  closestSqDist = sqDist;
143  }
144  }
145  return closestParam.value();
146  }
147 
148  void compute()
149  {
150  GISMO_ASSERT(m_data.size()>(m_deg),"Data needs to have more than "<<m_deg<<" points, but only "<<m_data.size()<<" are stored!");
151  index_t nsteps = m_data.size();
152 
153  gsDebugVar(nsteps);
154 
155  // gsMatrix<T> ptimes(1,nsteps);
156  std::vector<T> ptimes(nsteps);
157  gsMatrix<T> rhs(nsteps,m_data.at(0).rows());
158  index_t k=0;
159  for(typename std::map<T,gsVector<T>>::iterator it = m_data.begin(); it != m_data.end(); ++it, ++k)
160  {
161  // ptimes(0,k) = it->first;
162  ptimes.at(k) = it->first;
163  rhs.row(k) = it->second;
164  }
165 
166  // Prepare fitting basis
167  std::vector<T> u_bar(ptimes.size());
168  u_bar.front() = 0;
169  u_bar.back() = 1;
170 
171  gsKnotVector<T> kv;
172  index_t param = m_options.getInt("Parameterization");
173  if (param==0)
174  {
175  kv = gsKnotVector<T>(gsAsVector<T>(ptimes).minCoeff(),gsAsVector<T>(ptimes).maxCoeff(),nsteps-(m_deg+1),m_deg+1, 1);
176  }
177  else if (param==1 || param==2)
178  {
179  // Chord-length parameterization
180  T d = 0.;
181  for (typename std::vector<T>::iterator pit = std::next(ptimes.begin()); pit!=ptimes.end(); pit++)
182  d += math::pow(*pit - *std::prev(pit),1./param);
183 
184  typename std::vector<T>::iterator pit = std::next(ptimes.begin());
185  for (typename std::vector<T>::iterator it=std::next(u_bar.begin()); it!=std::prev(u_bar.end()); it++, pit++)
186  *it = *std::prev(it) + 1. / d * math::pow(*pit - *std::prev(pit),1./param);
187 
188  std::vector<T> u(nsteps+m_deg+1);
189  for (index_t k=0; k!=m_deg+1; k++)
190  {
191  u.at(k) = 0;
192  u.at(u.size()-k-1) = 1;
193  }
194  for (index_t j=1; j!=nsteps-m_deg; j++)
195  {
196  u.at(j+m_deg) = 0;
197  for (index_t i=j; i!=j+m_deg; i++)
198  u.at(j+m_deg) += u_bar.at(i);
199 
200  u.at(j+m_deg) /= m_deg;
201  }
202 
203  kv = gsKnotVector<T>(u,m_deg);
204  kv.affineTransformTo(ptimes.front(),ptimes.back());
205  }
206  else
207  GISMO_ERROR("Parameterization unknown");
208 
209 
210  m_basis = gsBSplineBasis<T>(kv);
211 
212  // get the Greville Abcissae (anchors)
213  gsMatrix<T> anchors = m_basis.anchors();
214 
215  // Get the collocation matrix at the anchors
216  gsSparseMatrix<T> C = m_basis.collocationMatrix(anchors);
217 
218  // gsSparseSolver<>::LU solver;
219  // solver.compute(C);
220 
221  // m_coefs = solver.solve(rhs);
222  // m_fit = gsBSpline<T>(m_basis,give(m_coefs));
223 
224  gsFitting<T> fitter(gsAsMatrix<T>(ptimes,1,ptimes.size()),rhs.transpose(),m_basis);
225  gsSparseMatrix<T> lhs(nsteps,nsteps);
226  lhs.setIdentity();
227  fitter.setConstraints(C,rhs);
228  fitter.compute(0.0);
229 
230  m_fit = gsBSpline<T>(m_basis,give(fitter.result()->coefs()));
231  }
232 
233 protected:
234  std::map<T,gsVector<T>> m_data;
235 
236  gsVector<T> m_ptimes;
237  index_t m_deg;
238 
239  mutable gsBSplineBasis<T> m_basis;
240  gsMatrix<T> m_coefs;
241 
242  mutable gsBSpline<T> m_fit;
243 
244  gsOptionList m_options;
245 
246 };
247 
248 
249 } // namespace gismo
250 
251 // #ifndef GISMO_BUILD_LIB
252 // #include GISMO_HPP_HEADER(gsSpaceTimeHierarchy.hpp)
253 // #endif
S give(S &x)
Definition: gsMemory.h:266
Provides declaration of data fitting algorithms by least squares approximation.
#define index_t
Definition: gsConfig.h:32
#define GISMO_ENSURE(cond, message)
Definition: gsDebug.h:102
#define GISMO_ASSERT(cond, message)
Definition: gsDebug.h:89
gsMatrix< T > uniformPointGrid(const gsVector< T > &lower, const gsVector< T > &upper, int numPoints=1000)
Definition: gsPointGrid.hpp:94
Provides a list of labeled parameters/options that can be set and accessed easily.
#define GISMO_ERROR(message)
Definition: gsDebug.h:118