Cellular Automata CHEM274B Group Project
Contributor(s): Chongye Feng, Emmanuel Cortes, Trevor Oldham
Loading...
Searching...
No Matches
CAdatatypes.h
Go to the documentation of this file.
1
11#pragma once // Ensures that this file is only included once
12 // during compilation
13
14#include "CAutils.h"
15#include <array>
16#include <unordered_map>
17#include <iostream>
18#include <algorithm> // max_element
19#include <utility> // pair
20#include <cmath> // pow
21
22// File path of the output data log
23const std::string FILE_PATH = "Data/data.csv";
24
29using MajorityCounter = std::unordered_map<int, int>;
30
35namespace CAEnums
36{
43 {
44 VonNeumann,
45 Moore
46 };
47
54 {
55 Periodic,
56 Walled,
57 CutOff
58 };
59
65 enum Rule
66 {
67 Majority,
68 Parity,
69 Custom
70 };
71
78 {
79 CellsAlreadyInitialized = -1,
80 CellsAreNull = -2,
81 CellsMalloc = -3,
82 InvalidCellState = -4,
83 InvalidCellStateCondition = -5,
84 InvalidRadius = -6,
85 InvalidNumStates = -7,
86 NeighborhoodCellsMalloc = -8,
87 CustomRuleIsNull = -9,
88 RadiusLargerThanDimensions = -10
89 };
90}
91
101{
102public:
111
118
125
133
143
151
158};
159
174template <typename T>
176{
177private:
178 T *vector;
179 T *next_vector;
180 T **matrix;
181 T **next_matrix;
182 T ***tensor;
183 T ***next_tensor;
184 int steps_taken;
185
200 int set_new_cell_state(int *cell_index, int index_size,
201 T *neighborhood_cells, int neighborhood_size,
202 T &new_cell_state, void(custom_rule)(int *, int, T *, int, T &))
203 {
204 int sum = 0; // sum of cells within boundary_radius for Parity rule
205 MajorityCounter state_votes_counter; // counter to keep track of votes for Majority rule
206 initialize_majority_rule_counter(state_votes_counter, num_states);
207
208 switch (rule_type)
209 {
210 case CAEnums::Custom:
211 if (vector == nullptr && matrix == nullptr && tensor == nullptr)
212 {
213 new_cell_state.state = -1;
214 return CAEnums::CellsAreNull;
215 }
216 if (custom_rule == nullptr)
217 {
218 new_cell_state.state = -1;
219 return CAEnums::CustomRuleIsNull;
220 }
221 else
222 {
223 // custom_rule should set the new_cell_state
224 custom_rule(cell_index, index_size, neighborhood_cells, neighborhood_size, new_cell_state);
225 }
226 break;
227 case CAEnums::Parity:
228 for (int i = 0; i < neighborhood_size; i++)
229 {
230 // update sum with current cell value
231 sum += neighborhood_cells[i].state;
232 }
233 new_cell_state.state = sum % num_states; // store the parity state as the new state
234 break;
235 case CAEnums::Majority:
236 for (int i = 0; i < neighborhood_size; i++)
237 {
238 // increment the cell state's number of votes
239 auto it = state_votes_counter.find(neighborhood_cells[i].state);
240 if (it != state_votes_counter.end())
241 {
242 it->second++;
243 }
244 }
245 // find the max_element in the counter based on the pairs' second variable (number of "votes")
246 auto max_elem = max_element(state_votes_counter.begin(), state_votes_counter.end(),
248 new_cell_state.state = max_elem->first; // set the majority state as the new state
249 break;
250 }
251 return 0;
252 }
253
266 int get_state_from_neighborhood_1d(int *cell_index, int index_size, T &new_cell_state,
267 void(custom_rule)(int *, int, T *, int, T &))
268 {
269 int error_code = 0; // store error code return by other methods
270 int i = cell_index[0]; // get i-th index from array
271 int neighborhood_index = 0; // keep track of neighborhood array as we iterate through CA cells
272
273 // allocate memory and check if operation was successful
274 T *neighborhood_cells = malloc_neighborhood_array(index_size);
275 if (neighborhood_cells == nullptr)
276 {
277 return CAEnums::NeighborhoodCellsMalloc;
278 }
279
280 // VonNeumann and Moore do not differ for 1d (vector) case
281 switch (boundary_type)
282 {
283 case CAEnums::Periodic:
284 generate_periodic_neighborhood_1d(cell_index, neighborhood_cells, neighborhood_index);
285 error_code = set_new_cell_state(cell_index, index_size, neighborhood_cells, neighborhood_index, new_cell_state, custom_rule);
286 break;
287 case CAEnums::Walled: // with walled boundaries the edge cells never change
288 // check if i is a boundary cell
289 if (i == 0 || i == axis1_dim - 1)
290 {
291 new_cell_state = vector[i]; // keep boundary cell state
292 break;
293 }
294 // fall-through to CutOff case when cell isn't a boundary cell
295 case CAEnums::CutOff:
296 generate_cutoff_neighborhood_1d(cell_index, neighborhood_cells, neighborhood_index);
297 error_code = set_new_cell_state(cell_index, index_size, neighborhood_cells, neighborhood_index, new_cell_state, custom_rule);
298 break;
299 }
300 delete[] neighborhood_cells;
301 return error_code;
302 }
303
316 int get_state_from_neighborhood_2d(int *cell_index, int index_size, T &new_cell_state,
317 void(custom_rule)(int *, int, T *, int, T &))
318 {
319 int error_code = 0; // store error code return by other methods
320 int i = cell_index[0]; // get i-th index from array
321 int j = cell_index[1]; // get j-th index from array
322 int neighborhood_index = 0; // keep track of neighborhood array as we iterate through CA cells
323
324 // allocate memory and check if operation was successful
325 T *neighborhood_cells = malloc_neighborhood_array(index_size);
326 if (neighborhood_cells == nullptr)
327 {
328 return CAEnums::NeighborhoodCellsMalloc;
329 }
330
331 switch (boundary_type)
332 {
333 case CAEnums::Periodic:
334 generate_periodic_neighborhood_2d(cell_index, neighborhood_cells, neighborhood_index);
335 error_code = set_new_cell_state(cell_index, index_size, neighborhood_cells, neighborhood_index, new_cell_state, custom_rule);
336 break;
337 case CAEnums::Walled: // with walled boundaries the edge cells never change
338 // check if i,j is a boundary cell
339 if ((i == 0 || i == axis1_dim - 1) || (j == 0 || j == axis2_dim - 1))
340 {
341 new_cell_state = matrix[i][j]; // keep boundary cell state
342 break;
343 }
344 // fall-through to CutOff case when cell isn't a boundary cell
345 case CAEnums::CutOff:
346 generate_cutoff_neighborhood_2d(cell_index, neighborhood_cells, neighborhood_index);
347 error_code = set_new_cell_state(cell_index, index_size, neighborhood_cells, neighborhood_index, new_cell_state, custom_rule);
348 break;
349 }
350 delete[] neighborhood_cells;
351 return error_code;
352 }
353
366 int get_state_from_neighborhood_3d(int *cell_index, int index_size, T &new_cell_state,
367 void(custom_rule)(int *, int, T *, int, T &))
368 {
369 int error_code = 0; // store error code return by other methods
370 int i = cell_index[0]; // get i-th index from array
371 int j = cell_index[1]; // get j-th index from array
372 int k = cell_index[2]; // get k-th index from array
373 int neighborhood_index = 0; // keep track of neighborhood array as we iterate through CA cells
374
375 // allocate memory and check if operation was successful
376 T *neighborhood_cells = malloc_neighborhood_array(index_size); // index_size == rank
377 if (neighborhood_cells == nullptr)
378 {
379 return CAEnums::NeighborhoodCellsMalloc;
380 }
381
382 switch (boundary_type)
383 {
384 case CAEnums::Periodic:
385 generate_periodic_neighborhood_3d(cell_index, neighborhood_cells, neighborhood_index);
386 error_code = set_new_cell_state(cell_index, index_size, neighborhood_cells, neighborhood_index, new_cell_state, custom_rule);
387 break;
388 case CAEnums::Walled:
389 // check if i,j,k is a boundary cell
390 if ((i == 0 || i == axis1_dim - 1) || (j == 0 || j == axis2_dim - 1) || (k == 0 || j == axis3_dim - 1))
391 {
392 // with walled boundaries the edge cells never change
393 new_cell_state = tensor[i][j][k]; // keep boundary cell state
394 break;
395 }
396 // fall-through to CutOff case when cell isn't a boundary cell
397 case CAEnums::CutOff:
398 generate_cutoff_neighborhood_3d(cell_index, neighborhood_cells, neighborhood_index);
399 error_code = set_new_cell_state(cell_index, index_size, neighborhood_cells, neighborhood_index, new_cell_state, custom_rule);
400 break;
401 }
402 delete[] neighborhood_cells;
403 return error_code;
404 }
405
414 void generate_periodic_neighborhood_1d(int *cell_index, T *neighborhood_cells, int &neighborhood_index)
415 {
416 int i = cell_index[0]; // get i-th index from array
417 int periodic_i; // axis1_dim index used by Periodic boundary type
418
419 for (int di = -boundary_radius; di <= boundary_radius; di++)
420 {
421 periodic_i = get_periodic_index(i, di, axis1_dim);
422 // add state to flattened array
423 neighborhood_cells[neighborhood_index] = vector[periodic_i];
424 neighborhood_index++;
425 }
426 }
427
436 void generate_cutoff_neighborhood_1d(int *cell_index, T *neighborhood_cells, int &neighborhood_index)
437 {
438 int i = cell_index[0]; // get i-th index from array
439 int neighbor_i; // neighboring cell's i-th index
440
441 for (int di = -boundary_radius; di <= boundary_radius; di++)
442 {
443 neighbor_i = di + i;
444 // exclude cells that are out of bounds
445 if (neighbor_i <= 0 || neighbor_i >= axis1_dim)
446 {
447 // outside bounds; don't include cell state in the sum/counter
448 continue;
449 }
450 // add state to flattened array
451 neighborhood_cells[neighborhood_index] = vector[di];
452 neighborhood_index++;
453 }
454 }
455
464 void generate_periodic_neighborhood_2d(int *cell_index, T *neighborhood_cells, int &neighborhood_index)
465 {
466 int i = cell_index[0]; // get i-th index from array
467 int j = cell_index[1]; // get j-th index from array
468 int periodic_i; // axis1_dim index used by Periodic boundary type
469 int periodic_j; // axis2_dim index used by Periodic boundary type
470
471 if (neighborhood_type == CAEnums::VonNeumann)
472 {
473 for (int di = -boundary_radius; di <= boundary_radius; di++)
474 {
475 periodic_i = get_periodic_index(i, di, axis1_dim);
476 for (int dj = -boundary_radius; dj <= boundary_radius; dj++)
477 {
478 // exclude diagonal cells from neighborhood when VonNeumann is selected
480 {
481 // current di,dj cell is a diagonal neighbor so exclude it
482 continue;
483 }
484 periodic_j = get_periodic_index(j, dj, axis2_dim);
485 // add state to flattened array
486 neighborhood_cells[neighborhood_index] = matrix[periodic_i][periodic_j];
487 neighborhood_index++;
488 }
489 }
490 }
491 else // Moore neighborhood
492 {
493 for (int di = -boundary_radius; di <= boundary_radius; di++)
494 {
495 periodic_i = get_periodic_index(i, di, axis1_dim);
496 for (int dj = -boundary_radius; dj <= boundary_radius; dj++)
497 {
498 periodic_j = get_periodic_index(j, dj, axis2_dim);
499 // add state to flattened array
500 neighborhood_cells[neighborhood_index] = matrix[periodic_i][periodic_j];
501 neighborhood_index++;
502 }
503 }
504 }
505 }
506
515 void generate_cutoff_neighborhood_2d(int *cell_index, T *neighborhood_cells, int &neighborhood_index)
516 {
517 int i = cell_index[0]; // get i-th index from array
518 int j = cell_index[1]; // get j-th index from array
519 int neighbor_i; // neighboring cell's i-th index
520 int neighbor_j; // neighboring cell's j-th index
521
522 if (neighborhood_type == CAEnums::VonNeumann)
523 {
524 for (int di = -boundary_radius; di <= boundary_radius; di++)
525 {
526 neighbor_i = di + i;
527 for (int dj = -boundary_radius; dj <= boundary_radius; dj++)
528 {
529 neighbor_j = dj + j;
530 // exclude diagonal cells from neighborhood when VonNeumann is selected
532 {
533 // current di,dj cell is a diagonal neighbor so exclude it
534 continue;
535 }
536 // exclude cells that are out of bounds
537 if ((neighbor_i <= 0 || neighbor_i >= axis1_dim) || (neighbor_j <= 0 || neighbor_j >= axis2_dim))
538 {
539 // outside bounds; don't include cell state in the sum/counter
540 continue;
541 }
542 // add state to flattened array
543 neighborhood_cells[neighborhood_index] = matrix[neighbor_i][neighbor_j];
544 neighborhood_index++;
545 }
546 }
547 }
548 else // Moore neighborhood
549 {
550 for (int di = -boundary_radius; di <= boundary_radius; di++)
551 {
552 neighbor_i = di + i;
553 for (int dj = -boundary_radius; dj <= boundary_radius; dj++)
554 {
555 neighbor_j = dj + j;
556 // exclude cells that are out of bounds
557 if ((neighbor_i <= 0 || neighbor_i >= axis1_dim) || (neighbor_j <= 0 || neighbor_j >= axis2_dim))
558 {
559 // outside bounds; don't include cell state in the sum/counter
560 continue;
561 }
562 // add state to flattened array
563 neighborhood_cells[neighborhood_index] = matrix[neighbor_i][neighbor_j];
564 neighborhood_index++;
565 }
566 }
567 }
568 }
569
578 void generate_periodic_neighborhood_3d(int *cell_index, T *neighborhood_cells, int &neighborhood_index)
579 {
580 int i = cell_index[0]; // get i-th index from array
581 int j = cell_index[1]; // get j-th index from array
582 int k = cell_index[2]; // get k-th index from array
583 int periodic_i; // axis1_dim index used by Periodic boundary type
584 int periodic_j; // axis2_dim index used by Periodic boundary type
585 int periodic_k; // axis3_dim index used by Periodic boundary type
586
587 if (neighborhood_type == CAEnums::VonNeumann)
588 {
589 for (int di = -boundary_radius; di <= boundary_radius; di++)
590 {
591 periodic_i = get_periodic_index(i, di, axis1_dim);
592 for (int dj = -boundary_radius; dj <= boundary_radius; dj++)
593 {
594 periodic_j = get_periodic_index(j, dj, axis2_dim);
595 for (int dk = -boundary_radius; dk <= boundary_radius; dk++)
596 {
597 // exclude diagonal cells from neighborhood when VonNeumann is selected
598 if (is_diagonal_neighboring_cell_3d(di, dj, dk))
599 {
600 // current di,dj,dk cell is a diagonal neighbor so exclude it
601 continue;
602 }
603 periodic_k = get_periodic_index(k, dk, axis3_dim);
604 // add state to flattened array
605 neighborhood_cells[neighborhood_index] = tensor[periodic_i][periodic_j][periodic_k];
606 neighborhood_index++;
607 }
608 }
609 }
610 }
611 else // Moore neighborhood
612 {
613 for (int di = -boundary_radius; di <= boundary_radius; di++)
614 {
615 periodic_i = get_periodic_index(i, di, axis1_dim);
616 for (int dj = -boundary_radius; dj <= boundary_radius; dj++)
617 {
618 periodic_j = get_periodic_index(j, dj, axis2_dim);
619 for (int dk = -boundary_radius; dk <= boundary_radius; dk++)
620 {
621 periodic_k = get_periodic_index(k, dk, axis3_dim);
622 // add state to flattened array
623 neighborhood_cells[neighborhood_index] = tensor[periodic_i][periodic_j][periodic_k];
624 neighborhood_index++;
625 }
626 }
627 }
628 }
629 }
630
639 void generate_cutoff_neighborhood_3d(int *cell_index, T *neighborhood_cells, int &neighborhood_index)
640 {
641 int i = cell_index[0]; // get i-th index from array
642 int j = cell_index[1]; // get j-th index from array
643 int k = cell_index[2]; // get k-th index from array
644 int neighbor_i; // neighboring cell's i-th index
645 int neighbor_j; // neighboring cell's j-th index
646 int neighbor_k; // neighboring cell's k-th index
647
648 if (neighborhood_type == CAEnums::VonNeumann)
649 {
650 for (int di = -boundary_radius; di <= boundary_radius; di++)
651 {
652 neighbor_i = di + i;
653 for (int dj = -boundary_radius; dj <= boundary_radius; dj++)
654 {
655 neighbor_j = dj + j;
656 for (int dk = -boundary_radius; dk <= boundary_radius; dk++)
657 {
658 neighbor_k = dk + k;
659 // exclude diagonal cells from neighborhood when VonNeumann is selected
660 if (is_diagonal_neighboring_cell_3d(di, dj, dk))
661 {
662 // current di,dj,dk cell is a diagonal neighbor so exclude it
663 continue;
664 }
665 // exclude cells that are out of bounds
666 if ((neighbor_i <= 0 || neighbor_i >= axis1_dim) ||
667 (neighbor_j <= 0 || neighbor_j >= axis2_dim) ||
668 (neighbor_k <= 0 || neighbor_k >= axis3_dim))
669 {
670 // outside bounds; don't include cell state in the sum/counter
671 continue;
672 }
673 // add state to flattened array
674 neighborhood_cells[neighborhood_index] = tensor[neighbor_i][neighbor_j][neighbor_k];
675 neighborhood_index++;
676 }
677 }
678 }
679 }
680 else // Moore neighborhood
681 {
682 for (int di = -boundary_radius; di <= boundary_radius; di++)
683 {
684 neighbor_i = di + i;
685 for (int dj = -boundary_radius; dj <= boundary_radius; dj++)
686 {
687 neighbor_j = dj + j;
688 for (int dk = -boundary_radius; dk <= boundary_radius; dk++)
689 {
690 neighbor_k = dk + k;
691 // exclude cells that are out of bounds
692 if ((neighbor_i <= 0 || neighbor_i >= axis1_dim) ||
693 (neighbor_j <= 0 || neighbor_j >= axis2_dim) ||
694 (neighbor_k <= 0 || neighbor_k >= axis3_dim))
695 {
696 // outside bounds; don't include cell state in the sum/counter
697 continue;
698 }
699 // add state to flattened array
700 neighborhood_cells[neighborhood_index] = tensor[neighbor_i][neighbor_j][neighbor_k];
701 neighborhood_index++;
702 }
703 }
704 }
705 }
706 }
707
714 T *malloc_neighborhood_array(int rank)
715 {
716 /*
717 * Generate a flatten array of the cell's neighborhood.
718 * The neighborhood array can then be utilized for Majority, Parity, or Custom rule
719 */
720 int max_neighborhood_size = get_neighborhood_size(rank, boundary_radius, neighborhood_type);
721 // allocate memory and check if operation was successful
722 T *neighborhood_cells = new (std::nothrow) T[max_neighborhood_size];
723 return neighborhood_cells;
724 }
725
733 int append_log()
734 {
735 std::ofstream file;
736 file.open(FILE_PATH, std::ios::app);
737
738 if (vector != nullptr)
739 {
740 for (int i = 0; i < axis1_dim; i++)
741 {
742 file << vector[i].state << ",";
743 }
744 }
745 else if (matrix != nullptr)
746 {
747 for (int j = 0; j < axis1_dim; j++)
748 {
749 for (int k = 0; k < axis2_dim; k++)
750 {
751 file << matrix[j][k].state << ",";
752 }
753 }
754 }
755 else if (tensor != nullptr)
756 {
757 for (int i = 0; i < axis1_dim; i++)
758 {
759 for (int j = 0; j < axis2_dim; j++)
760 {
761 for (int k = 0; k < axis3_dim; k++)
762 {
763 file << tensor[i][j][k].state << ",";
764 }
765 }
766 }
767 }
768 else
769 {
770 return CAEnums::CellsAreNull;
771 }
772 file << "\n";
773 file.close();
774 return (0);
775 }
776
781 int create_log(void)
782 {
783 std::ofstream file;
784 file.open(FILE_PATH, std::ios::trunc | std::ios::out); // If it is pre-existing content, it should be erased
785
786 file << this->num_states << ",\n"; // First Line: Number of the states.
787 // Rule and function
788 file << this->axis1_dim << ","
789 << this->axis2_dim << ","
790 << this->axis3_dim << ",\n"; // Second Line: Dimensions of each axis.
791 file.close();
792 return 0;
793 }
794
795public:
802 {
803 return vector;
804 }
805
812 {
813 return next_vector;
814 }
815
822 {
823 return matrix;
824 }
825
832 {
833 return next_matrix;
834 }
835
842 {
843 return tensor;
844 }
845
852 {
853 return next_tensor;
854 }
855
866 int setup_boundary(CAEnums::Boundary bound_type, int radius)
867 {
868 if (radius <= 0)
869 {
870 return CAEnums::InvalidRadius;
871 }
872 if (vector != nullptr)
873 {
874 if (radius > axis1_dim / 2)
875 {
876 return CAEnums::RadiusLargerThanDimensions;
877 }
878 }
879 else if (matrix != nullptr)
880 {
881 if (radius > axis1_dim / 2 || radius > axis2_dim / 2)
882 {
883 return CAEnums::RadiusLargerThanDimensions;
884 }
885 }
886 else if (tensor != nullptr)
887 {
888 if (radius > axis2_dim / 2 || radius > axis3_dim / 2)
889 {
890 return CAEnums::RadiusLargerThanDimensions;
891 }
892 }
893
894 this->boundary_type = bound_type;
895 this->boundary_radius = radius;
896 return 0;
897 }
898
905 {
906 steps_taken = 0;
907 vector = nullptr;
908 next_vector = nullptr;
909 matrix = nullptr;
910 next_matrix = nullptr;
911 tensor = nullptr;
912 next_tensor = nullptr;
913 }
914
921 {
922 // Free each sub-array
923 if (vector != nullptr)
924 {
925 // Free the array of pointers
926 delete[] vector;
927 delete[] next_vector;
928 }
929
930 if (matrix != nullptr)
931 {
932 // Free each sub-array
933 for (int i = 0; i < axis1_dim; ++i)
934 {
935 delete[] matrix[i];
936 delete[] next_matrix[i];
937 }
938 // Free the array of pointers
939 delete[] matrix;
940 delete[] next_matrix;
941 }
942
943 if (tensor != nullptr)
944 {
945 // Free each sub-array
946 for (int i = 0; i < axis1_dim; ++i)
947 {
948 for (int j = 0; j < axis2_dim; j++)
949 {
950 delete[] tensor[i][j];
951 delete[] next_tensor[i][j];
952 }
953
954 delete[] tensor[i];
955 delete[] next_tensor[i];
956 }
957
958 // Free the array of pointers
959 delete[] tensor;
960 delete[] next_tensor;
961 }
962 }
963
974 int setup_dimensions_1d(int axis1_dim, int fill_value = 0)
975 {
976 if (vector != nullptr)
977 {
978 return CAEnums::CellsAlreadyInitialized;
979 }
980
981 this->axis1_dim = axis1_dim;
982 vector = new (std::nothrow) T[axis1_dim];
983 next_vector = new (std::nothrow) T[axis1_dim];
984
985 if (vector == nullptr || next_vector == nullptr)
986 {
987 return CAEnums::CellsMalloc;
988 }
989
990 // initialize vector filled with zeros
991 for (int j = 0; j < axis1_dim; j++)
992 {
993 vector[j].state = fill_value;
994 next_vector[j].state = fill_value;
995 }
996
997 create_log();
998 return 0;
999 }
1000
1012 int setup_dimensions_2d(int axis1_dim, int axis2_dim, int fill_value = 0)
1013 {
1014 if (matrix != nullptr)
1015 {
1016 return CAEnums::CellsAlreadyInitialized;
1017 }
1018
1019 this->axis1_dim = axis1_dim;
1020 this->axis2_dim = axis2_dim;
1021 matrix = new (std::nothrow) T *[axis1_dim];
1022 next_matrix = new (std::nothrow) T *[axis1_dim];
1023
1024 if (matrix == nullptr || next_matrix == nullptr)
1025 {
1026 return CAEnums::CellsMalloc;
1027 }
1028
1029 for (int i = 0; i < axis1_dim; i++)
1030 {
1031 matrix[i] = new (std::nothrow) T[axis2_dim];
1032 next_matrix[i] = new (std::nothrow) T[axis2_dim];
1033 }
1034
1035 // initialize matrix filled with zeros
1036 for (int j = 0; j < axis1_dim; j++)
1037 {
1038 for (int k = 0; k < axis2_dim; k++)
1039 {
1040 matrix[j][k].state = fill_value;
1041 next_matrix[j][k].state = fill_value;
1042 }
1043 }
1044
1045 create_log();
1046 return 0;
1047 }
1048
1061 int setup_dimensions_3d(int axis1_dim, int axis2_dim, int axis3_dim, int fill_value = 0)
1062 {
1063 if (tensor != nullptr)
1064 {
1065 return CAEnums::CellsAlreadyInitialized;
1066 }
1067
1068 this->axis1_dim = axis1_dim;
1069 this->axis2_dim = axis2_dim;
1070 this->axis3_dim = axis3_dim;
1071 tensor = new (std::nothrow) T **[axis1_dim];
1072 next_tensor = new (std::nothrow) T **[axis1_dim];
1073
1074 if (tensor == nullptr || next_tensor == nullptr)
1075 {
1076 return CAEnums::CellsMalloc;
1077 }
1078
1079 for (int i = 0; i < axis1_dim; i++)
1080 {
1081 tensor[i] = new (std::nothrow) T *[axis2_dim];
1082 next_tensor[i] = new (std::nothrow) T *[axis2_dim];
1083
1084 for (int j = 0; j < axis2_dim; j++)
1085 {
1086 tensor[i][j] = new (std::nothrow) T[axis3_dim];
1087 next_tensor[i][j] = new (std::nothrow) T[axis3_dim];
1088 }
1089 }
1090
1091 // initialize matrix filled with zeros
1092 for (int i = 0; i < axis1_dim; i++)
1093 {
1094 for (int j = 0; j < axis2_dim; j++)
1095 {
1096 for (int k = 0; k < axis3_dim; k++)
1097 {
1098 tensor[i][j][k].state = fill_value;
1099 next_tensor[i][j][k].state = fill_value;
1100 }
1101 }
1102 }
1103
1104 create_log();
1105 return 0;
1106 }
1107
1118 int init_condition(int x_state, double prob)
1119 {
1120 if (!(x_state < num_states))
1121 {
1122 return CAEnums::InvalidCellStateCondition;
1123 }
1124
1125 srand(time(NULL));
1126 double random_cell_state;
1127
1128 if (vector != nullptr)
1129 {
1130 for (int i = 0; i < axis1_dim; i++)
1131 {
1132 random_cell_state = (double)rand() / RAND_MAX;
1133 if (random_cell_state < prob)
1134 {
1135 vector[i].state = x_state;
1136 }
1137 }
1138 }
1139 else if (matrix != nullptr)
1140 {
1141 for (int j = 0; j < axis1_dim; j++)
1142 {
1143 for (int k = 0; k < axis2_dim; k++)
1144 {
1145 random_cell_state = (double)rand() / RAND_MAX;
1146 if (random_cell_state < prob)
1147 {
1148 matrix[j][k].state = x_state;
1149 }
1150 }
1151 }
1152 }
1153 else if (tensor != nullptr)
1154 {
1155 for (int i = 0; i < axis1_dim; i++)
1156 {
1157 for (int j = 0; j < axis2_dim; j++)
1158 {
1159 for (int k = 0; k < axis3_dim; k++)
1160 {
1161 random_cell_state = (double)rand() / RAND_MAX;
1162 if (random_cell_state < prob)
1163 {
1164 tensor[i][j][k].state = x_state;
1165 }
1166 }
1167 }
1168 }
1169 }
1170 else
1171 {
1172 return CAEnums::CellsAreNull;
1173 }
1174 return 0;
1175 }
1176
1192 int step(void(custom_rule)(int *, int, T *, int, T &))
1193 {
1194 int error_code = 0; // store error code return by other methods
1195 T new_cell_state; // stores the cell's new state
1196 T empty_cell_state; // cell state for zeroing out old states
1197 int index_size; // number of indices required to address the cell
1198
1199 if (vector != nullptr)
1200 {
1201 // store the main cell's index in cell_index for custom rule type
1202 index_size = 1;
1203#ifdef ENABLE_OMP
1204#pragma omp parallel for firstprivate(error_code) private(new_cell_state)
1205#endif
1206 for (int i = 0; i < axis1_dim; i++)
1207 {
1208 int cell_index[index_size] = {i};
1209 new_cell_state = vector[i];
1210 error_code = get_state_from_neighborhood_1d(cell_index, index_size, new_cell_state, custom_rule);
1211 if (error_code < 0)
1212 {
1213#ifdef ENABLE_OMP
1214#pragma omp cancel for
1215#endif
1216#ifndef ENABLE_OMP
1217 /*
1218 * Return error code when omp is not enabled
1219 * otherwise the above pragma will exit out of the for loop(s)
1220 */
1221 return error_code;
1222#endif
1223 }
1224 /*
1225 * The update cell if new_cell_state is no empty_state.
1226 * Avoids overwriting the motion of cells.
1227 */
1228 if (new_cell_state != empty_cell_state)
1229 {
1230 next_vector[cell_index[0]] = new_cell_state;
1231 }
1232 }
1233 // store next cell state to the current cell state for the next time step
1234 swap_states<T>(vector, next_vector, axis1_dim);
1235 }
1236 else if (matrix != nullptr)
1237 {
1238 // store the main cell's index in cell_index for custom rule type
1239 index_size = 2;
1240#ifdef ENABLE_OMP
1241#pragma omp parallel for firstprivate(error_code) private(new_cell_state)
1242#endif
1243 for (int i = 0; i < axis1_dim; i++)
1244 {
1245 for (int j = 0; j < axis2_dim; j++)
1246 {
1247 int cell_index[index_size] = {i, j};
1248 new_cell_state = matrix[i][j];
1249 error_code = get_state_from_neighborhood_2d(cell_index, index_size, new_cell_state, custom_rule);
1250 if (error_code < 0)
1251 {
1252#ifdef ENABLE_OMP
1253#pragma omp cancel for
1254#endif
1255#ifndef ENABLE_OMP
1256 /*
1257 * Return error code when omp is not enabled
1258 * otherwise the above pragma will exit out of the for loop(s)
1259 */
1260 return error_code;
1261#endif
1262 }
1263 /*
1264 * The update cell if new_cell_state is no empty_state.
1265 * Avoids overwriting the motion of cells.
1266 */
1267 if (new_cell_state != empty_cell_state)
1268 {
1269 next_matrix[cell_index[0]][cell_index[1]] = new_cell_state;
1270 }
1271 }
1272 }
1273 // store next cell state to the current cell state for the next time step
1274 swap_states<T>(matrix, next_matrix, axis1_dim, axis2_dim);
1275 }
1276 else if (tensor != nullptr)
1277 {
1278 index_size = 3;
1279#ifdef ENABLE_OMP
1280#pragma omp parallel for firstprivate(error_code) private(new_cell_state)
1281#endif
1282 for (int i = 0; i < axis1_dim; i++)
1283 {
1284 for (int j = 0; j < axis2_dim; j++)
1285 {
1286 for (int k = 0; k < axis3_dim; k++)
1287 {
1288 int cell_index[index_size] = {i, j, k};
1289 new_cell_state = tensor[i][j][k];
1290 error_code = get_state_from_neighborhood_3d(cell_index, index_size, new_cell_state, custom_rule);
1291 if (error_code < 0)
1292 {
1293#ifdef ENABLE_OMP
1294#pragma omp cancel for
1295#endif
1296#ifndef ENABLE_OMP
1297 /*
1298 * Return error code when omp is not enabled
1299 * otherwise the above pragma will exit out of the for loop(s)
1300 */
1301 return error_code;
1302#endif
1303 }
1304 /*
1305 * The update cell if new_cell_state is no empty_state.
1306 * Avoids overwriting the motion of cells.
1307 */
1308 if (new_cell_state != empty_cell_state)
1309 {
1310 next_tensor[cell_index[0]][cell_index[1]][cell_index[2]] = new_cell_state;
1311 }
1312 }
1313 }
1314 }
1315 // store next cell state to the current cell state for the next time step
1316 swap_states<T>(tensor, next_tensor, axis1_dim, axis2_dim, axis3_dim);
1317 }
1318 else
1319 {
1320 return CAEnums::CellsAreNull;
1321 }
1322
1323 steps_taken++;
1324 // Appending the step to the file log
1325 error_code = append_log();
1326 return error_code;
1327 }
1328
1337 int step()
1338 {
1339 return step(nullptr); // return step(func) error code
1340 }
1341
1350 {
1351 if (vector != nullptr)
1352 {
1353 for (int i = 0; i < axis1_dim; i++)
1354 {
1355 std::cout << vector[i].state << " ";
1356 }
1357 std::cout << std::endl;
1358 }
1359 else if (matrix != nullptr)
1360 {
1361 for (int j = 0; j < axis1_dim; j++)
1362 {
1363 for (int k = 0; k < axis2_dim; k++)
1364 {
1365 std::cout << matrix[j][k].state << " ";
1366 }
1367 std::cout << std::endl;
1368 }
1369 }
1370 else if (tensor != nullptr)
1371 {
1372 for (int i = 0; i < axis1_dim; i++)
1373 {
1374 std::cout << "Printing " << i << "'th slice of Tensor" << std::endl;
1375 for (int j = 0; j < axis2_dim; j++)
1376 {
1377 for (int k = 0; k < axis3_dim; k++)
1378 {
1379 std::cout << tensor[i][j][k].state << " ";
1380 }
1381 std::cout << std::endl;
1382 }
1383 }
1384 }
1385 else
1386 {
1387 return CAEnums::CellsAreNull;
1388 }
1389 return 0;
1390 }
1391
1401 {
1402 if (neighborhood_type == CAEnums::VonNeumann)
1403 {
1404 return (2 * rank * radius) + 1; // +1 to include cell of interest
1405 }
1406 else // CAEnums::Moore neighborhood
1407 {
1408 return pow((2 * radius + 1), rank);
1409 }
1410 }
1411
1419 {
1420 // sets the counter for every cell state type to 0
1421 for (int j = 0; j < num_states; j++)
1422 {
1423 counter.insert(std::make_pair(j, 0));
1424 }
1425 }
1426};
1427
1428// Declare specialized int methods. Methods are defined in cellularautomata.cpp
1429
1440template <>
1441int CellularAutomata<int>::setup_dimensions_1d(int axis1_dim, int fill_value);
1442
1454template <>
1455int CellularAutomata<int>::setup_dimensions_2d(int axis1_dim, int axis2_dim, int fill_value);
1456
1469template <>
1470int CellularAutomata<int>::setup_dimensions_3d(int axis1_dim, int axis2_dim, int axis3_dim, int fill_value);
1471
1482template <>
1483int CellularAutomata<int>::init_condition(int x_state, double prob);
1484
1499template <>
1500int CellularAutomata<int>::set_new_cell_state(int *cell_index, int index_size,
1501 int *neighborhood_cells, int neighborhood_size,
1502 int &new_cell_state, void(custom_rule)(int *, int, int *, int, int &));
1503
1511template <>
1513
1521template <>
1523
1539template <>
1540int CellularAutomata<int>::step(void(custom_rule)(int *, int, int *, int, int &));
std::unordered_map< int, int > MajorityCounter
counter type for determining which cell state is the majority
Definition: CAdatatypes.h:29
This header files contains utility functions used by CellularAutomata class.
bool is_diagonal_neighboring_cell_3d(int i, int j, int k)
determines if a cell is diagonal to the central cell if k == 0 then both i and j have be non-zero dia...
Definition: CA_utils.cpp:28
bool less_than_votes(const std::pair< int, int > &a, const std::pair< int, int > &b)
Determines if a has less votes than b.
Definition: CA_utils.cpp:49
bool is_diagonal_neighboring_cell_2d(int i, int j)
determines if a cell is diagonal to the central cell diagram of slice: 1 = diagonal cell 1 0 1 0 0 0 ...
Definition: CA_utils.cpp:19
int get_periodic_index(int i, int di, int axis_dim)
get the periodic index used for finding periodic boundary neighbors
Definition: CA_utils.cpp:54
A base CellularAutomata class that contains non-templated member variables and method definitions fro...
Definition: CAdatatypes.h:101
int axis1_dim
count of cells in first dimension
Definition: CAdatatypes.h:108
BaseCellularAutomata()
Construct a new Cellular Automata:: Cellular Automata object.
CAEnums::Rule rule_type
enum code for rule type
Definition: CAdatatypes.h:107
int setup_cell_states(int num_states)
Defines the range of cell states to be used in the CA object.
int num_states
integer code for number of states
Definition: CAdatatypes.h:106
int axis2_dim
count of cells in second dimension
Definition: CAdatatypes.h:109
int boundary_radius
declare a radius for the boundary
Definition: CAdatatypes.h:104
int setup_rule(CAEnums::Rule rule_type)
Setup the rule choice to specify the rule type to be used in CA object.
int axis3_dim
count of cells in third dimension
Definition: CAdatatypes.h:110
CAEnums::Boundary boundary_type
enum code to hold boundary
Definition: CAdatatypes.h:103
CAEnums::Neighborhood neighborhood_type
enum code to hold neighborhood type
Definition: CAdatatypes.h:105
void print_error_status(CAEnums::ErrorCode error)
Prints an error message for the given error code.
int setup_neighborhood(CAEnums::Neighborhood neighborhood_type)
Setup neighborhood with values from enum neighborhood.
virtual ~BaseCellularAutomata()
Destroy the Cellular Automata:: Cellular Automata object.
Definition: CAdatatypes.h:124
A CellularAutomata class for simulating cellular automata models.
Definition: CAdatatypes.h:176
T *** get_next_tensor()
Get the next state tensor cell grid.
Definition: CAdatatypes.h:851
int init_condition(int x_state, double prob)
Initializes the first state of the grid using random numbers.
Definition: CAdatatypes.h:1118
T *** get_tensor()
Get the tensor cell grid.
Definition: CAdatatypes.h:841
T ** get_matrix()
Get the matrix cell grid.
Definition: CAdatatypes.h:821
int setup_dimensions_1d(int axis1_dim, int fill_value=0)
Set up 1d array of cell states.
Definition: CAdatatypes.h:974
int step()
Simulates a cellular automata step.
Definition: CAdatatypes.h:1337
int print_grid()
Print the current state of the grid.
Definition: CAdatatypes.h:1349
int setup_dimensions_2d(int axis1_dim, int axis2_dim, int fill_value=0)
Set up 2d matrix of cell states.
Definition: CAdatatypes.h:1012
T * get_next_vector()
Get the next state vector cell grid.
Definition: CAdatatypes.h:811
int setup_boundary(CAEnums::Boundary bound_type, int radius)
Setup boundary with enum values from boundary and set the boundary radius.
Definition: CAdatatypes.h:866
int setup_dimensions_3d(int axis1_dim, int axis2_dim, int axis3_dim, int fill_value=0)
Set up 3d tensor of cell states.
Definition: CAdatatypes.h:1061
static int get_neighborhood_size(int rank, int radius, CAEnums::Neighborhood neighborhood_type)
calculates the neighborhood size using the rank, radius and neighborhood type
Definition: CAdatatypes.h:1400
static void initialize_majority_rule_counter(MajorityCounter &counter, int num_states)
initializes a MajorityCounter instance utilized by Majority rule
Definition: CAdatatypes.h:1418
T * get_vector()
Get the vector cell grid.
Definition: CAdatatypes.h:801
int step(void(custom_rule)(int *, int, T *, int, T &))
Simulates a cellular automata step.
Definition: CAdatatypes.h:1192
~CellularAutomata()
Destroy the Cellular Automata object.
Definition: CAdatatypes.h:920
CellularAutomata()
Construct a new Cellular Automata object.
Definition: CAdatatypes.h:904
T ** get_next_matrix()
Get the next state matrix cell grid.
Definition: CAdatatypes.h:831
Enum namespace that contains various enum definitions used by CellularAutomata.
Definition: CAdatatypes.h:36
Neighborhood
enum containing the different type of neighborhoods the CellularAutomata class supports
Definition: CAdatatypes.h:43
Rule
enum containing the different type of rules the CellularAutomata class supports for it's transitions
Definition: CAdatatypes.h:66
ErrorCode
enum containing the various error codes the CellularAutomata class can return
Definition: CAdatatypes.h:78
Boundary
enum containing the different type of boundaries the CellularAutomata class supports
Definition: CAdatatypes.h:54
void custom_rule(int *cell_index, const int index_size, int *neighborhood_cells, const int neighborhood_size, int &new_cell_state)
Test custom rule function for setting a new cell state for the cell at cell_index.
Definition: test_CA.cpp:23