Skip to content
Snippets Groups Projects
Verified Commit dd444c5d authored by Dorian Stoll's avatar Dorian Stoll
Browse files

zellularautomat: cpp: Initial port

parent 7ff4202e
No related branches found
No related tags found
No related merge requests found
......@@ -290,3 +290,7 @@ pyrightconfig.json
# Hidden files
.*
# Downloaded meson subprojects
subprojects/*
!subprojects/*.wrap
project(
'praktikum',
'c',
'cpp',
'fortran',
default_options: [
'warning_level=3',
......
/*
* simulate a cellular automaton with periodic boundaries (torus-like)
* OpenMP version
*
* (c) 2024 Dorian Stoll (C++ / Eigen port)
* (c) 2016 Steffen Christgau (C99 port, modularization, parallelization)
* (c) 1996,1997 Peter Sanders, Ingo Boesnach (original source)
*
* command line arguments:
* #1: Number of lines
* #2: Number of iterations to be simulated
*
*/
/*
* Eigen defaults to using column-major order for its container types.
*
* However, using it like that would make the lines parameter correspond to Eigens .cols() function
* instead of the more fitting .rows(). To make the code more obvious, we change the default.
*/
#define EIGEN_DEFAULT_TO_ROW_MAJOR
#include <Eigen/Eigen>
#include <cassert>
#include <cstdint>
#include <cstdlib>
#include <iostream>
#include <stdexcept>
class Automat {
private:
/* "ADT" State */
using cell_state_t = uint8_t;
using Field = Eigen::Matrix<cell_state_t, Eigen::Dynamic, Eigen::Dynamic>;
private:
/* horizontal size of the configuration */
static constexpr Eigen::Index XSIZE = 1024;
static constexpr Eigen::Index LINE_SIZE = XSIZE + 2;
/* annealing rule from ChoDro96 page 34
* the table is used to map the number of nonzero
* states in the neighborhood to the new state
*/
static constexpr std::array<cell_state_t, 10> ANNEAL {0, 0, 0, 0, 1, 0, 1, 1, 1, 1};
private:
int m_lines;
Field m_from {};
Field m_to {};
public:
Automat(const int lines) : m_lines {lines}
{
if (lines <= 0)
throw std::invalid_argument {"There must be at least one line"};
m_from.resize(lines + 2, LINE_SIZE);
m_to.resize(lines + 2, LINE_SIZE);
}
void run(const int its)
{
this->init_config();
for (int i = 0; i < its; i++) {
this->boundary();
this->simulate();
std::swap(m_from, m_to);
}
}
void report()
{
auto block = m_from(Eigen::seqN(1, m_lines), Eigen::all);
for (Eigen::Index y = 0; y < block.rows(); y++) {
block(y, 0) = 0;
block(y, block.cols() - 1) = 0;
}
const auto start = block.data();
const auto end = start + (block.size() * sizeof(*start));
std::copy(start, end, std::ostreambuf_iterator(std::cout));
}
private:
/* random starting configuration */
void init_config()
{
srand(424243);
for (Eigen::Index y = 1; y <= m_to.rows() - 2; y++) {
for (Eigen::Index x = 1; x <= m_from.cols() - 2; x++)
m_from(y, x) = rand() % 2;
}
}
/* treat torus like boundary conditions */
void boundary()
{
#pragma omp sections
{
#pragma omp section
#pragma omp parallel for
for (Eigen::Index y = 0; y < m_from.rows(); y++) {
/* copy rightmost column to the buffer column 0 */
m_from(y, 0) = m_from(y, m_from.cols() - 2);
/* copy leftmost column to the buffer column XSIZE + 1 */
m_from(y, m_from.cols() - 1) = m_from(y, 1);
}
#pragma omp section
#pragma omp parallel for
for (Eigen::Index x = 0; x < m_from.cols(); x++) {
/* copy bottommost row to buffer row 0 */
m_from(0, x) = m_from(m_from.rows() - 2, x);
/* copy topmost row to buffer row lines + 1 */
m_from(m_from.rows() - 1, x) = m_from(1, x);
}
}
}
/* make one simulation iteration with lines lines.
* old configuration is in from, new one is written to to.
*/
void simulate()
{
#pragma omp parallel for
for (Eigen::Index y = 1; y <= m_from.rows() - 2; y++) {
for (Eigen::Index x = 1; x <= m_from.cols() - 2; x++)
m_to(y, x) = transition(m_from, x, y);
}
}
/* a: reference to matrix; x,y: coordinates; result: n-th element of anneal,
where n is the number of neighbors */
cell_state_t transition(const Field &a, const Eigen::Index x, const Eigen::Index y) const
{
return ANNEAL[a(Eigen::seq(y - 1, y + 1), Eigen::seq(x - 1, x + 1)).sum()];
}
};
int main(int argc, char **argv)
{
assert(argc == 3);
int lines = atoi(argv[1]);
int its = atoi(argv[2]);
Automat ca {lines};
ca.run(its);
ca.report();
return EXIT_SUCCESS;
}
sources = [
'main.cpp',
]
# Build wrapped dependencies as static libraries and disable warnings
dependency_options = [
'default_library=static',
'warning_level=0',
'werror=false',
]
dependencies = [
dependency('openmp'),
dependency(
'eigen3',
fallback: ['eigen', 'eigen_dep'],
default_options: dependency_options,
include_type: 'system',
),
]
options = [
'cpp_std=c++17',
]
executable(
'zellularautomat-cpp',
sources,
dependencies: dependencies,
override_options: options,
)
subdir('c')
subdir('cpp')
subdir('fortran')
[wrap-file]
directory = eigen-3.4.0
source_url = https://gitlab.com/libeigen/eigen/-/archive/3.4.0/eigen-3.4.0.tar.bz2
source_filename = eigen-3.4.0.tar.bz2
source_hash = b4c198460eba6f28d34894e3a5710998818515104d6e74e5cc331ce31e46e626
patch_filename = eigen_3.4.0-2_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/eigen_3.4.0-2/get_patch
patch_hash = cb764fd9fec02d94aaa2ec673d473793c0d05da4f4154c142f76ef923ea68178
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/eigen_3.4.0-2/eigen-3.4.0.tar.bz2
wrapdb_version = 3.4.0-2
[provide]
eigen3 = eigen_dep
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment