Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
tpl_ca_graph_automaton.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
73#ifndef TPL_CA_GRAPH_AUTOMATON_H
74#define TPL_CA_GRAPH_AUTOMATON_H
75
76#include <array>
77#include <cstddef>
78#include <functional>
79#include <span>
80#include <type_traits>
81#include <utility>
82
83#include <ah-errors.H>
84#include <tpl_array.H>
85
86#include <ca-traits.H>
87#include <tpl_ca_concepts.H>
88
89namespace Aleph {
90namespace CA {
91
106template <typename T>
108{
109 static_assert(CellState<T>, "Graph_Lattice requires a CellState type");
110
111public:
112 using state_type = T;
114 using extents_type = std::array<ca_size_t, 1>;
115
117 static constexpr std::size_t rank = 1;
118
119private:
123
124public:
126 Graph_Lattice() = default;
127
139 explicit Graph_Lattice(const Array<Array<std::size_t>> &adjacency,
140 const T &init = T{})
141 : num_nodes_(static_cast<ca_size_t>(adjacency.size()))
143 , adjacency_(num_nodes_, Array<std::size_t>{})
144 {
145 for (ca_size_t i = 0; i < num_nodes_; ++i)
146 {
147 const auto &row = adjacency[i];
148 Array<std::size_t> r(row.size(), std::size_t{0});
149 for (std::size_t k = 0; k < row.size(); ++k)
150 {
152 << "Graph_Lattice: neighbour id " << row[k] << " of node " << i
153 << " is out of range [0, " << num_nodes_ << ")";
154 r[k] = row[k];
155 }
156 adjacency_[i] = std::move(r);
157 }
158 }
159
160 // -------------------------------------------------------------
161 // LatticeLike surface.
162 // -------------------------------------------------------------
163
164 [[nodiscard]] static constexpr std::size_t dimension() noexcept { return rank; }
165
167
168 [[nodiscard]] ca_size_t size(std::size_t d) const
169 {
171 << "Graph_Lattice::size: axis " << d << " out of range";
172 return num_nodes_;
173 }
174
179
180 [[nodiscard]] T at(const coord_type &c) const
181 {
182 ah_out_of_range_error_if(c[0] < 0 or static_cast<ca_size_t>(c[0]) >= num_nodes_)
183 << "Graph_Lattice::at: id " << c[0] << " out of [0, " << num_nodes_ << ")";
184 return cells_[static_cast<ca_size_t>(c[0])];
185 }
186
187 void set(const coord_type &c, const T &v)
188 {
189 ah_out_of_range_error_if(c[0] < 0 or static_cast<ca_size_t>(c[0]) >= num_nodes_)
190 << "Graph_Lattice::set: id " << c[0] << " out of [0, " << num_nodes_ << ")";
191 cells_[static_cast<ca_size_t>(c[0])] = v;
192 }
193
197 [[nodiscard]] T at_safe(const coord_type &c) const
198 {
199 if (c[0] < 0 or static_cast<ca_size_t>(c[0]) >= num_nodes_)
200 return T{};
201 return cells_[static_cast<ca_size_t>(c[0])];
202 }
203
204 // -------------------------------------------------------------
205 // Graph-specific accessors.
206 // -------------------------------------------------------------
207
209 [[nodiscard]] T at_node(std::size_t n) const
210 {
212 << "Graph_Lattice::at_node: id " << n << " out of [0, " << num_nodes_ << ")";
213 return cells_[n];
214 }
215
217 void set_node(std::size_t n, const T &v)
218 {
220 << "Graph_Lattice::set_node: id " << n << " out of [0, " << num_nodes_ << ")";
221 cells_[n] = v;
222 }
223
225 [[nodiscard]] std::size_t degree(std::size_t n) const
226 {
228 << "Graph_Lattice::degree: id " << n << " out of [0, " << num_nodes_ << ")";
229 return adjacency_[n].size();
230 }
231
233 [[nodiscard]] const Array<std::size_t> &neighbours(std::size_t n) const
234 {
236 << "Graph_Lattice::neighbours: id " << n << " out of [0, " << num_nodes_ << ")";
237 return adjacency_[n];
238 }
239
242 {
243 std::size_t m = 0;
244 for (ca_size_t i = 0; i < num_nodes_; ++i)
245 if (adjacency_[i].size() > m)
246 m = adjacency_[i].size();
247 return m;
248 }
249
251 void fill(const T &value)
252 {
253 for (ca_size_t i = 0; i < num_nodes_; ++i)
254 cells_[i] = value;
255 }
256
258 void swap(Graph_Lattice &other) noexcept
259 {
260 using std::swap;
261 swap(num_nodes_, other.num_nodes_);
262 cells_.swap(other.cells_);
263 adjacency_.swap(other.adjacency_);
264 }
265};
266
268template <typename T>
269inline void swap(Graph_Lattice<T> &a, Graph_Lattice<T> &b) noexcept
270{
271 a.swap(b);
272}
273
274// -----------------------------------------------------------------------
275// Synchronous engine for graph CAs.
276// -----------------------------------------------------------------------
277
293template <typename R, typename T>
295and (requires(const R &r, const T &s, Neighbor_View<T> v) {
296 { r(s, v) } -> std::convertible_to<T>;
297 }
298 or requires(const R &r, const T &s, Neighbor_View<T> v,
299 const Cell_Context<1> &ctx) {
300 { r(s, v, ctx) } -> std::convertible_to<T>;
301 });
302
320template <typename Lattice, typename Rule>
322{
323public:
326
327private:
329 "Graph_Synchronous_Engine requires a GraphRuleLike rule");
330
333 Rule rule_;
335 std::function<void(std::size_t, const Lattice &)> pre_hook_;
336 std::function<void(std::size_t, const Lattice &)> post_hook_;
337 std::size_t step_count_ = 0;
338
339public:
351 : cur_buf_(std::move(initial)), nxt_buf_(cur_buf_), rule_(std::move(r))
352 {
353 nbuf_.reserve(cur_buf_.max_degree());
354 }
355
357 [[nodiscard]] const Lattice &frame() const noexcept { return cur_buf_; }
358
360 [[nodiscard]] std::size_t steps_run() const noexcept { return step_count_; }
361
363 template <typename F>
364 void on_pre_step(F &&f)
365 {
366 pre_hook_ = std::forward<F>(f);
367 }
368
370 template <typename F>
371 void on_post_step(F &&f)
372 {
373 post_hook_ = std::forward<F>(f);
374 }
375
383 void step()
384 {
385 if (pre_hook_)
387
388 const ca_size_t n = cur_buf_.size();
389 for (ca_size_t node = 0; node < n; ++node)
390 {
391 const auto &neigh_ids = cur_buf_.neighbours(node);
392 const std::size_t deg = neigh_ids.size();
393 // Aleph::Array has no `resize(n)`; grow by appending value-
394 // initialised cells until capacity is reached. Size only
395 // grows monotonically (max_degree is bounded), so this is
396 // amortised O(1) per node.
397 while (nbuf_.size() < deg)
399
400 for (std::size_t k = 0; k < deg; ++k)
401 nbuf_[k] = cur_buf_.at_node(neigh_ids[k]);
402
403 const state_type *const view_ptr = deg > 0 ? &nbuf_[0] : nullptr;
405 const Cell_Context<1> ctx{step_count_, {static_cast<ca_index_t>(node)}};
406 nxt_buf_.set_node(node,
407 apply_rule(rule_, cur_buf_.at_node(node), view, ctx));
408 }
409
411 ++step_count_;
412
413 if (post_hook_)
415 }
416
420 void run(const std::size_t steps)
421 {
422 for (std::size_t i = 0; i < steps; ++i)
423 step();
424 }
425};
426
427// -----------------------------------------------------------------------
428// Graph builders for common test patterns.
429// -----------------------------------------------------------------------
430
446 std::size_t rows, std::size_t cols, bool periodic = false)
447{
449 auto idx = [cols](std::size_t i, std::size_t j) { return i * cols + j; };
450 for (std::size_t i = 0; i < rows; ++i)
451 for (std::size_t j = 0; j < cols; ++j)
452 {
453 auto &row = adj[idx(i, j)];
454 // North
455 if (i > 0)
456 row.append(idx(i - 1, j));
457 else if (periodic)
458 row.append(idx(rows - 1, j));
459 // South
460 if (i + 1 < rows)
461 row.append(idx(i + 1, j));
462 else if (periodic)
463 row.append(idx(0, j));
464 // West
465 if (j > 0)
466 row.append(idx(i, j - 1));
467 else if (periodic)
468 row.append(idx(i, cols - 1));
469 // East
470 if (j + 1 < cols)
471 row.append(idx(i, j + 1));
472 else if (periodic)
473 row.append(idx(i, 0));
474 }
475 return adj;
476}
477
489 std::size_t n, bool cycle = false)
490{
492 for (std::size_t i = 0; i < n; ++i)
493 {
494 if (i > 0)
495 adj[i].append(i - 1);
496 else if (cycle and n > 1)
497 adj[i].append(n - 1);
498 if (i + 1 < n)
499 adj[i].append(i + 1);
500 else if (cycle and n > 1)
501 adj[i].append(0);
502 }
503 return adj;
504}
505
506} // namespace CA
507} // namespace Aleph
508
509#endif // TPL_CA_GRAPH_AUTOMATON_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
size_t steps
Definition ca-c-api.h:126
size_t size_t int32_t value
Definition ca-c-api.h:116
size_t row
Definition ca-c-api.h:115
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.
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:138
constexpr size_t size() const noexcept
Return the number of elements stored in the stack.
Definition tpl_array.H:365
T & append(const T &data)
Append a copy of data
Definition tpl_array.H:250
void reserve(size_t cap)
Reserves cap cells into the array.
Definition tpl_array.H:320
Graph lattice: one cell per node + precomputed adjacency.
std::size_t degree(std::size_t n) const
Degree of node n.
Graph_Lattice()=default
Construct an empty graph lattice (0 nodes).
void set(const coord_type &c, const T &v)
ca_size_t size() const noexcept
T at_node(std::size_t n) const
Direct read at node id n.
const Array< std::size_t > & neighbours(std::size_t n) const
Neighbour ids of node n in the order supplied at construction.
ca_size_t size(std::size_t d) const
extents_type extents() const noexcept
Graph_Lattice(const Array< Array< std::size_t > > &adjacency, const T &init=T{})
Build a graph lattice from an adjacency list.
void set_node(std::size_t n, const T &v)
Direct write at node id n.
std::array< ca_size_t, 1 > extents_type
T at(const coord_type &c) const
static constexpr std::size_t dimension() noexcept
void swap(Graph_Lattice &other) noexcept
O(1) swap with another graph lattice of the same type.
static constexpr std::size_t rank
Rank-1 lattice: node id is the only axis.
std::size_t max_degree() const noexcept
Maximum degree across the graph (computed in O(N)).
T at_safe(const coord_type &c) const
Boundary-aware read.
void fill(const T &value)
Set every cell to value.
Array< Array< std::size_t > > adjacency_
Synchronous double-buffered engine for graph CAs.
void step()
Apply the rule to every node once and swap buffers.
Array< state_type > nbuf_
Aleph::Array reused across cells.
void on_post_step(F &&f)
Register a hook fired after every step().
void run(const std::size_t steps)
Run several synchronous steps.
Graph_Synchronous_Engine(Lattice initial, Rule r)
Build an engine on top of an existing graph lattice.
std::function< void(std::size_t, const Lattice &)> pre_hook_
const Lattice & frame() const noexcept
void on_pre_step(F &&f)
Register a hook fired before every step().
typename Lattice::state_type state_type
std::function< void(std::size_t, const Lattice &)> post_hook_
Lattice that adds boundary-aware access on top of a storage.
typename Storage::state_type state_type
void swap(Lattice &other) noexcept(noexcept(store_.swap(other.store_)))
O(1) swap.
ca_size_t size() const noexcept
constexpr size_t size() const noexcept
Returns the number of entries in the table.
Definition hashDry.H:619
A type usable as the value stored inside a cell.
Concept satisfied by graph rules.
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
@ R
Recovered (and immune).
std::span< const T > Neighbor_View
Read-only view over a contiguous range of neighbour values.
Definition ca-traits.H:90
std::ptrdiff_t ca_index_t
Signed coordinate component used by lattices and neighborhoods.
Definition ca-traits.H:60
std::array< ca_index_t, N > Coord_Vec
Default coordinate vector.
Definition ca-traits.H:69
Array< Array< std::size_t > > make_grid_graph_adjacency(std::size_t rows, std::size_t cols, bool periodic=false)
Build the adjacency of a 2D 4-neighbour grid graph.
Array< Array< std::size_t > > make_path_graph_adjacency(std::size_t n, bool cycle=false)
Build the adjacency of a path graph with n nodes.
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
State apply_rule(const Rule &r, const State &s, Neighbor_View< State > v, const Cell_Context< Rank > &ctx)
Invoke a rule, optionally forwarding the per-cell context.
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
std::decay_t< typename HeadC::Item_Type > T
Definition ah-zip.H:105
static std::atomic< bool > init
Definition hash-fct.C:54
STL namespace.
Per-cell context handed to rules that need to know "where" and "when" they are firing.
Definition ca-traits.H:106
FooMap m(5, fst_unit_pair_hash, snd_unit_pair_hash)
static int * k
gsl_rng * r
Dynamic array container with automatic resizing.
C++20 concepts for the Cellular Automata module.