Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
ca_properties_test.cc
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
54#include <cstdint>
55#include <random>
56
57#include <gtest/gtest.h>
58
59#include <ca-traits.H>
60#include <tpl_ca_storage.H>
61#include <tpl_ca_lattice.H>
62#include <tpl_ca_neighborhood.H>
63#include <tpl_ca_rule.H>
64#include <tpl_ca_engine.H>
65#include <tpl_ca_async_engine.H>
67#include <tpl_ca_block_rule.H>
68
69using namespace Aleph;
70using namespace Aleph::CA;
71
72namespace
73{
74
76
78 double p_alive, std::uint32_t seed)
79{
80 std::mt19937 rng(seed);
81 std::uniform_real_distribution<double> u(0.0, 1.0);
82 Grid g({rows, cols}, 0);
83 for (ca_size_t i = 0; i < rows; ++i)
84 for (ca_size_t j = 0; j < cols; ++j)
85 g.set({static_cast<ca_index_t>(i), static_cast<ca_index_t>(j)},
86 u(rng) < p_alive ? 1 : 0);
87 return g;
88}
89
90std::size_t alive_count(const Grid &g)
91{
92 std::size_t n = 0;
93 for (ca_size_t i = 0; i < g.size(0); ++i)
94 for (ca_size_t j = 0; j < g.size(1); ++j)
95 if (g.at({static_cast<ca_index_t>(i), static_cast<ca_index_t>(j)}) != 0)
96 ++n;
97 return n;
98}
99
100bool grids_equal(const Grid &a, const Grid &b)
101{
102 if (a.size(0) != b.size(0) or a.size(1) != b.size(1))
103 return false;
104 for (ca_size_t i = 0; i < a.size(0); ++i)
105 for (ca_size_t j = 0; j < a.size(1); ++j)
106 if (a.at({static_cast<ca_index_t>(i), static_cast<ca_index_t>(j)})
107 != b.at({static_cast<ca_index_t>(i), static_cast<ca_index_t>(j)}))
108 return false;
109 return true;
110}
111
115template <typename F>
116void for_random_grids(F &&body)
117{
118 static const std::uint32_t seeds[] = {
119 0x00000001u, 0xC0FFEEu, 0xDEADBEEFu, 0xFEEDFACEu,
120 0xABCDEF01u, 0x13572468u, 0x9E3779B9u, 0x5BD1E995u};
121 static const ca_size_t sides[] = {12, 16, 24};
122 for (std::uint32_t s : seeds)
124 body(s, side);
125}
126
129{
130 const ca_index_t m = static_cast<ca_index_t>(n);
131 ca_index_t r = v % m;
132 if (r < 0)
133 r += m;
134 return r;
135}
136
137} // namespace
138
139// ===================================================================
140// Property: particle-conserving block rules conserve population
141// ===================================================================
142
144{
145 int grids = 0;
146 for_random_grids([&](std::uint32_t seed, ca_size_t side)
147 {
148 Grid g = make_random_grid(side, side, 0.20, seed);
149 const auto initial = alive_count(g);
151 std::move(g), BBM_Rule{}, Null_Neighborhood<2>{});
152 for (std::size_t t = 0; t < 60; ++t)
153 {
154 eng.step();
156 << "BBM lost particles (seed=" << seed << ", side=" << side
157 << ", step=" << t << ")";
158 }
159 ++grids;
160 });
161 EXPECT_GE(grids, 20) << "property must sweep enough random grids";
162}
163
165{
166 for_random_grids([&](std::uint32_t seed, ca_size_t side)
167 {
168 Grid g = make_random_grid(side, side, 0.20, seed);
169 const auto initial = alive_count(g);
171 std::move(g), TM_Gas_Rule{}, Null_Neighborhood<2>{});
172 for (std::size_t t = 0; t < 60; ++t)
173 {
174 eng.step();
176 << "TM Gas lost particles (seed=" << seed << ", side=" << side
177 << ", step=" << t << ")";
178 }
179 });
180}
181
182// ===================================================================
183// Property: Margolus with an involutive rule is reversible
184// ===================================================================
185
186namespace
187{
188
189template <typename Block_Rule>
190void check_reversible(Block_Rule rule, const char *label)
191{
192 for_random_grids([&](std::uint32_t seed, ca_size_t side)
193 {
194 // Margolus partitions act on even side lengths.
195 const ca_size_t s = (side % 2 == 0) ? side : side + 1;
196 Grid initial = make_random_grid(s, s, 0.45, seed);
197 Grid baseline = initial;
198
200 eng(std::move(initial), rule, Null_Neighborhood<2>{}, Margolus_Update{});
201
202 const std::size_t N = 300;
203 ASSERT_NO_THROW(eng.run(N))
204 << label << " forward threw (seed=" << seed << ")";
205 ASSERT_NO_THROW(eng.run_back(N))
206 << label << " backward threw (seed=" << seed << ")";
207
209 << label << ": forward(" << N << ") then backward(" << N
210 << ") must restore the initial state (seed=" << seed
211 << ", side=" << s << ")";
212 EXPECT_EQ(eng.steps_run(), 0u);
213 });
214}
215
216} // namespace
217
222
227
232
233// ===================================================================
234// Property: the synchronous engine is deterministic
235// ===================================================================
236
238{
239 for_random_grids([&](std::uint32_t seed, ca_size_t side)
240 {
241 Grid initial = make_random_grid(side, side, 0.35, seed);
242
247
248 for (std::size_t t = 0; t < 40; ++t)
249 {
250 ASSERT_TRUE(grids_equal(a.frame(), b.frame()))
251 << "non-deterministic frame (seed=" << seed << ", step=" << t << ")";
252 a.step();
253 b.step();
254 }
255 EXPECT_TRUE(grids_equal(a.frame(), b.frame()));
256 });
257}
258
259// ===================================================================
260// Property: at_safe is well-defined for any coordinate
261// ===================================================================
262
264{
265 for_random_grids([&](std::uint32_t seed, ca_size_t side)
266 {
267 Grid g = make_random_grid(side, side, 0.50, seed);
268 std::mt19937 rng(seed ^ 0xA5A5A5A5u);
269 // Range deliberately spans far past the grid bounds, including large
270 // negative indices, to exercise every fold in the toroidal policy.
271 std::uniform_int_distribution<int> coord(-4 * static_cast<int>(side),
272 4 * static_cast<int>(side));
273 for (int k = 0; k < 200; ++k)
274 {
275 const ca_index_t r = static_cast<ca_index_t>(coord(rng));
276 const ca_index_t c = static_cast<ca_index_t>(coord(rng));
277 const int v = g.at_safe({r, c});
278 // Binary grid: the resolved value must stay in-domain (no UB / no
279 // garbage read; ASan/UBSan in CI catches any OOB access here).
280 ASSERT_TRUE(v == 0 or v == 1)
281 << "at_safe returned out-of-domain value " << v
282 << " at (" << r << ", " << c << ")";
283 // Toroidal boundary: at_safe must agree with the wrapped in-range
284 // read.
285 const int folded = g.at({wrap(r, g.size(0)), wrap(c, g.size(1))});
286 ASSERT_EQ(v, folded)
287 << "at_safe disagrees with toroidal wrap at (" << r << ", " << c
288 << ")";
289 }
290 });
291}
size_t * rows
Definition ca-c-api.h:112
size_t cols
Definition ca-c-api.h:105
Common typedefs and tag types for the Cellular Automata module.
Update-scheme aware engine.
Fredkin–Toffoli Billiard Ball Machine block rule.
Critters reversible CA block rule.
Lattice that adds boundary-aware access on top of a storage.
Moore (Chebyshev) neighborhood of radius R in N dimensions.
Placeholder neighbourhood for engines that drive block rules.
Synchronous double-buffered engine.
void step()
Apply the rule to every cell once and swap buffers.
Toffoli–Margolus (TM) lattice-gas block rule.
#define TEST(name)
static mt19937 rng
#define N
Definition fib.C:294
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
constexpr Game_Of_Life_Rule make_game_of_life_rule() noexcept
Build the canonical Game of Life rule.
std::ptrdiff_t ca_index_t
Signed coordinate component used by lattices and neighborhoods.
Definition ca-traits.H:60
std::size_t ca_size_t
Unsigned size component used for extents and counts.
Definition ca-traits.H:63
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
Margolus 2×2 partition update for reversible CAs.
The lattice wraps around on every axis.
Definition ca-traits.H:124
FooMap m(5, fst_unit_pair_hash, snd_unit_pair_hash)
ValueArg< size_t > seed
Definition testHash.C:53
static int * k
gsl_rng * r
Phase 13 update-scheme aware engine.
Block-based local rules for Aleph::CA (Phase 13).
Synchronous double-buffered engine for cellular automata.
Cellular automata lattice with pluggable boundary policies.
Neighborhoods catalogue for Aleph::CA.
Rule mechanisms for Aleph::CA.
Dense, contiguous storage for cellular automata cells (1D/2D/3D).
Phase 13 update-scheme strategies for Aleph::CA.