OpenFPM  5.2.0
Project that contain the implementation of distributed structures
regression.hpp
1 /*
2  * Regression module
3  * Obtains polynomial models for data from vector_dist
4  * author : sachin (sthekke@mpi-cbg.de)
5  * date : 28.04.2022
6  *
7  */
8 #ifndef OPENFPM_NUMERICS_REGRESSION_HPP
9 #define OPENFPM_NUMERICS_REGRESSION_HPP
10 
11 #include "Vector/map_vector.hpp"
12 #include "Space/Shape/Point.hpp"
13 #include "DMatrix/EMatrix.hpp"
14 #include "DCPSE/SupportBuilder.hpp"
15 #include "minter/minter.h"
16 
17 
18 template<typename vector_type_support, typename NN_type>
20 {
22 
23 public:
24 
25  template<typename iterator_type>
26  RegressionSupport(vector_type_support &vd, iterator_type itPoint, unsigned int requiredSize, support_options opt, NN_type &cellList_in) : domain(vd),
27  cellList(cellList_in)
28  {
29  rCut = cellList_in.getCellBox().getHigh(0);
30  // Get spatial position from point iterator
31  vect_dist_key_dx p = itPoint.get();
33 
34  // Get cell containing current point and add it to the set of cell keys
35  grid_key_dx<vector_type_support::dims> curCellKey = cellList.getCellGrid(pos); // Here get the key of the cell where the current point is
36  std::set<grid_key_dx<vector_type_support::dims>> supportCells;
37  supportCells.insert(curCellKey);
38 
39  // Make sure to consider a set of cells providing enough points for the support
40  enlargeSetOfCellsUntilSize(supportCells, requiredSize + 1,
41  opt); // NOTE: this +1 is because we then remove the point itself
42 
43  // Now return all the points from the support into a vector
44  keys = getPointsInSetOfCells(supportCells, p, requiredSize, opt);
45  }
46 
47  // additional constructor if keys are known already
48  RegressionSupport(openfpm::vector<size_t> keysIn, vector_type_support &vd, NN_type &cellList_in) : keys(keysIn), domain(vd), cellList(cellList_in)
49  {
50  rCut = cellList_in.getCellBox().getHigh(0);
51  }
52 
53  auto getKeys()
54  {
55  return keys;
56  }
57 
58  auto getNumParticles()
59  {
60  return keys.size();
61  }
62 
63 private:
64 
65  vector_type_support &domain;
66  NN_type &cellList;
67  typename vector_type_support::stype rCut;
68 
69 
70  void enlargeSetOfCellsUntilSize(std::set<grid_key_dx<vector_type_support::dims>> &set, unsigned int requiredSize,
71  support_options opt) {
72  if (opt == support_options::RADIUS) {
73  auto cell = *set.begin();
75  int n = std::ceil(rCut / cellList.getCellBox().getHigh(0));
76  size_t sz[vector_type_support::dims];
77  for (int i = 0; i < vector_type_support::dims; i++) {
78  sz[i] = 2 * n + 1;
79  middle.set_d(i, n);
80  }
83  while (g_k.isNext()) {
84  auto key = g_k.get();
85  key = cell + key - middle;
86  if (isCellKeyInBounds(key)) {
87  set.insert(key);
88  }
89  ++g_k;
90  }
91  }
92  else if (opt == support_options::AT_LEAST_N_PARTICLES) {
93 
94  int done = 0;
95  int n = 1;
96  auto cell = *set.begin();
98  size_t sz[vector_type_support::dims];
99 
100  while(true) // loop for the number of cells enlarged per dimension
101  {
102  std::set<grid_key_dx<vector_type_support::dims>> temp_set;
103  for (int i = 0; i < vector_type_support::dims; i++) {
104  sz[i] = 2 * n + 1;
105  middle.set_d(i, n);
106  }
107 
110  while (g_k.isNext()) {
111  auto key = g_k.get();
112  key = cell + key - middle;
113  if (isCellKeyInBounds(key)) {
114  temp_set.insert(key);
115  }
116  ++g_k;
117  }
118  if (getNumElementsInSetOfCells(temp_set) < requiredSize) n++;
119  else
120  {
121  set = temp_set;
122  break;
123  }
124  }
125  }
126  else {
127  while (getNumElementsInSetOfCells(set) <
128  5.0 * requiredSize) //Why 5*requiredSize? Becasue it can help with adaptive resolutions.
129  {
130  auto tmpSet = set;
131  for (const auto el: tmpSet) {
132  for (unsigned int i = 0; i < vector_type_support::dims; ++i) {
133  const auto pOneEl = el.move(i, +1);
134  const auto mOneEl = el.move(i, -1);
135  if (isCellKeyInBounds(pOneEl)) {
136  set.insert(pOneEl);
137  }
138  if (isCellKeyInBounds(mOneEl)) {
139  set.insert(mOneEl);
140  }
141  }
142  }
143 
144  }
145  }
146  }
147 
148  openfpm::vector<size_t> getPointsInSetOfCells(std::set<grid_key_dx<vector_type_support::dims>> set,
149  vect_dist_key_dx &p,
150  size_t requiredSupportSize,
151  support_options opt) {
152  struct reord {
153  typename vector_type_support::stype dist;
154  size_t offset;
155 
156  bool operator<(const reord &p) const { return this->dist < p.dist; }
157  };
158 
162  for (const auto cellKey: set) {
163  const size_t cellLinId = getCellLinId(cellKey);
164  const size_t elemsInCell = getNumElementsInCell(cellKey);
165  for (size_t k = 0; k < elemsInCell; ++k) {
166  size_t el = cellList.get(cellLinId, k);
167 
169  //points.push_back(el);
170 
171  reord pr;
172 
173  pr.dist = xp.distance(xq);
174  pr.offset = el;
175  rp.add(pr);
176  }
177  }
178 
179  if (opt == support_options::RADIUS) {
180  for (int i = 0; i < rp.size(); i++) {
181  if (rp.get(i).dist < rCut) {
182  points.add(rp.get(i).offset);
183  }
184  }
185  /* #ifdef SE_CLASS1
186  if (points.size()<requiredSupportSize)
187  {
188  std::cerr<<__FILE__<<":"<<__LINE__<<"Note that the DCPSE neighbourhood doesn't have asked no. particles (Increase the rCut or reduce the over_sampling factor)";
189  std::cout<<"Particels asked (minimum*oversampling_factor): "<<requiredSupportSize<<". Particles Possible with given options:"<<points.size()<<"."<<std::endl;
190  }
191  #endif*/
192  }
193  else if (opt == support_options::AT_LEAST_N_PARTICLES) {
194  for (int i = 0; i < rp.size(); i++) points.add(rp.get(i).offset);
195  }
196  else {
197  rp.sort();
198  for (int i = 0; i < requiredSupportSize; i++) {
199  points.add(rp.get(i).offset);
200  }
201  }
202  //MinSpacing=MinSpacing/requiredSupportSize
203  return points;
204  }
205 
206  size_t getCellLinId(const grid_key_dx<vector_type_support::dims> &cellKey) {
207  mem_id id = cellList.getGrid().LinId(cellKey);
208  return static_cast<size_t>(id);
209  }
210 
211  size_t getNumElementsInCell(const grid_key_dx<vector_type_support::dims> &cellKey) {
212  const size_t curCellId = getCellLinId(cellKey);
213  size_t numElements = cellList.getNelements(curCellId);
214  return numElements;
215  }
216  size_t getNumElementsInSetOfCells(const std::set<grid_key_dx<vector_type_support::dims>> &set)
217  {
218  size_t tot = 0;
219  for (const auto cell : set)
220  {
221  tot += getNumElementsInCell(cell);
222  }
223  return tot;
224 }
225 
226  bool isCellKeyInBounds(grid_key_dx<vector_type_support::dims> key)
227  {
228  const size_t *cellGridSize = cellList.getGrid().getSize();
229  for (size_t i = 0; i < vector_type_support::dims; ++i)
230  {
231  if (key.value(i) < 0 || key.value(i) >= cellGridSize[i])
232  {
233  return false;
234  }
235  }
236  return true;
237  }
238 };
239 
240 
241 template<int spatial_dim, unsigned int prp_id, typename MatType = EMatrixXd, typename VecType = EVectorXd>
243 {
244 
245 public:
246  minter::PolyModel<spatial_dim, MatType, VecType> *model = nullptr;
247  minter::PolyModel<spatial_dim, MatType, VecType> *deriv_model[spatial_dim];
248 
249  RegressionModel(unsigned int poly_degree, float lp_degree) {
250  // construct polynomial model
251  model = new minter::PolyModel<spatial_dim, MatType, VecType>();
252  model->setDegree(poly_degree, lp_degree);
253 
254  // placeholder for derivatives
255  for(int i = 0;i < spatial_dim;++i)
256  deriv_model[i] = nullptr;
257  }
258 
259  template<typename vector_type, typename reg_support_type>
260  RegressionModel(vector_type &vd, reg_support_type &support, unsigned int poly_degree, float lp_degree = 2.0)
261  {
262  int num_particles = support->getNumParticles();
263  int dim = vector_type::dims;
264 
265  MatType points(num_particles, dim);
266  VecType values(num_particles);
267 
268  auto keys = support->getKeys();
269  for(int i = 0;i < num_particles;++i)
270  {
271  for(int j = 0;j < dim;++j)
272  points(i,j) = vd.getPos(keys.get(i))[j];
273  values(i) = vd.template getProp<prp_id>(keys.get(i));
274  }
275 
276  // construct polynomial model
277  model = new minter::PolyModel<spatial_dim, MatType, VecType>(points, values, poly_degree, lp_degree);
278 
279  // placeholder for derivatives
280  for(int i = 0;i < dim;++i)
281  deriv_model[i] = nullptr;
282  }
283 
284 
285  // Constructor for all points in a proc (domain + ghost) and a specified poly_degree
286  template<typename vector_type>
287  RegressionModel(vector_type &vd, unsigned int poly_degree, float lp_degree = 2.0)
288  {
289  int num_particles = vd.size_local_with_ghost();
290  int dim = vector_type::dims;
291 
292  MatType points(num_particles, dim);
293  VecType values(num_particles);
294 
295  auto it = vd.getDomainAndGhostIterator();
296  int i = 0;
297  while (it.isNext())
298  {
299  auto key = it.get();
300  for(int j = 0;j < dim;++j)
301  points(i,j) = vd.getPos(key)[j];
302 
303  values(i) = vd.template getProp<prp_id>(key);
304 
305  ++it;
306  ++i;
307  }
308 
309  // construct polynomial model
310  model = new minter::PolyModel<spatial_dim, MatType, VecType>(points, values, poly_degree, lp_degree);
311 
312  for(i = 0;i < dim;++i)
313  deriv_model[i] = nullptr;
314  }
315 
316  // Constructor for all points in a proc (domain + ghost) within a tolerance
317  template<typename vector_type>
318  RegressionModel(vector_type &vd, double tolerance)
319  {
320  int num_particles = vd.size_local_with_ghost();
321  int dim = vector_type::dims;
322 
323  MatType points(num_particles, dim);
324  VecType values(num_particles);
325 
326  auto it = vd.getDomainAndGhostIterator();
327  int i = 0;
328  while (it.isNext())
329  {
330  auto key = it.get();
331  for(int j = 0;j < dim;++j)
332  points(i,j) = vd.getPos(key)[j];
333 
334  values(i) = vd.template getProp<prp_id>(key);
335 
336  ++it;
337  ++i;
338  }
339 
340  int poly_degree = 1;
341  double error = -1.0;
342  minter::PolyModel<spatial_dim, MatType, VecType> *mdl = nullptr;
343 
344  do
345  {
346  ++poly_degree;
347  if(mdl)
348  delete mdl;
349 
350  // construct polynomial model
351  mdl = new minter::PolyModel<spatial_dim, MatType, VecType>(points, values, poly_degree, 2.0);
352 
353  // check if linf_error is within the tolerance
354  error = -1.0;
355  for(i = 0;i < num_particles;++i)
356  {
357  double pred = mdl->eval(points.block(i,0,1,dim))(0); // evaluated for one point
358  double err = std::abs(pred - values(i));
359  if (err > error)
360  error = err;
361  }
362  // std::cout<<"Fit of degree "<<poly_degree<<" with error = "<<error<<std::endl;
363 
364  }while(error > tolerance);
365 
366  model = mdl;
367 
368  for(i = 0;i < dim;++i)
369  deriv_model[i] = nullptr;
370 
371  }
372 
373  template<typename vector_type, typename reg_support_type>
374  void computeCoeffs(vector_type& vd, reg_support_type& support)
375  {
376 
377  unsigned int dim = vector_type::dims;
378  auto num_particles = support.getNumParticles();
379 
380  Eigen::MatrixXd points(num_particles, dim);
381  Eigen::VectorXd values(num_particles);
382 
383  auto keys = support.getKeys();
384  for(int i = 0;i < num_particles;++i)
385  {
386  for(int j = 0;j < dim;++j)
387  points(i,j) = vd.getPos(keys.get(i))[j];
388  values(i) = vd.template getProp<prp_id>(keys.get(i));
389  }
390 
391  model->computeCoeffs(points, values);
392  }
393 
394  // overload for multidimensional property
395  template<typename vector_type, typename reg_support_type>
396  void computeCoeffs(vector_type& vd, reg_support_type& support, size_t component)
397  {
398 
399  unsigned int dim = vector_type::dims;
400  auto num_particles = support.getNumParticles();
401 
402  Eigen::MatrixXd points(num_particles, dim);
403  Eigen::VectorXd values(num_particles);
404 
405  auto keys = support.getKeys();
406  for(int i = 0;i < num_particles;++i)
407  {
408  for(int j = 0;j < dim;++j)
409  points(i,j) = vd.getPos(keys.get(i))[j];
410  values(i) = vd.template getProp<prp_id>(keys.get(i))[component];
411  }
412 
413  model->computeCoeffs(points, values);
414  }
415 
416  ~RegressionModel()
417  {
418 
419  if(model)
420  delete model;
421 
422  for(int i = 0;i < spatial_dim;++i)
423  {
424  if(deriv_model[i])
425  delete deriv_model[i];
426  }
427  }
428 
429  template<typename T> // Typical: Point<vector_type::dims, typename vector_type::stype>
430  double eval(T pos)
431  {
432  int dim = pos.dims;
433  MatType point(1,dim);
434  for(int j = 0;j < dim;++j)
435  point(0,j) = pos.get(j);
436 
437  return model->eval(point)(0);
438  }
439 
440  // T1 : Point<vector_type::dims, typename vector_type::stype>
441  // T2 : Point<vector_type::dims, int>
442  template<typename T1, typename T2>
443  double deriv(T1 pos, T2 deriv_order)
444  {
445 
446  int dim = pos.dims;
447  MatType point(1,dim);
448  for(int j = 0;j < dim;++j)
449  point(0,j) = pos.get(j);
450 
451  std::vector<int> order;
452  for(int j = 0;j < dim;++j)
453  order.push_back(deriv_order.get(j));
454 
455  return model->deriv_eval(point, order)(0);
456  }
457 
458  void compute_grad()
459  {
460  for(int i = 0;i < spatial_dim;++i)
461  {
462  std::vector<int> ord(spatial_dim, 0);
463  ord[i] = 1;
464  deriv_model[i] = model->derivative(ord);
465  }
466  }
467 
468  // T: Point<vector_type::dims, typename vector_type::stype>
469  template<typename T>
470  T eval_grad(T pos)
471  {
472  T res;
473 
474  if(!deriv_model[0])
475  compute_grad();
476 
477  for(int i = 0;i < spatial_dim;++i)
478  res.get(i) = deriv_model[i]->eval(pos);
479 
480  return res;
481  }
482 
483 };
484 
485 
486 #endif /* REGRESSION_HPP_ */
This class implement the point shape in an N-dimensional space.
Definition: Point.hpp:28
__device__ __host__ T distance(const Point< dim, T > &q) const
It calculate the distance between 2 points.
Definition: Point.hpp:265
const grid_key_dx< dim > & get() const
Get the actual key.
bool isNext()
Check if there is the next element.
grid_key_dx is the key to access any element in the grid
Definition: grid_key.hpp:19
__device__ __host__ mem_id value(size_t i) const
Get the i index.
Definition: grid_key.hpp:477
__device__ __host__ void set_d(index_type i, index_type id)
Set the i index.
Definition: grid_key.hpp:516
Declaration grid_sm.
Definition: grid_sm.hpp:167
size_t size()
Stub size.
Definition: map_vector.hpp:212
Grid key for a distributed grid.
vect_dist_key_dx get()
Get the actual key.
Distributed vector.
static const unsigned int dims
template parameters typedefs
size_t size_local_with_ghost() const
return the local size of the vector
auto getPos(vect_dist_key_dx vec_key) -> decltype(vPos.template get< 0 >(vec_key.getKey()))
Get the position of an element.
vector_dist_iterator getDomainAndGhostIterator() const
Get an iterator that traverse the particles in the domain.