Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
ca-observer.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
75#ifndef CA_OBSERVER_H
76#define CA_OBSERVER_H
77
78#include <concepts>
79#include <cstddef>
80#include <cstdint>
81#include <functional>
82#include <optional>
83#include <tuple>
84#include <utility>
85
86#include <ah-errors.H>
87#include <tpl_array.H>
88
89#include <ca-metrics.H>
90#include <ca-traits.H>
91#include <tpl_ca_concepts.H>
92
93namespace Aleph {
94namespace CA {
95
107template <typename O, typename Lattice>
108concept ObserverLike = requires(O &o, std::size_t s, const Lattice &f) {
109 o.on_step_begin(s, f);
110 o.on_step_end(s, f);
111};
112
113// ---------------------------------------------------------------------------
114// Composite observer.
115// ---------------------------------------------------------------------------
116
127template <typename... Observers>
129{
130 std::tuple<Observers...> obs_;
131
132public:
148 explicit Composite_Observer(Observers... observers) : obs_(std::forward<Observers>(observers)...)
149 {}
150
156 template <std::size_t I>
157 [[nodiscard]] const auto &get() const noexcept
158 {
159 return std::get<I>(obs_);
160 }
161
167 template <std::size_t I>
169 {
170 return std::get<I>(obs_);
171 }
172
177 [[nodiscard]] static constexpr std::size_t size() noexcept
178 {
179 return sizeof...(Observers);
180 }
181
191 template <typename Lattice>
192 void on_step_begin(std::size_t step, const Lattice &frame)
193 {
194 std::apply([&](auto &...o)
195 {
196 (o.on_step_begin(step, frame), ...);
197 }, obs_);
198 }
199
209 template <typename Lattice>
210 void on_step_end(std::size_t step, const Lattice &frame)
211 {
212 std::apply([&](auto &...o)
213 {
214 (o.on_step_end(step, frame), ...);
215 }, obs_);
216 }
217};
218
233template <typename... Observers>
234[[nodiscard]] inline auto make_composite_observer(Observers &&...observers)
235{
236 return Composite_Observer<Observers...>(std::forward<Observers>(observers)...);
237}
238
239// ---------------------------------------------------------------------------
240// Engine plumbing.
241// ---------------------------------------------------------------------------
242
261template <typename Engine, typename Observer>
263{
264 using Lat = typename Engine::lattice_type;
265 engine.on_pre_step([&observer](std::size_t step, const Lat &frame)
266 {
267 observer.on_step_begin(step, frame);
268 });
269 engine.on_post_step([&observer](std::size_t step, const Lat &frame)
270 {
271 observer.on_step_end(step, frame);
272 });
273}
274
291template <typename Engine, typename Observer>
293{
294 engine.on_pre_step([&observer](std::size_t step, const auto &frame)
295 {
296 observer.on_step_begin(step, frame);
297 });
298 engine.on_post_step([&observer](std::size_t step, const auto &frame)
299 {
300 observer.on_step_end(step, frame);
301 });
302}
303
304// ---------------------------------------------------------------------------
305// Density observer.
306// ---------------------------------------------------------------------------
307
327template <typename State>
329{
333 bool seeded_ = false;
334
335public:
339 Density_Observer() = default;
340
345 explicit Density_Observer(State s) : tracked_state_(std::move(s)) {}
346
352 {
353 return counts_;
354 }
355
361 {
362 return totals_;
363 }
364
370 [[nodiscard]] double density_at(const std::size_t i) const
371 {
373 << "Density_Observer: index " << i << " out of range";
374 return totals_[i] == 0 ? 0.0 : static_cast<double>(counts_[i]) / static_cast<double>(totals_[i]);
375 }
376
381 [[nodiscard]] std::size_t size() const noexcept
382 {
383 return counts_.size();
384 }
385
393 {
394 counts_.clear();
395 totals_.clear();
396 seeded_ = false;
397 }
398
405 template <typename Lattice>
406 void on_step_begin(const std::size_t step, const Lattice &frame)
407 {
408 if (step == 0 and not seeded_)
409 {
411 totals_.append(total_cells(frame));
412 seeded_ = true;
413 }
414 }
415
422 template <typename Lattice>
423 void on_step_end(const std::size_t step, const Lattice &frame)
424 {
425 (void) step;
427 totals_.append(total_cells(frame));
428 }
429};
430
431// ---------------------------------------------------------------------------
432// Activity observer.
433// ---------------------------------------------------------------------------
434
457template <typename Lattice>
459{
460 std::optional<Lattice> prev_;
462
463public:
467 Activity_Observer() = default;
468
474 {
475 return activity_;
476 }
477
482 [[nodiscard]] std::size_t size() const noexcept
483 {
484 return activity_.size();
485 }
486
494 {
495 prev_.reset();
497 }
498
504 void on_step_begin(std::size_t step, const Lattice &frame)
505 {
506 (void) step;
507 prev_.emplace(frame);
508 }
509
515 void on_step_end(const std::size_t step, const Lattice &frame)
516 {
517 (void) step;
518 if (prev_.has_value())
520 else
521 activity_.append(0);
522 }
523};
524
525// ---------------------------------------------------------------------------
526// Entropy observer.
527// ---------------------------------------------------------------------------
528
547template <std::size_t MaxState>
549{
551 bool seeded_ = false;
552
553public:
557 Entropy_Observer() = default;
558
564 {
565 return entropy_;
566 }
567
572 [[nodiscard]] std::size_t size() const noexcept
573 {
574 return entropy_.size();
575 }
576
584 {
585 entropy_.clear();
586 seeded_ = false;
587 }
588
595 template <typename Lattice>
596 void on_step_begin(const std::size_t step, const Lattice &frame)
597 {
598 if (step == 0 and not seeded_)
599 {
601 seeded_ = true;
602 }
603 }
604
611 template <typename Lattice>
612 void on_step_end(const std::size_t step, const Lattice &frame)
613 {
614 (void) step;
616 }
617};
618
619// ---------------------------------------------------------------------------
620// Stationary detector.
621// ---------------------------------------------------------------------------
622
648{
649 std::size_t max_cycle_length_ = 0;
652 std::optional<std::size_t> cycle_length_;
653 std::optional<std::size_t> cycle_start_;
654
655 void push(const std::uint64_t h, const std::size_t step) noexcept
656 {
657 if (hashes_.size() >= max_cycle_length_ + 1)
658 {
659 // Drop the oldest entry (front of the buffer); remove_first
660 // closes the gap, preserving the window order.
663 }
665 steps_.append(step);
666 }
667
668public:
673
680
686 {
687 return cycle_length_.has_value();
688 }
689
694 [[nodiscard]] std::optional<std::size_t> cycle_length() const noexcept
695 {
696 return cycle_length_;
697 }
698
703 [[nodiscard]] std::optional<std::size_t> cycle_start() const noexcept
704 {
705 return cycle_start_;
706 }
707
713 {
714 return hashes_.size();
715 }
716
724 {
725 hashes_.clear();
726 steps_.clear();
727 cycle_length_.reset();
728 cycle_start_.reset();
729 }
730
737 template <typename Lattice>
738 void on_step_begin(const std::size_t step, const Lattice &frame)
739 {
740 if (max_cycle_length_ == 0)
742 if (step == 0 and hashes_.is_empty())
743 push(frame_hash(frame), step);
744 }
745
752 template <typename Lattice>
753 void on_step_end(std::size_t step, const Lattice &frame)
754 {
755 if (max_cycle_length_ == 0)
757 const std::uint64_t h = frame_hash(frame);
758
759 if (not cycle_length_.has_value())
760 for (std::size_t i = 0; i < hashes_.size(); ++i)
761 if (hashes_[i] == h)
762 {
763 cycle_start_ = steps_[i];
764 cycle_length_ = step - steps_[i];
765 break;
766 }
767 push(h, step);
768 }
769};
770
771// ---------------------------------------------------------------------------
772// Sampling observer.
773// ---------------------------------------------------------------------------
774
797template <typename Lattice>
799{
800 std::size_t period_ = 1;
803
804public:
808 Sampling_Observer() = default;
809
814 explicit Sampling_Observer(const std::size_t period) : period_(period) {}
815
821 {
822 return snapshots_;
823 }
824
833
838 [[nodiscard]] std::size_t size() const noexcept
839 {
840 return snapshots_.size();
841 }
842
850 {
851 snapshots_.clear();
853 }
854
860 void on_step_begin(const std::size_t step, const Lattice &frame)
861 {
862 if (period_ != 0 and step == 0 and snapshots_.is_empty())
863 {
864 snapshots_.append(frame);
866 }
867 }
868
874 void on_step_end(const std::size_t step, const Lattice &frame)
875 {
876 if (period_ == 0)
877 return;
878 if (step % period_ == 0)
879 {
880 snapshots_.append(frame);
881 step_indices_.append(step);
882 }
883 }
884};
885
886} // namespace CA
887} // namespace Aleph
888
889#endif // CA_OBSERVER_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
Free metric helpers for cellular automata frames.
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
void clear() noexcept
Empties the container.
Definition tpl_array.H:350
constexpr bool is_empty() const noexcept
Checks if the container is empty.
Definition tpl_array.H:359
T & append(const T &data)
Append a copy of data
Definition tpl_array.H:250
Records the number of cells that changed across each step.
void on_step_begin(std::size_t step, const Lattice &frame)
Capture the frame before a step.
std::optional< Lattice > prev_
Activity_Observer()=default
Construct an empty activity observer.
const Array< ca_size_t > & activity() const noexcept
Return the activity time series.
void on_step_end(const std::size_t step, const Lattice &frame)
Record changed cells after a step.
Array< ca_size_t > activity_
void reset() noexcept
Clear the previous frame and activity samples.
std::size_t size() const noexcept
Return the number of recorded steps.
Variadic adaptor that fan-outs notifications to many observers.
std::tuple< Observers... > obs_
static constexpr std::size_t size() noexcept
Return the number of child observers.
void on_step_end(std::size_t step, const Lattice &frame)
Forward a step-end event to every child observer.
Composite_Observer(Observers... observers)
Build the composite from child observers.
auto & get() noexcept
Return mutable access to a child observer.
const auto & get() const noexcept
Return read-only access to a child observer.
void on_step_begin(std::size_t step, const Lattice &frame)
Forward a step-begin event to every child observer.
Records the count of cells in tracked_state per step.
Density_Observer(State s)
Construct an observer tracking a specific state.
Array< ca_size_t > counts_
double density_at(const std::size_t i) const
Return density at a recorded sample.
const Array< ca_size_t > & counts() const noexcept
Return the count time series.
const Array< ca_size_t > & totals() const noexcept
Return the total-cell time series.
Density_Observer()=default
Construct an observer that tracks State{}.
Array< ca_size_t > totals_
void reset() noexcept
Clear all recorded samples.
std::size_t size() const noexcept
Return the number of recorded samples.
void on_step_begin(const std::size_t step, const Lattice &frame)
Record the initial frame before step zero.
void on_step_end(const std::size_t step, const Lattice &frame)
Record the frame after a completed step.
Records the Shannon entropy of the state distribution.
std::size_t size() const noexcept
Return the number of entropy samples.
void on_step_end(const std::size_t step, const Lattice &frame)
Record entropy after a completed step.
void on_step_begin(const std::size_t step, const Lattice &frame)
Record entropy of the initial frame.
Entropy_Observer()=default
Construct an empty entropy observer.
void reset() noexcept
Clear all entropy samples.
const Array< double > & entropy() const noexcept
Return the entropy time series.
Lattice that adds boundary-aware access on top of a storage.
Snapshots the lattice every period steps.
Sampling_Observer()=default
Construct an observer that snapshots every step.
std::size_t size() const noexcept
Return the number of stored snapshots.
const Array< std::size_t > & step_indices() const noexcept
Return step indices for collected snapshots.
Array< Lattice > snapshots_
Array< std::size_t > step_indices_
const Array< Lattice > & snapshots() const noexcept
Return collected snapshots.
void reset() noexcept
Clear all snapshots and step indices.
void on_step_begin(const std::size_t step, const Lattice &frame)
Capture the initial frame when sampling is enabled.
void on_step_end(const std::size_t step, const Lattice &frame)
Capture the completed frame at period boundaries.
Sampling_Observer(const std::size_t period)
Construct an observer with an explicit sampling period.
Detects fixed points and short cycles via frame hashing.
std::optional< std::size_t > cycle_start_
std::size_t buffered_hashes() const noexcept
Return the number of buffered hashes.
Stationary_Detector()=default
Construct a detector with the default 16-frame window.
void reset() noexcept
Clear buffered hashes and detected-cycle state.
std::optional< std::size_t > cycle_start() const noexcept
Return the cycle start step.
Array< std::size_t > steps_
Array< std::uint64_t > hashes_
bool cycle_detected() const noexcept
Return whether a cycle has been detected.
void push(const std::uint64_t h, const std::size_t step) noexcept
std::optional< std::size_t > cycle_length() const noexcept
Return the detected cycle length.
std::optional< std::size_t > cycle_length_
void on_step_end(std::size_t step, const Lattice &frame)
Check the completed frame against the recent hash window.
void on_step_begin(const std::size_t step, const Lattice &frame)
Seed the detector with the initial frame hash.
Stationary_Detector(std::size_t max_cycle_length)
Construct a detector with an explicit window size.
Concept satisfied by any cellular-automaton observer.
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
void attach_observer(Engine &engine, Observer &observer)
Plug an observer into an engine's pre/post-step hooks.
double shannon_entropy(const Lattice &lat, std::size_t max_state)
Shannon entropy in nats of the [0, max_state) distribution.
Definition ca-metrics.H:276
void attach_observer_graph(Engine &engine, Observer &observer)
Attach an observer to engines without a lattice_type alias.
ca_size_t total_cells(const Lattice &lat) noexcept
Definition ca-metrics.H:204
std::uint64_t frame_hash(const Lattice &lat)
Deterministic 64-bit FNV-1a hash of the lattice cells.
Definition ca-metrics.H:311
ca_size_t count_state(const Lattice &lat, const typename Lattice::state_type &s)
Definition ca-metrics.H:175
auto make_composite_observer(Observers &&...observers)
Build a Composite_Observer with deduced ownership semantics.
ca_size_t cell_diff_count(const Lattice &a, const Lattice &b)
Definition ca-metrics.H:386
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
STL namespace.
static mt19937 engine
Dynamic array container with automatic resizing.
C++20 concepts for the Cellular Automata module.