G+Smo  25.01.0
Geometry + Simulation Modules
 
Loading...
Searching...
No Matches
gsSolutionFitter.h
Go to the documentation of this file.
1
15#include <gsIO/gsOptionList.h>
16
17#pragma once
18
19namespace gismo
20{
21
22template<class T>
23class gsSolutionFitter
24{
25public:
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
69protected:
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
82public:
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
233protected:
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
#define index_t
Definition gsConfig.h:32
#define GISMO_ERROR(message)
Definition gsDebug.h:118
#define GISMO_ENSURE(cond, message)
Definition gsDebug.h:102
#define GISMO_ASSERT(cond, message)
Definition gsDebug.h:89
Provides declaration of data fitting algorithms by least squares approximation.
Provides a list of labeled parameters/options that can be set and accessed easily.
The G+Smo namespace, containing all definitions for the library.
S give(S &x)
Definition gsMemory.h:266
gsMatrix< T > uniformPointGrid(const gsVector< T > &lower, const gsVector< T > &upper, int numPoints=1000)
Definition gsPointGrid.hpp:94