fpmas 1.6
grid_builder.h
Go to the documentation of this file.
1#ifndef FPMAS_GRID_BUILDER_H
2#define FPMAS_GRID_BUILDER_H
3
8#include "grid.h"
10#include "grid_load_balancing.h"
11
12namespace fpmas { namespace model { namespace detail {
13
18
28 template<typename CellType>
30 protected:
37 typedef std::vector<std::vector<CellType*>> CellMatrix;
38
39 private:
41 DiscreteCoordinate _width;
42 DiscreteCoordinate _height;
43
44 mutable std::map<DiscretePoint, std::size_t> built_cells;
45 mutable GridCellIndex cell_begin {&built_cells};
46 mutable GridCellIndex cell_end {&built_cells};
47 mutable std::map<GridCellIndex, CellType*> cell_index;
48
49 CellMatrix buildLocalCells(
51 GridDimensions local_dimensions,
52 api::model::GroupList groups) const;
53
54 void allocate(
55 CellMatrix& matrix,
58
59 std::vector<CellType*> _build(
61 api::model::GroupList groups) const;
62
63 protected:
83 virtual void buildLocalGrid(
85 GridDimensions local_dimensions,
86 CellMatrix& cells
87 ) const = 0;
88
124 virtual void linkFrontiers(
126 GridDimensions local_dimensions,
127 CellMatrix& local_cells,
128 std::vector<CellType*>& frontier
129 ) const = 0;
130
131 public:
132
137
154 : cell_factory(cell_factory), _width(width), _height(height) {
155
156 }
157
170
171 }
172
177 return _width;
178 }
183 return _height;
184 }
185
206 std::vector<CellType*> build(
207 api::model::SpatialModel<CellType>& spatial_model) const override {
208 return this->_build(spatial_model, {});
209 }
210
214 std::vector<CellType*> build(
216 api::model::GroupList groups) const override {
217 return this->_build(spatial_model, groups);
218 }
219
258 std::size_t n,
259 std::function<void(CellType*)> init_function
260 ) const;
261
304 template<typename T>
306 const std::vector<T>& items,
307 std::function<void(
308 CellType*,
309 typename std::vector<T>::const_reference
310 )> init_function
311 ) const;
312
313 };
314
315 template<typename CellType>
317
318 template<typename CellType>
320 CellMatrix& matrix,
321 DiscreteCoordinate width,
322 DiscreteCoordinate height) const {
323 matrix.resize(height);
324 for(auto& row : matrix)
325 row.resize(width);
326 }
327
328 template<typename CellType>
329 typename GridBuilder<CellType>::CellMatrix GridBuilder<CellType>::buildLocalCells(
331 GridDimensions local_dimensions,
332 api::model::GroupList groups) const {
333
334 DiscretePoint origin = local_dimensions.getOrigin();
335 DiscretePoint extents = local_dimensions.getExtent();
336
337 CellMatrix cells;
338 allocate(cells, local_dimensions.width(), local_dimensions.height());
339
340 for(DiscreteCoordinate y = origin.y; y < extents.y; y++) {
341 for(DiscreteCoordinate x = origin.x; x < extents.x; x++) {
342 auto cell = cell_factory.build({x, y});
343 cells[y-origin.y][x-origin.x] = cell;
344 model.add(cell);
345 for(auto group : groups)
346 group.get().add(cell);
347 }
348 }
349
350 for(DiscreteCoordinate y = 0; y < this->height(); y++)
351 for(DiscreteCoordinate x = 0; x < this->width(); x++)
352 built_cells.insert(std::pair<api::model::DiscretePoint, std::size_t>(
353 {x, y}, 1ul
354 ));
355
356 cell_begin = GridCellIndex::begin(&built_cells);
357 cell_end = GridCellIndex::end(&built_cells);
358
359 for(auto row : cells) {
360 for(auto cell : row) {
361 // GridCellIndex offset is always 0 in this case (at most one
362 // cell per coordinate)
363 GridCellIndex index(&built_cells, cell->location(), 0);
364 cell_index.insert({index, cell});
365 }
366 }
367
368 // Random cell seed initialization
369 std::vector<std::mt19937_64::result_type> local_seeds(
370 GridCellIndex::distance(cell_begin, cell_end)
371 );
372
373 for(std::size_t i = 0; i < local_seeds.size(); i++)
374 // Generates a unique integer for each, independently from the
375 // current distribution
376 local_seeds[i] = i;
377 // Genererates a better seed distribution. The same seeds are
378 // generated on all processes.
379 std::seed_seq seed_generator(local_seeds.begin(), local_seeds.end());
380 seed_generator.generate(local_seeds.begin(), local_seeds.end());
381
382 initSequence(local_seeds, [] (
383 CellType* cell,
384 const std::mt19937_64::result_type& seed) {
385 cell->seed(seed);
386 });
387
388 return cells;
389 }
390
391 template<typename CellType>
392 std::vector<CellType*> GridBuilder<CellType>::_build(
393 api::model::SpatialModel<CellType>& model,
394 api::model::GroupList groups) const {
395 typedef std::pair<std::pair<DistributedId, DiscretePoint>, std::vector<api::model::GroupId>>
396 GridCellPack;
397
398 CellMatrix cells;
399 auto& mpi_comm = model.graph().getMpiCommunicator();
401
402 TreeProcessMapping tree_process_mapping(
403 this->width(), this->height(), mpi_comm
404 );
405 // Represents the local grid shape built on this process
406 GridDimensions local_dimensions
407 = tree_process_mapping.gridDimensions(mpi_comm.getRank());
408
409 // Build cells
410 cells = buildLocalCells(
411 model, local_dimensions, groups
412 );
413
414 // Build local links
415 buildLocalGrid(model, local_dimensions, cells);
416
417 std::unordered_map<int, std::vector<GridCellPack>> mpi_frontiers;
418
419 /* exchange frontiers */
420 /*
421 * Exchange process:
422 * When the TreeProcessMapping is used, the intuitive distribution on 4
423 * processes looks as follows:
424 * +------+------+
425 * | 1 | 2 |
426 * +------+------+
427 * | 0 | 3 |
428 * +------+------+
429 *
430 * In this case, the right frontier of 0 is sent to 3, the top frontier
431 * of 0 is sent to 1, and the top right corner of 0 is sent to 2.
432 *
433 * There are however specific cases about corners, see below.
434 */
435
436 DiscreteCoordinate left_x = local_dimensions.getOrigin().x-1;
437 if(left_x >= 0) {
438 // left frontier
439 for(DiscreteCoordinate y = 1; y < local_dimensions.height()-1; y++) {
440 auto cell = cells[y][0];
441 std::set<int> processes = {
442 tree_process_mapping.process({left_x, cell->location().y})
443 };
444 if(cell->location().y+1 < this->height())
445 processes.insert(
446 tree_process_mapping.process({left_x, cell->location().y+1})
447 );
448 if(cell->location().y-1 >= 0)
449 processes.insert(
450 tree_process_mapping.process({left_x, cell->location().y-1})
451 );
452
453 for(int process : processes) {
454 mpi_frontiers[process].push_back(
455 {{cell->node()->getId(), cell->location()}, cell->groupIds()});
456 }
457 }
458 }
459 DiscreteCoordinate bottom_y = local_dimensions.getOrigin().y-1;
460 if(bottom_y >= 0) {
461 // bottom frontier
462 for(DiscreteCoordinate x = 1; x < local_dimensions.width()-1; x++) {
463 auto cell = cells[0][x];
464 std::set<int> processes = {
465 tree_process_mapping.process({cell->location().x, bottom_y})
466 };
467 if(cell->location().x+1 < this->width())
468 processes.insert(
469 tree_process_mapping.process({cell->location().x+1, bottom_y})
470 );
471 if(cell->location().x-1 >= 0)
472 processes.insert(
473 tree_process_mapping.process({cell->location().x-1, bottom_y})
474 );
475 for(int process : processes) {
476 mpi_frontiers[process].push_back(
477 {{cell->node()->getId(), cell->location()}, cell->groupIds()});
478 }
479 }
480
481 }
482 DiscreteCoordinate right_x = local_dimensions.getExtent().x;
483 if(right_x < this->width()) {
484 // right frontier
485 for(DiscreteCoordinate y = 1; y < local_dimensions.height()-1; y++) {
486 auto cell = cells[y][local_dimensions.width()-1];
487 std::set<int> processes = {
488 tree_process_mapping.process({right_x, cell->location().y})
489 };
490 if(cell->location().y+1 < this->height())
491 processes.insert(
492 tree_process_mapping.process({right_x, cell->location().y+1})
493 );
494 // if y == 0, this corner is checked at bottom frontier
495 if(cell->location().y-1 >= 0)
496 processes.insert(
497 tree_process_mapping.process({right_x, cell->location().y-1})
498 );
499 for(int process : processes) {
500 mpi_frontiers[process].push_back(
501 {{cell->node()->getId(), cell->location()}, cell->groupIds()});
502 }
503 }
504 }
505
506 DiscreteCoordinate top_y = local_dimensions.getExtent().y;
507 if(top_y < this->height()) {
508 // top frontier
509 for(DiscreteCoordinate x = 1; x < local_dimensions.width()-1; x++) {
510 auto cell = cells[local_dimensions.height()-1][x];
511 std::set<int> processes = {
512 tree_process_mapping.process({cell->location().x, top_y})
513 };
514
515 // if x == local_dimensions.width()-1, this corner is checked
516 // by the right frontier
517 if(cell->location().x+1 < this->width())
518 processes.insert(
519 tree_process_mapping.process({cell->location().x+1, top_y})
520 );
521 if(cell->location().x-1 >= 0)
522 processes.insert(
523 tree_process_mapping.process({cell->location().x-1, top_y})
524 );
525
526 for(int process : processes) {
527 mpi_frontiers[process].push_back(
528 {{cell->node()->getId(), cell->location()}, cell->groupIds()});
529 }
530 }
531 }
532
533 /* corners */
534
535 /*
536 * Note about corners:
537 *
538 * When the TreeProcessMapping is used, the distributed grid might have
539 * the following shape on 4 processes (where 3's width is greater than
540 * 2's width):
541 * +------+---+----+
542 * | 1 | | |
543 * +------| 2 | 3 |
544 * | 0 | | |
545 * +------+---+----+
546 *
547 * In that case, for example, the top right corner of the process 0
548 * should **not** be sent again to 2, since it has already been sent as
549 * the right frontier of process 0.
550 *
551 * The following cases prevent all those issues.
552 */
553 if(local_dimensions.height() > 0 && local_dimensions.width() > 0) {
554 std::set<int> corners_processes;
555 // Bottom left corner
556 {
557 auto cell = cells[0][0];
558 std::set<int> processes;
559 auto x = cell->location().x;
560 auto y = cell->location().y;
561 if(y-1 >= 0) {
562 processes.insert(
563 tree_process_mapping.process({x, y-1})
564 );
565 if(x-1 >= 0)
566 processes.insert(
567 tree_process_mapping.process({x-1, y-1})
568 );
569 if(x+1 < this->width())
570 processes.insert(
571 tree_process_mapping.process({x+1, y-1})
572 );
573 }
574 if(x-1 >= 0) {
575 processes.insert(
576 tree_process_mapping.process({x-1, y})
577 );
578 if(y+1 < this->height())
579 processes.insert(
580 tree_process_mapping.process({x-1, y+1})
581 );
582 }
583 for(int process : processes) {
584 mpi_frontiers[process].push_back(
585 {{cell->node()->getId(), cell->location()}, cell->groupIds()});
586 corners_processes.insert(processes.begin(), processes.end());
587 }
588 }
589
590 {
591 // Bottom right corner
592 auto cell = cells[0][local_dimensions.width()-1];
593 std::set<int> processes;
594 auto x = cell->location().x;
595 auto y = cell->location().y;
596 if(y-1 >= 0) {
597 processes.insert(
598 tree_process_mapping.process({x, y-1})
599 );
600 if(x-1 >= 0)
601 processes.insert(
602 tree_process_mapping.process({x-1, y-1})
603 );
604 if(x+1 < this->width())
605 processes.insert(
606 tree_process_mapping.process({x+1, y-1})
607 );
608 }
609 if(x+1 < this->width()) {
610 processes.insert(
611 tree_process_mapping.process({x+1, y})
612 );
613 if(y+1 < this->height())
614 processes.insert(
615 tree_process_mapping.process({x+1, y+1})
616 );
617 }
618 for(int process : processes) {
619 mpi_frontiers[process].push_back(
620 {{cell->node()->getId(), cell->location()}, cell->groupIds()});
621 corners_processes.insert(processes.begin(), processes.end());
622 }
623 }
624
625 // Top left corner
626 {
627 auto cell = cells[local_dimensions.height()-1][0];
628 std::set<int> processes;
629 auto x = cell->location().x;
630 auto y = cell->location().y;
631 if(y+1 < this->height()) {
632 processes.insert(
633 tree_process_mapping.process({x, y+1})
634 );
635 if(x-1 >= 0)
636 processes.insert(
637 tree_process_mapping.process({x-1, y+1})
638 );
639 if(x+1 < this->width())
640 processes.insert(
641 tree_process_mapping.process({x+1, y+1})
642 );
643 }
644 if(x-1 >= 0) {
645 processes.insert(
646 tree_process_mapping.process({x-1, y})
647 );
648 if(y-1 >= 0)
649 processes.insert(
650 tree_process_mapping.process({x-1, y+1})
651 );
652 }
653 for(int process : processes) {
654 mpi_frontiers[process].push_back(
655 {{cell->node()->getId(), cell->location()}, cell->groupIds()});
656 corners_processes.insert(processes.begin(), processes.end());
657 }
658 }
659
660 // Top right corner
661 {
662 auto cell = cells[local_dimensions.height()-1][local_dimensions.width()-1];
663 std::set<int> processes;
664 auto x = cell->location().x;
665 auto y = cell->location().y;
666 if(y+1 < this->height()) {
667 processes.insert(
668 tree_process_mapping.process({x, y+1})
669 );
670 if(x-1 >= 0)
671 processes.insert(
672 tree_process_mapping.process({x-1, y+1})
673 );
674 if(x+1 < this->width())
675 processes.insert(
676 tree_process_mapping.process({x+1, y+1})
677 );
678 }
679 if(x+1 < this->width()) {
680 processes.insert(
681 tree_process_mapping.process({x+1, y})
682 );
683 if(y-1 >= 0)
684 processes.insert(
685 tree_process_mapping.process({x+1, y-1})
686 );
687 }
688 for(int process : processes) {
689 mpi_frontiers[process].push_back(
690 {{cell->node()->getId(), cell->location()}, cell->groupIds()});
691 corners_processes.insert(processes.begin(), processes.end());
692 }
693 }
694 if(local_dimensions.width() <= 2 || local_dimensions.height() <=2) {
695 // Edge case, some corner cells are added several times to some
696 // frontiers
697
698 auto less_grid_cell_pack = [](const GridCellPack& p1, const GridCellPack& p2) {
699 return p1.first.first < p2.first.first;
700 };
701 for(int process : corners_processes) {
702 std::set<GridCellPack, decltype(less_grid_cell_pack)> packs(less_grid_cell_pack);
703 for(auto pack : mpi_frontiers[process])
704 packs.insert(pack);
705 mpi_frontiers[process].clear();
706 for(auto pack : packs)
707 mpi_frontiers[process].push_back(pack);
708 }
709 }
710 }
711
712
713
714 mpi_frontiers = mpi.allToAll(mpi_frontiers);
715
716 std::vector<CellType*> cell_frontiers;
717 for(auto frontier : mpi_frontiers) {
718 for(auto cell_pack : frontier.second) {
719 // Builds a temporary cell
720 auto cell = cell_factory.build(cell_pack.first.second);
721 // Cells group ids must be updated before it is
722 // inserted in the graph
723 for(auto gid : cell_pack.second)
724 cell->addGroupId(gid);
725
726 // Builds a temporary node representing the node on the
727 // frontier (that is located on an other process)
728 api::graph::DistributedNode<AgentPtr>* tmp_node
729 = new graph::DistributedNode<AgentPtr>(cell_pack.first.first, cell);
730 tmp_node->setLocation(frontier.first);
731 model.graph().insertDistant(tmp_node);
732 cell_frontiers.push_back(cell);
733 }
734 }
735
736 linkFrontiers(model, local_dimensions, cells, cell_frontiers);
737
738 model.graph().synchronize();
739 std::vector<CellType*> built_cells;
740 for(auto row : cells)
741 for(auto cell : row)
742 built_cells.push_back(cell);
743 return built_cells;
744 }
745
746 template<typename CellType>
748 std::size_t n,
749 std::function<void(CellType*)> init_function) const {
750 std::vector<GridCellIndex> indexes = random::sample_indexes(
751 cell_begin, cell_end, n, RandomGridAgentBuilder::rd);
752 for(auto index : indexes) {
753 auto it = cell_index.find(index);
754 if(it != cell_index.end())
755 init_function(it->second);
756 }
757 }
758
759 template<typename CellType>
760 template<typename T>
762 const std::vector<T> &items,
763 std::function<void (
764 CellType*,
765 typename std::vector<T>::const_reference
766 )> init_function) const {
767 GridCellIndex grid_index = cell_begin;
768 auto map_index = cell_index.begin();
769 std::size_t i = 0;
770 while(map_index != cell_index.end()) {
771 if(map_index->first == grid_index) {
772 init_function(
773 map_index->second,
774 items[i]
775 );
776 ++map_index;
777 }
778 ++grid_index;
779 ++i;
780 }
781 }
782
783}}}
784#endif
Definition: spatial_model.h:568
virtual CellType * build(DiscretePoint location)=0
virtual void add(CellType *cell)=0
Definition: grid_load_balancing.h:30
DiscreteCoordinate height() const
Definition: grid_load_balancing.h:79
DiscreteCoordinate width() const
Definition: grid_load_balancing.h:72
DiscretePoint getOrigin() const
Definition: grid_load_balancing.h:53
DiscretePoint getExtent() const
Definition: grid_load_balancing.h:62
Definition: grid_builder.h:29
virtual void buildLocalGrid(api::model::SpatialModel< CellType > &model, GridDimensions local_dimensions, CellMatrix &cells) const =0
void initSequence(const std::vector< T > &items, std::function< void(CellType *, typename std::vector< T >::const_reference)> init_function) const
Definition: grid_builder.h:761
static GridCellFactory< CellType > default_cell_factory
Definition: grid_builder.h:136
virtual void linkFrontiers(api::model::SpatialModel< CellType > &model, GridDimensions local_dimensions, CellMatrix &local_cells, std::vector< CellType * > &frontier) const =0
std::vector< CellType * > build(api::model::SpatialModel< CellType > &spatial_model, api::model::GroupList groups) const override
Definition: grid_builder.h:214
std::vector< CellType * > build(api::model::SpatialModel< CellType > &spatial_model) const override
Definition: grid_builder.h:206
DiscreteCoordinate width() const
Definition: grid_builder.h:176
GridBuilder(DiscreteCoordinate width, DiscreteCoordinate height)
Definition: grid_builder.h:166
DiscreteCoordinate height() const
Definition: grid_builder.h:182
std::vector< std::vector< CellType * > > CellMatrix
Definition: grid_builder.h:37
void initSample(std::size_t n, std::function< void(CellType *)> init_function) const
Definition: grid_builder.h:747
GridBuilder(api::model::GridCellFactory< CellType > &cell_factory, DiscreteCoordinate width, DiscreteCoordinate height)
Definition: grid_builder.h:150
static Index end(const std::map< DiscretePoint, std::size_t > *item_counts)
Definition: random.h:479
static std::size_t distance(const Index &i1, const Index &i2)
Definition: random.h:529
static Index begin(const std::map< DiscretePoint, std::size_t > *item_counts)
Definition: random.h:471
std::vector< std::reference_wrapper< AgentGroup > > GroupList
Definition: model.h:833
long DiscreteCoordinate
Definition: grid.h:15
random::Index< DiscretePoint > GridCellIndex
Definition: grid_builder.h:17
std::vector< Index_t > sample_indexes(Index_t begin, Index_t end, std::size_t n, Generator_t &gen)
Definition: random.h:292
Definition: fpmas.cpp:3
void seed(unsigned long seed)
Definition: fpmas.cpp:23
DiscreteCoordinate x
Definition: grid.h:25
DiscreteCoordinate y
Definition: grid.h:29
Definition: communication.h:585
static random::mt19937_64 rd
Definition: grid.h:238