FEAT 3
Finite Element Analysis Toolbox
Loading...
Searching...
No Matches
parti_iterative.hpp
1// FEAT3: Finite Element Analysis Toolbox, Version 3
2// Copyright (C) 2010 by Stefan Turek & the FEAT group
3// FEAT3 is released under the GNU General Public License version 3,
4// see the file 'copyright.txt' in the top level directory for details.
5
6#pragma once
7
9#include <kernel/adjacency/graph.hpp>
10#include <kernel/geometry/conformal_mesh.hpp>
12#include <kernel/util/random.hpp>
13#include <kernel/util/dist.hpp>
14#include <kernel/util/mutable_priority_queue.hpp>
15#include <kernel/util/time_stamp.hpp>
16
17#include <map>
18#include <vector>
19#include <list>
20
21namespace FEAT
22{
23 namespace Geometry
24 {
25 namespace Intern
26 {
28 {
29 Index patch; //to which center does this cell belong
30 Index distance; //distance to the nearest center
31
33 distance(std::numeric_limits<Index>::max())
34 {
35 }
36 };
37
38 template<typename Shape_, int num_coords_, typename Coord_>
39 std::vector<Index> parti_iterative_distance(Index start, ConformalMesh<Shape_, num_coords_, Coord_> & mesh, Index num_patches)
40 {
42 static constexpr int facet_dim = MeshType::shape_dim-1;
43 const auto& facet_idx = mesh.template get_index_set<MeshType::shape_dim, facet_dim>();
44 // nachbar zu zelle i sind neighbors[i][j] mit j = 0 bis faced_idx.get_num_indices() und neighbors != ~Index(0)
45 auto& neighbors = mesh.get_neighbors();
46
47 Index num_elems(mesh.get_num_elements());
48
49 // the list that shall contain our computed distance results
50 std::vector<Index> distances(num_elems, std::numeric_limits<Index>::max());
51 distances.at(start) = Index(0);
52 // the 'queue' of nodes that have not yet been visited
53 // the priorities are sorted from max to min, thus we need to use the inverse distance as the key, resulting in the lowest distance being at the front of the queue
55 for (Index i(0) ; i < num_elems ; ++i)
56 {
57 pending_nodes.insert(i, 0);
58 }
59 pending_nodes.update(start, std::numeric_limits<Index>::max());
60
61 Index exploration_threshold = Math::max(
62 Index(Math::pow(double(num_elems), double(1) / double(MeshType::shape_dim)) + 1),
63 num_elems / num_patches);
64 exploration_threshold = Math::max(exploration_threshold, Index(2));
65
66
67 while (pending_nodes.size() > 0)
68 {
69 Index next_node = pending_nodes.front_value();
70 Index next_distance = std::numeric_limits<Index>::max() - pending_nodes.front_key();
71 // remove current node from queue of unvisited nodes
72 pending_nodes.pop();
73 //abort exploration if we have already reached a hopefully large enough portion of the mesh
74 if (next_distance != std::numeric_limits<Index>::max() && next_distance > exploration_threshold)
75 {
76 return distances;
77 }
78
79 //update all neighbors
80 for (int j(0) ; j < facet_idx.get_num_indices() ; ++j)
81 {
82 Index other_cell(neighbors[next_node][j]);
83 //update neighbor cell distance, if neighbor exists and neighbor is in the queue of pending nodes
84 if (other_cell != ~Index(0) && pending_nodes.count(other_cell) > 0)
85 {
86 Index new_distance = distances.at(next_node) + 1;
87 if (distances.at(other_cell) > new_distance)
88 {
89 distances.at(other_cell) = new_distance;
90 pending_nodes.update(other_cell, std::numeric_limits<Index>::max() - new_distance);
91 }
92 }
93 }
94 }
95 return distances;
96 }
97
98 template<typename Shape_, int num_coords_, typename Coord_>
100 {
101 public: //TODO write getter for cells per rank and make all member variables private
111 std::vector<std::set<Index>> _cells_per_patch;
113 std::set<Index> _centers;
115 std::vector<std::set<Index>> _boundary_cells;
117 std::vector<Index> _patch_per_cell;
119 std::vector<Index> _boundary_size_per_patch;
120
121 public:
122
132 explicit PartiIterativeIndividual(MeshType& mesh, Random & rng, Index num_patches) :
133 _mesh(mesh),
134 _num_patches(num_patches),
135 _num_elems(mesh.get_num_elements()),
140 {
143
144 // List of cells with informations regarding the partitioning layout
145 std::vector<Intern::PartiIterativeItem> items;
146
147 bool bad_centers(true);
148 while (bad_centers)
149 {
150 bad_centers = false;
151 _centers.clear();
152 items.clear();
153 items.resize(_num_elems);
154
155 // find randomly num_patches different cells as cluster centers
156 while(_centers.size() < _num_patches)
157 {
158 Index cell = rng(Index(0), _num_elems - 1);
159 if (_centers.count(cell) == 0)
160 _centers.insert(cell);
161 }
162
163 // create distance list from each center and assign items the distance and patch of the nearest center
164 auto center_iterator = _centers.begin();
165 for (Index patch(0) ; patch < _num_patches ; ++patch, ++center_iterator)
166 {
167 auto distance_list = Intern::parti_iterative_distance(*center_iterator, mesh, _num_patches);
168 for (Index node(0) ; node < _num_elems ; ++node)
169 {
170 if(distance_list.at(node) < items.at(node).distance)
171 {
172 items.at(node).distance = distance_list.at(node);
173 items.at(node).patch = patch;
174 }
175 }
176 }
177
178 //check for max uint values in items list, i.e. we did not reach every cell
179 for (Index node(0) ; node < _num_elems ; ++node)
180 {
181 bad_centers &= (items.at(node).distance == std::numeric_limits<Index>::max());
182 }
183 }
184
185 for (Index i(0) ; i < _num_elems ; ++i)
186 {
187 _cells_per_patch.at(items.at(i).patch).insert(i);
188 }
189
190 //setup cell->patch mapping
191 for (Index patch(0) ; patch < _num_patches ; ++patch)
192 {
193 for (auto cell : _cells_per_patch.at(patch))
194 {
195 _patch_per_cell.at(cell) = patch;
196 }
197 }
198
199
200 static constexpr int facet_dim = MeshType::shape_dim-1;
201 const auto& facet_idx = mesh.template get_index_set<MeshType::shape_dim, facet_dim>();
202 // nachbar zu zelle i sind neighbors[i][j] mit j = 0 bis faced_idx.get_num_indices() und neighbors != ~Index(0)
203 auto& neighbors = mesh.get_neighbors();
204
205 //setup boundary cells
206 for (Index patch(0) ; patch < _num_patches ; ++patch)
207 {
208 for (auto cell : _cells_per_patch.at(patch))
209 {
210 for (int j(0) ; j < facet_idx.get_num_indices() ; ++j)
211 {
212 Index other_cell(neighbors[cell][j]);
213 // if neighbor exists and is not in our own patch
214 if (other_cell != ~Index(0) && _patch_per_cell.at(other_cell) != patch)
215 {
216 _boundary_cells.at(patch).insert(cell);
217 }
218 }
219 }
220 }
221
223
224 //TODO anzahl nachbar patches fuer fitness noetig ???
225 }
226
228 void mutate(MeshType& mesh, Random & rng, Index mutation_max)
229 {
230 static constexpr int facet_dim = MeshType::shape_dim-1;
231 const auto& facet_idx = mesh.template get_index_set<MeshType::shape_dim, facet_dim>();
232 // nachbar zu zelle i sind neighbors[i][j] mit j = 0 bis faced_idx.get_num_indices() und neighbors != ~Index(0)
233 auto& neighbors = mesh.get_neighbors();
234
235 Index mutation_count(0);
236 Index tries(0);
237 //iterate over every cell at the boundaries
238 while (tries < 10*mutation_max && mutation_count < mutation_max)
239 {
240 ++tries;
241
242 Index patch(rng(Index(0), _num_patches - 1));
243 if (_boundary_cells.at(patch).size() == 0)
244 continue;
245 if (_cells_per_patch.at(patch).size() == 1)
246 continue;
247
248 Index rng_cell_idx = rng(Index(0), Index(_boundary_cells.at(patch).size() - 1));
249 Index counter(0);
250 Index cell(*(_boundary_cells.at(patch).begin()));
251 //choose cell randomly from current boundary set
253 for (auto it(_boundary_cells.at(patch).begin()) ; counter != rng_cell_idx ; ++counter, ++it)
254 {
255 cell = *it;
256 }
257
258 // list of neighbor cells in other patches
259 std::list<Index> trans_patch_neighbors;
260 for (int j(0) ; j < facet_idx.get_num_indices() ; ++j)
261 {
262 Index other_cell(neighbors[cell][j]);
263 // if neighbor exists and is not in our own patch
264 if (other_cell != ~Index(0) && _patch_per_cell.at(other_cell) != patch)
265 {
266 trans_patch_neighbors.push_back(other_cell);
267 }
268 }
269
270 //check if any neighbor has a smaller patch
271 Index smallest_patch(patch);
272 Index smallest_size = Index(_cells_per_patch.at(patch).size());
273 for (auto neighbor : trans_patch_neighbors)
274 {
275 if (_cells_per_patch.at(_patch_per_cell.at(neighbor)).size() < smallest_size)
276 {
277 smallest_size = Index(_cells_per_patch.at(_patch_per_cell.at(neighbor)).size());
278 smallest_patch = _patch_per_cell.at(neighbor);
279 }
280 }
281
282 if (smallest_patch == patch)
283 continue;
284
285 //build up list of neighbors in the same (old) patch
286 std::list<Index> in_patch_neighbors;
287 for (int j(0) ; j < facet_idx.get_num_indices() ; ++j)
288 {
289 Index other_cell(neighbors[cell][j]);
290 // if neighbor exists and is in our own patch
291 if (other_cell != ~Index(0) && _patch_per_cell.at(other_cell) == patch)
292 {
293 in_patch_neighbors.push_back(other_cell);
294 }
295 }
296 //check if we would isolate one of our neighbors completly and abort
297 //i.e. our neighbor from the same patch has only us as a link to its patch
298 bool neighbor_missing = false;
299 for (auto neighbor : in_patch_neighbors)
300 {
301 bool other_neighbor = false;
302 for (int j(0) ; j < facet_idx.get_num_indices() ; ++j)
303 {
304 Index other_cell(neighbors[neighbor][j]);
305 if (other_cell != ~Index(0) && other_cell != cell && _patch_per_cell.at(other_cell) == patch)
306 {
307 other_neighbor = true;
308 break;
309 }
310 }
311 if (other_neighbor == false)
312 neighbor_missing = true;
313 }
314 if (neighbor_missing)
315 continue;
316
317 //found new patch for our cell, update structures
318 _patch_per_cell.at(cell) = smallest_patch;
319 XASSERT(_cells_per_patch.at(patch).count(cell) > 0);
320 _cells_per_patch.at(patch).erase(cell);
321 _cells_per_patch.at(smallest_patch).insert(cell);
322 XASSERT(_boundary_cells.at(patch).count(cell) > 0);
323 _boundary_cells.at(patch).erase(cell);
324 _boundary_cells.at(smallest_patch).insert(cell);
325
326 //update neighbor cells in other patch if they loose their boundary property due to our patch switch
327 for (auto neighbor : trans_patch_neighbors)
328 {
329 //update only cells in common new patch
330 if (_patch_per_cell.at(neighbor) != smallest_patch)
331 continue;
332
333 bool boundary(false);
334 for (int j(0) ; j < facet_idx.get_num_indices() ; ++j)
335 {
336 Index other_cell(neighbors[neighbor][j]);
337 if (other_cell != ~Index(0) && _patch_per_cell.at(other_cell) != smallest_patch)
338 {
339 boundary = true;
340 break;
341 }
342 }
343
344 if (! boundary)
345 _boundary_cells.at(smallest_patch).erase(neighbor);
346 }
347
348 //update our own patchs neighbors to become a boundary cell
349 for (auto neighbor : in_patch_neighbors)
350 {
351 _boundary_cells.at(patch).insert(neighbor);
352 }
353
354 //we mutate successfully
355 ++mutation_count;
356 }
358 }
359
362 {
363 static constexpr int facet_dim = MeshType::shape_dim-1;
364 const auto& facet_idx = _mesh.template get_index_set<MeshType::shape_dim, facet_dim>();
365 auto& neighbors = _mesh.get_neighbors();
366
367 for (Index patch(0) ; patch < _num_patches ; ++patch)
368 {
369 Index sum(0);
370 for (auto cell : _boundary_cells.at(patch))
371 {
372 for (int j(0) ; j < facet_idx.get_num_indices() ; ++j)
373 {
374 Index other_cell(neighbors[cell][j]);
375 if (other_cell != ~Index(0) && _patch_per_cell.at(other_cell) != patch)
376 {
377 ++sum;
378 }
379 }
380 }
381 _boundary_size_per_patch.at(patch) = sum;
382 }
383 }
384
385 Index get_boundary_size() const
386 {
387 Index sum(0);
388 for (Index patch(0) ; patch < _num_patches ; ++patch)
389 {
390 sum += get_boundary_size(patch);
391 }
392 return sum;
393 }
394
395 Index get_boundary_size(Index patch) const
396 {
397 return _boundary_size_per_patch.at(patch);
398 }
399
400 Index get_boundary_deviation() const
401 {
402 Index min(std::numeric_limits<Index>::max());
403 Index max(0);
404 for (Index patch(0) ; patch < _num_patches ; ++patch)
405 {
406 Index patches_boundary_size(get_boundary_size(patch));
407 min = Math::min(patches_boundary_size, min);
408 max = Math::max(patches_boundary_size, max);
409 }
410 return max - min;
411 }
412
413 Index get_cell_deviation() const
414 {
415 Index min(std::numeric_limits<Index>::max());
416 Index max(0);
417 for (Index patch(0) ; patch < _num_patches ; ++patch)
418 {
419 min = Math::min(Index(_cells_per_patch.at(patch).size()), min);
420 max = Math::max(Index(_cells_per_patch.at(patch).size()), max);
421 }
422 return max - min;
423 }
424 };
425
426 template<typename T_>
427 bool parti_iterative_compare_cell_deviation_simple(const T_ & first, const T_ & second)
428 {
429 if (first.get_cell_deviation() != second.get_cell_deviation())
430 return first.get_cell_deviation() < second.get_cell_deviation();
431 else if (first.get_boundary_deviation() != second.get_boundary_deviation() )
432 return first.get_boundary_deviation() < second.get_boundary_deviation();
433 else
434 return first.get_boundary_size() < second.get_boundary_size();
435 }
436
437 template<typename T_>
438 bool parti_iterative_compare_cell_deviation(const T_ & first, const T_ & second)
439 {
440 if (first.get_cell_deviation() != second.get_cell_deviation())
441 return first.get_cell_deviation() < second.get_cell_deviation() && first.get_boundary_size() <= second.get_boundary_size() + 1;
442 else if (first.get_boundary_deviation() != second.get_boundary_deviation() )
443 return first.get_boundary_deviation() < second.get_boundary_deviation() && first.get_boundary_size() <= second.get_boundary_size() + 1;
444 else
445 return first.get_boundary_size() < second.get_boundary_size();
446 }
447 }
449 template<typename Mesh_>
451
463 template<typename Shape_, int num_coords_, typename Coord_>
464 class PartiIterative<ConformalMesh<Shape_, num_coords_, Coord_>>
465 {
466 private:
474 std::list<Geometry::Intern::PartiIterativeIndividual<Shape_, num_coords_, Coord_>> _population;
477
478 public:
481
497 explicit PartiIterative(MeshType& mesh, const Dist::Comm & comm, Index num_patches, double time_init, double time_mutate) :
498 _num_elems(mesh.get_num_elements()),
499 _num_ranks(Index(comm.size())),
500 _comm(comm),
501 _num_patches(num_patches)
502 {
503 Random rng;
504
505 mesh.fill_neighbors();
506
507 {
509 _population.push_back(indi);
510 }
511
512 TimeStamp at;
513 while(at.elapsed_now() < time_init)
514 {
515 for (Index i(0) ; i < 10 ; ++i)
516 {
518 _population.push_back(indi);
519 }
520 _population.sort(Geometry::Intern::parti_iterative_compare_cell_deviation_simple<typename decltype(_population)::value_type>);
521 while (_population.size() > 10)
522 _population.pop_back();
523 }
524
525 //std::cout<<"pre: "<<_population.back().get_cell_deviation()<<" " <<_population.back().get_boundary_deviation()<<" " << _population.back().get_boundary_size()<<"\n";
526
527 //mutate each of the 10 individuals and store the top 10 fittest of all in _population
528 at.stamp();
529 while(at.elapsed_now() < time_mutate)
530 {
531 std::list<typename decltype(_population)::value_type> mutations;
532 for (auto it(_population.begin()) ; it != _population.end() ; ++it)
533 {
534 auto indi(*it);
535 indi.mutate(mesh, rng, 5);
536 mutations.push_back(indi);
537 }
538 mutations.sort(Geometry::Intern::parti_iterative_compare_cell_deviation<typename decltype(_population)::value_type>);
539 _population.merge(mutations, Geometry::Intern::parti_iterative_compare_cell_deviation<typename decltype(_population)::value_type>);
540 while (_population.size() > 20)
541 _population.pop_back();
542 }
543
544 _population.sort(Geometry::Intern::parti_iterative_compare_cell_deviation<typename decltype(_population)::value_type>);
545 while (_population.size() > 1)
546 _population.pop_back();
547
548 //std::cout<<_population.back().get_cell_deviation()<<" " <<_population.back().get_boundary_deviation()<<" " << _population.back().get_boundary_size()<<"\n";
549 }
550
558 {
559 auto& indi = _population.front();
560
561 Index own_cell_deviation = indi.get_cell_deviation();
562 Index lowest_cell_deviation(std::numeric_limits<Index>::max());
563 Index own_boundary_deviation = indi.get_boundary_deviation();
564 Index lowest_boundary_deviation(std::numeric_limits<Index>::max());
565 Index own_boundary_size = indi.get_boundary_size();
566 Index lowest_boundary_size(std::numeric_limits<Index>::max());
567 Index lowest_rank(0);
569 Index * all_cell_deviation = new Index[_num_ranks];
570 Index * all_boundary_deviation = new Index[_num_ranks];
571 Index * all_boundary_size = new Index[_num_ranks];
572 _comm.allgather(&own_cell_deviation, 1, all_cell_deviation, 1);
573 _comm.allgather(&own_boundary_deviation, 1, all_boundary_deviation, 1);
574 _comm.allgather(&own_boundary_size, 1, all_boundary_size, 1);
575 for (Index i(0) ; i < _num_ranks ; ++i)
576 {
577 if (all_cell_deviation[i] < lowest_cell_deviation)
578 {
579 lowest_cell_deviation = all_cell_deviation[i];
580 lowest_boundary_deviation = all_boundary_deviation[i];
581 lowest_boundary_size = all_boundary_size[i];
582 lowest_rank = i;
583 }
584 else if (all_cell_deviation[i] == lowest_cell_deviation && all_boundary_deviation[i] < lowest_boundary_deviation)
585 {
586 lowest_cell_deviation = all_cell_deviation[i];
587 lowest_boundary_deviation = all_boundary_deviation[i];
588 lowest_boundary_size = all_boundary_size[i];
589 lowest_rank = i;
590 }
591 else if (all_cell_deviation[i] == lowest_cell_deviation && all_boundary_deviation[i] == lowest_boundary_deviation && all_boundary_size[i] < lowest_boundary_size)
592 {
593 lowest_cell_deviation = all_cell_deviation[i];
594 lowest_boundary_deviation = all_boundary_deviation[i];
595 lowest_boundary_size = all_boundary_size[i];
596 lowest_rank = i;
597 }
598 }
599 delete[] all_cell_deviation;
600 delete[] all_boundary_deviation;
601 delete[] all_boundary_size;
602
603 int _rank = _comm.rank();
604 if (_rank == (int)lowest_rank)
605 {
606 //std::cout<<"selected: " << _population.front().get_cell_deviation()<<" " <<_population.front().get_boundary_deviation()<<" " << _population.front().get_boundary_size()<<"\n";
607 Adjacency::Graph graph(_num_patches, _num_elems, _num_elems);
608 Index* ptr = graph.get_domain_ptr();
609 Index* idx = graph.get_image_idx();
610
611 // build pointer array
612 ptr[0] = 0;
613 for(Index i(0); i < _num_patches; ++i)
614 {
615 ptr[i+1] = Index(indi._cells_per_patch.at(i).size()) + ptr[i];
616 }
617
618 // build index array
619 Index counter(0);
620 for (Index rank(0) ; rank < _num_patches ; ++rank)
621 {
622 for (auto cell : indi._cells_per_patch.at(rank))
623 {
624 idx[counter] = cell;
625 ++counter;
626 }
627 }
628 _comm.bcast(graph.get_domain_ptr(), _num_patches + 1, _rank);
629 _comm.bcast(graph.get_image_idx(), _num_elems, _rank);
630 return graph;
631 }
632 else
633 {
634 Index * domain_ptr = new Index[_num_patches + 1];
635 _comm.bcast(domain_ptr, _num_patches + 1, (int)lowest_rank);
636 Index * image_idx = new Index[_num_elems];
637 _comm.bcast(image_idx, _num_elems, (int)lowest_rank);
638 Adjacency::Graph graph(_num_patches, _num_elems, _num_elems, domain_ptr, image_idx);
639 delete[] domain_ptr;
640 delete[] image_idx;
641 return graph;
642 }
643 }
644 }; // class PartiIterative
645 } // namespace Geometry
646} // namespace FEAT
#define XASSERT(expr)
Assertion macro definition.
Definition: assertion.hpp:262
FEAT Kernel base header.
Adjacency Graph implementation.
Definition: graph.hpp:34
Index * get_domain_ptr()
Returns the domain pointer array.
Definition: graph.hpp:359
Index * get_image_idx()
Returns the image node index array.
Definition: graph.hpp:374
Communicator class.
Definition: dist.hpp:1349
void bcast(void *buffer, std::size_t count, const Datatype &datatype, int root) const
Blocking broadcast.
Definition: dist.cpp:541
int rank() const
Returns the rank of this process in this communicator.
Definition: dist.hpp:1494
void allgather(const void *sendbuf, std::size_t sendcount, const Datatype &sendtype, void *recvbuf, std::size_t recvcount, const Datatype &recvtype) const
Blocking gather-to-all.
Definition: dist.cpp:589
Conformal mesh class template.
static constexpr int shape_dim
shape dimension
IndexSet< shape_dim, shape_dim-1 >::Type & get_neighbors()
Index get_num_elements() const
Returns the number of elements.
void fill_neighbors()
Fills the neighbor index set.
std::vector< std::set< Index > > _boundary_cells
List of boundary cells per patch.
std::vector< Index > _patch_per_cell
mapping of cell to patch number
std::vector< std::set< Index > > _cells_per_patch
Mapping of patches to cells assigned to each patch.
void mutate(MeshType &mesh, Random &rng, Index mutation_max)
mutate individuum - let cells switch to smaller patches
PartiIterativeIndividual(MeshType &mesh, Random &rng, Index num_patches)
Constructor.
std::set< Index > _centers
List of cluster centers: cell id for each patch.
void update_boundary_size()
update boundary size per patch from _boundary_cells structure
const Index _num_elems
number of elements in input mesh
std::vector< Index > _boundary_size_per_patch
boundary size per patch, must be updated via update_boundary_size method
const Index _num_patches
number of desired patches
ConformalMesh< Shape_, num_coords_, Coord_ > MeshType
our mesh type
PartiIterative(MeshType &mesh, const Dist::Comm &comm, Index num_patches, double time_init, double time_mutate)
Constructor.
ConformalMesh< Shape_, num_coords_, Coord_ > MeshType
our mesh type
std::list< Geometry::Intern::PartiIterativeIndividual< Shape_, num_coords_, Coord_ > > _population
Our population.
Adjacency::Graph build_elems_at_rank() const
Returns the Elements-at-Rank graph of the partitioning.
Iterative-Partitioner class template declaration.
Pseudo-Random Number Generator.
Definition: random.hpp:54
Time stamp class.
Definition: time_stamp.hpp:54
TimeStamp & stamp()
Stamps the current time-stamp.
Definition: time_stamp.hpp:79
double elapsed_now() const
Calculates the time elapsed between the time stamp and now.
Definition: time_stamp.hpp:121
T_ pow(T_ x, T_ y)
Returns x raised to the power of y.
Definition: math.hpp:643
T_ min(T_ a, T_ b)
Returns the minimum of two values.
Definition: math.hpp:123
T_ max(T_ a, T_ b)
Returns the maximum of two values.
Definition: math.hpp:137
FEAT namespace.
Definition: adjactor.hpp:12
std::uint64_t Index
Index data type.