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