Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
tpl_ca_ghost_lattice.H
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
66#ifndef TPL_CA_GHOST_LATTICE_H
67#define TPL_CA_GHOST_LATTICE_H
68
69#include <cstddef>
70#include <type_traits>
71#include <utility>
72
73#include <ah-errors.H>
74
75#include <ca-traits.H>
76#include <tpl_ca_concepts.H>
77#include <tpl_ca_lattice.H> // for ca_lattice_detail::is_constant_boundary_v
78
79namespace Aleph {
80namespace CA {
81
82namespace ca_ghost_detail {
83
85template <std::size_t N>
86inline std::array<ca_size_t, N> enlarge_extents(const std::array<ca_size_t, N> &user,
87 const std::size_t halo) noexcept
88{
89 std::array<ca_size_t, N> e{};
90 for (std::size_t d = 0; d < N; ++d)
91 e[d] = user[d] == 0 ? 0 : user[d] + 2 * halo;
92 return e;
93}
94
96inline ca_size_t positive_mod(ca_index_t v, const ca_size_t n) noexcept
97{
98 const ca_index_t m = static_cast<ca_index_t>(n);
99 ca_index_t r = v % m;
100 if (r < 0)
101 r += m;
102 return static_cast<ca_size_t>(r);
103}
104
107inline ca_size_t reflect_index(const ca_index_t v, const ca_size_t n) noexcept
108{
109 if (n == 1)
110 return 0;
111 const ca_index_t period = static_cast<ca_index_t>(2 * n);
112 ca_index_t r = v % period;
113 if (r < 0)
114 r += period;
115 const ca_index_t ni = static_cast<ca_index_t>(n);
116 if (r >= ni)
117 r = period - 1 - r;
118 return static_cast<ca_size_t>(r);
119}
120
122inline ca_size_t clamp_index(const ca_index_t v, const ca_size_t n) noexcept
123{
124 if (v < 0)
125 return 0;
126 const ca_size_t u = static_cast<ca_size_t>(v);
127 return u >= n ? n - 1 : u;
128}
129
130} // namespace ca_ghost_detail
131
151template <typename Storage, typename Boundary = OpenBoundary, std::size_t Halo = 1>
153{
154 static_assert(Halo >= 1, "Ghost_Lattice requires Halo >= 1");
155
156public:
158 using boundary_type = Boundary;
159 using state_type = typename Storage::state_type;
160 using coord_type = typename Storage::coord_type;
161 using extents_type = typename Storage::extents_type;
162
164 static constexpr std::size_t rank = Storage::rank;
166 static constexpr std::size_t halo_v = Halo;
167
168private:
172
174 [[nodiscard]] coord_type to_store(const coord_type &c) const noexcept
175 {
176 coord_type s = c;
177 for (std::size_t d = 0; d < rank; ++d)
178 s[d] = c[d] + static_cast<ca_index_t>(Halo);
179 return s;
180 }
181
184 {
185 if constexpr (ca_lattice_detail::is_constant_boundary_v<Boundary>)
186 return static_cast<state_type>(Boundary::value);
187 else
188 return state_type{};
189 }
190
195 {
196 using namespace ca_ghost_detail;
197 for (std::size_t d = 0; d < rank; ++d)
198 {
199 const ca_size_t n = user_ext_[d];
200 if (n == 0)
201 return out_of_range_value();
202
203 if constexpr (std::is_same_v<Boundary, ToroidalBoundary>)
204 {
205 c[d] = static_cast<ca_index_t>(positive_mod(c[d], n));
206 }
207 else if constexpr (std::is_same_v<Boundary, ReflectiveBoundary>)
208 {
209 c[d] = static_cast<ca_index_t>(reflect_index(c[d], n));
210 }
211 else if constexpr (std::is_same_v<Boundary, NeumannBoundary>)
212 {
213 c[d] = static_cast<ca_index_t>(clamp_index(c[d], n));
214 }
215 else
216 {
217 if (c[d] < 0 or static_cast<ca_size_t>(c[d]) >= n)
218 return out_of_range_value();
219 }
220 }
221 return store_.at(to_store(c));
222 }
223
224 // -----------------------------------------------------------------
225 // Halo fillers (one per boundary policy). Each walks the halo cells
226 // and writes the right value into the backing storage. They all use
227 // the same helper `for_each_halo_cell` because the structure of the
228 // halo is identical across policies.
229 // -----------------------------------------------------------------
230
236 template <typename F>
237 void for_each_halo_cell(F &&f) const
238 {
239 for (std::size_t d = 0; d < rank; ++d)
240 if (user_ext_[d] == 0)
241 return;
242
243 coord_type u{};
244 const ca_index_t h = static_cast<ca_index_t>(Halo);
245
246 // Walk the enlarged box as a disjoint shell: until a halo band is
247 // selected, recurse through interior plus the two halo bands; after
248 // that, the remaining dimensions may span their full enlarged range.
249 auto enumerate = [&](auto &&self_fn, std::size_t d, bool in_halo) -> void
250 {
251 if (d == rank)
252 {
253 if (in_halo)
254 f(u);
255 return;
256 }
257 const ca_index_t n = static_cast<ca_index_t>(user_ext_[d]);
258 if (n == 0)
259 return;
260
261 if (in_halo)
262 {
263 for (ca_index_t i = -h; i < n + h; ++i)
264 {
265 u[d] = i;
266 self_fn(self_fn, d + 1, true);
267 }
268 return;
269 }
270
271 if (d + 1 < rank)
272 {
273 for (ca_index_t i = 0; i < n; ++i)
274 {
275 u[d] = i;
276 self_fn(self_fn, d + 1, false);
277 }
278 }
279
280 if (h > 0)
281 {
282 for (ca_index_t i = -h; i < 0; ++i)
283 {
284 u[d] = i;
285 self_fn(self_fn, d + 1, true);
286 }
287 for (ca_index_t i = n; i < n + h; ++i)
288 {
289 u[d] = i;
290 self_fn(self_fn, d + 1, true);
291 }
292 }
293 };
294 enumerate(enumerate, 0, false);
295 }
296
300 {
302 }
303
306 {
307 return store_.at(to_store(user_c));
308 }
309
310public:
312 Ghost_Lattice() = default;
313
326 const state_type &init = state_type{})
328 , store_ext_(ca_ghost_detail::enlarge_extents<rank>(user_extents, Halo))
330 {}
331
332 // ------------------------------------------------------------------
333 // LatticeLike surface (see `tpl_ca_concepts.H`).
334 // ------------------------------------------------------------------
335
337 [[nodiscard]] static constexpr std::size_t dimension() noexcept { return rank; }
338
341 {
342 ca_size_t p = 1;
343 for (std::size_t d = 0; d < rank; ++d)
344 p *= user_ext_[d];
345 return p;
346 }
347
352 [[nodiscard]] ca_size_t size(std::size_t d) const
353 {
355 << "Ghost_Lattice::size: axis " << d << " out of range";
356 return user_ext_[d];
357 }
358
361
364
366 [[nodiscard]] static constexpr std::size_t halo_radius() noexcept { return Halo; }
367
372 [[nodiscard]] state_type at(const coord_type &c) const
373 {
374 for (std::size_t d = 0; d < rank; ++d)
375 ah_out_of_range_error_if(c[d] < 0 or static_cast<ca_size_t>(c[d]) >= user_ext_[d])
376 << "Ghost_Lattice::at: coord[" << d << "]=" << c[d] << " out of [0, "
377 << user_ext_[d] << ")";
378 return store_get(c);
379 }
380
391 void set(const coord_type &c, const state_type &v)
392 {
393 for (std::size_t d = 0; d < rank; ++d)
394 ah_out_of_range_error_if(c[d] < 0 or static_cast<ca_size_t>(c[d]) >= user_ext_[d])
395 << "Ghost_Lattice::set: coord[" << d << "]=" << c[d] << " out of [0, "
396 << user_ext_[d] << ")";
397 store_set(c, v);
398 }
399
419 {
420 const ca_index_t h = static_cast<ca_index_t>(Halo);
421 bool in_halo_range = true;
422 for (std::size_t d = 0; d < rank; ++d)
423 {
424 if (user_ext_[d] == 0)
425 return out_of_range_value();
426 const ca_index_t n = static_cast<ca_index_t>(user_ext_[d]);
427 if (c[d] < -h or c[d] >= n + h)
428 {
429 in_halo_range = false;
430 break;
431 }
432 }
433 if (in_halo_range)
434 return store_get(c);
435 return resolve_via_policy(c);
436 }
437
438 // ------------------------------------------------------------------
439 // Halo refresh — the Phase 4 raison d'être.
440 // ------------------------------------------------------------------
441
457 {
458 if constexpr (std::is_same_v<Boundary, OpenBoundary>)
459 {
460 for_each_halo_cell([&](const coord_type &u) { store_set(u, state_type{}); });
461 }
462 else if constexpr (ca_lattice_detail::is_constant_boundary_v<Boundary>)
463 {
464 const state_type v = static_cast<state_type>(Boundary::value);
465 for_each_halo_cell([&](const coord_type &u) { store_set(u, v); });
466 }
467 else if constexpr (std::is_same_v<Boundary, NeumannBoundary>)
468 {
469 for_each_halo_cell([&](const coord_type &u)
470 {
471 coord_type src = u;
472 for (std::size_t d = 0; d < rank; ++d)
473 src[d] = static_cast<ca_index_t>(
475 store_set(u, store_get(src));
476 });
477 }
478 else if constexpr (std::is_same_v<Boundary, ToroidalBoundary>)
479 {
480 for_each_halo_cell([&](const coord_type &u)
481 {
482 coord_type src = u;
483 for (std::size_t d = 0; d < rank; ++d)
484 src[d] = static_cast<ca_index_t>(
486 store_set(u, store_get(src));
487 });
488 }
489 else if constexpr (std::is_same_v<Boundary, ReflectiveBoundary>)
490 {
491 for_each_halo_cell([&](const coord_type &u)
492 {
493 coord_type src = u;
494 for (std::size_t d = 0; d < rank; ++d)
495 src[d] = static_cast<ca_index_t>(
497 store_set(u, store_get(src));
498 });
499 }
500 else
501 {
502 static_assert(std::is_same_v<Boundary, OpenBoundary>
503 or std::is_same_v<Boundary, ToroidalBoundary>
504 or std::is_same_v<Boundary, ReflectiveBoundary>
505 or std::is_same_v<Boundary, NeumannBoundary>
506 or ca_lattice_detail::is_constant_boundary_v<Boundary>,
507 "Ghost_Lattice: unsupported Boundary policy");
508 }
509 }
510
511 // ------------------------------------------------------------------
512 // Utilities shared with `Lattice`.
513 // ------------------------------------------------------------------
514
516 [[nodiscard]] const Storage &storage() const noexcept { return store_; }
517
520
524 void fill(const state_type &value)
525 {
526 coord_type u{};
527 auto go = [&](auto &&self_fn, std::size_t d) -> void
528 {
529 if (d == rank)
530 {
531 store_set(u, value);
532 return;
533 }
534 const ca_index_t n = static_cast<ca_index_t>(user_ext_[d]);
535 for (ca_index_t i = 0; i < n; ++i)
536 {
537 u[d] = i;
538 self_fn(self_fn, d + 1);
539 }
540 };
541 go(go, 0);
542 }
543
545 void swap(Ghost_Lattice &other) noexcept(noexcept(store_.swap(other.store_)))
546 {
547 using std::swap;
548 swap(user_ext_, other.user_ext_);
549 swap(store_ext_, other.store_ext_);
550 store_.swap(other.store_);
551 }
552};
553
561template <typename Storage, typename Boundary, std::size_t Halo>
563 Ghost_Lattice<Storage, Boundary, Halo> &b) noexcept(noexcept(a.swap(b)))
564{
565 a.swap(b);
566}
567
568} // namespace CA
569} // namespace Aleph
570
571#endif // TPL_CA_GHOST_LATTICE_H
Exception handling system with formatted messages for Aleph-w.
#define ah_out_of_range_error_if(C)
Throws std::out_of_range if condition holds.
Definition ah-errors.H:584
long double h
Definition btreepic.C:154
size_t size_t int32_t value
Definition ca-c-api.h:116
Common typedefs and tag types for the Cellular Automata module.
Lattice with Halo ghost layers around the user-visible cells.
void for_each_halo_cell(F &&f) const
Invoke f(user_coord) on every halo cell of the storage.
const Storage & storage() const noexcept
Direct access to the underlying storage (read-only).
ca_size_t size() const noexcept
void refresh_halo()
Populate every ghost cell according to the boundary policy.
void set(const coord_type &c, const state_type &v)
Strict write through a user coordinate.
extents_type user_ext_
extents seen from the outside
extents_type store_ext_
extents of the backing storage (user + 2H)
Storage & storage() noexcept
Direct access to the underlying storage.
static constexpr std::size_t halo_radius() noexcept
state_type at_safe(const coord_type &c) const
Boundary-aware read exploiting the halo as a fast path.
void swap(Ghost_Lattice &other) noexcept(noexcept(store_.swap(other.store_)))
O(1) swap with another ghost lattice of the same type.
typename Storage::coord_type coord_type
state_type store_get(const coord_type &user_c) const
Read directly from the store at the given user coord.
void store_set(const coord_type &user_c, const state_type &value)
Write value directly into the store at the given user coord.
state_type at(const coord_type &c) const
Strict read through a user coordinate.
state_type out_of_range_value() const
Value returned when at_safe sees a coordinate outside the halo.
Storage store_
underlying storage (allocated once)
Ghost_Lattice()=default
Construct an empty ghost lattice (no cells allocated).
coord_type to_store(const coord_type &c) const noexcept
Convert a user coordinate into a store coordinate.
const extents_type & store_extents() const noexcept
Ghost_Lattice(const extents_type &user_extents, const state_type &init=state_type{})
Construct a ghost lattice with given user extents and init value.
static constexpr std::size_t rank
Number of axes (same as the underlying storage).
ca_size_t size(std::size_t d) const
User-visible extent along axis d.
void fill(const state_type &value)
Set every user-visible cell to value.
typename Storage::state_type state_type
static constexpr std::size_t dimension() noexcept
typename Storage::extents_type extents_type
static constexpr std::size_t halo_v
Halo thickness (ghost cells per side per axis).
const extents_type & extents() const noexcept
state_type resolve_via_policy(coord_type c) const
Resolve a user coordinate through the boundary policy (slow path).
#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
std::array< ca_size_t, N > enlarge_extents(const std::array< ca_size_t, N > &user, const std::size_t halo) noexcept
Bump the storage extents by 2 * Halo on every axis.
ca_size_t positive_mod(ca_index_t v, const ca_size_t n) noexcept
Wrap a possibly-negative index into [0, n) with positive modulo.
ca_size_t clamp_index(const ca_index_t v, const ca_size_t n) noexcept
Clamp a signed coordinate into [0, n - 1] for Neumann fills.
ca_size_t reflect_index(const ca_index_t v, const ca_size_t n) noexcept
Reflect a signed coordinate into [0, n) using the no-repeat triangle wave that also powers Lattice::a...
std::ptrdiff_t ca_index_t
Signed coordinate component used by lattices and neighborhoods.
Definition ca-traits.H:60
void swap(Bit_Cell_Storage< N > &a, Bit_Cell_Storage< N > &b) noexcept
Free-function swap so the storage plays nicely with std::swap.
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
auto enumerate(const Container &c)
Return pairs of (element, index).
static std::atomic< bool > init
Definition hash-fct.C:54
FooMap m(5, fst_unit_pair_hash, snd_unit_pair_hash)
gsl_rng * r
C++20 concepts for the Cellular Automata module.
Cellular automata lattice with pluggable boundary policies.