A "Semiring" here is not a base class – it is a compile-time trait shape a type must satisfy to be usable as naive_evaluate_weighted<Semiring>'s template parameter: a nested Value type alias plus four static functions, zero(), one(), add(Value,Value) (⊕), mul(Value,Value) (⊗). There is no virtual interface anywhere in this file.
More...
#include <datalog_semiring.hpp>
A "Semiring" here is not a base class – it is a compile-time trait shape a type must satisfy to be usable as naive_evaluate_weighted<Semiring>'s template parameter: a nested Value type alias plus four static functions, zero(), one(), add(Value,Value) (⊕), mul(Value,Value) (⊗). There is no virtual interface anywhere in this file.
- Note
- Stage 3 design decision 3 (templating mechanism): a compile-time template parameter with a duck-typed trait shape, not a type-erased/virtual interface. Rationale, checked against this codebase's own precedent rather than assumed:
- The mission's own open-design-question text guessed this would be "closer to how
`DeviceBackend` is chosen" – recon of
include/pulsatrix/device_backend.hpp shows that guess was backwards: DeviceBackend is an abstract virtual interface, chosen at runtime per-Tensor (DeviceType::Cpu/Cuda/Hip), precisely because a single running program legitimately mixes backends (a Tensor moves between devices via Tensor::to()). A Datalog evaluation's semiring choice has no such runtime-mixing requirement – one naive_evaluate_weighted call always uses exactly one semiring, decided at the call site, never switched mid-evaluation.
- The actual matching precedent, found by checking
include/pulsatrix/selection.hpp (the evolutionary-algorithms family), is a duck-typed template parameter pattern: template <typename Genotype, typename FitnessT, typename RNG> free functions, no C++20 concept (this codebase targets C++17), no shared base class – the compiler enforces the shape at the instantiation call site via ordinary overload resolution/member lookup. Semiring here follows that exact precedent.
- Templates give zero runtime overhead (every
add/mul call is inlined at naive_evaluate_weighted<BooleanSemiring> / <RealSemiring<double>>'s own call site, no vtable indirection per fixpoint-round arithmetic operation, which matters since these run inside the evaluator's innermost loop), and they make "the boolean
semiring is the trivial case, not special-cased engine logic" (campaign doc's own phrasing) literally true: naive_evaluate_weighted's body contains no if constexpr/if (is_boolean) branch anywhere – BooleanSemiring and RealSemiring<double> are two ordinary instantiations of one generic algorithm.
The boolean semiring: ⊕ = OR, ⊗ = AND, zero = false, one = true.
- Note
- This is Mission 0's implicit boolean-only engine semantics, now made an explicit, swappable instantiation rather than hardcoded evaluator logic (the mission's own framing: "boolean semiring re-expressed as the trivial case").
◆ Value
◆ add()
| static constexpr Value pulsatrix::datalog::BooleanSemiring::add |
( |
Value |
a, |
|
|
Value |
b |
|
) |
| |
|
inlinestaticconstexpr |
◆ mul()
| static constexpr Value pulsatrix::datalog::BooleanSemiring::mul |
( |
Value |
a, |
|
|
Value |
b |
|
) |
| |
|
inlinestaticconstexpr |
◆ one()
| static constexpr Value pulsatrix::datalog::BooleanSemiring::one |
( |
| ) |
|
|
inlinestaticconstexpr |
◆ zero()
| static constexpr Value pulsatrix::datalog::BooleanSemiring::zero |
( |
| ) |
|
|
inlinestaticconstexpr |
The documentation for this struct was generated from the following file: