aGrUM 3.2.0
a C++ library for (probabilistic) graphical models
gum::KTBNInference< GUM_SCALAR > Class Template Reference

Exact inference on a gum::KTBN with observations and interventions, by the interface algorithm. More...

#include <agrum/KTBN/inference/KTBNInference.h>

Collaboration diagram for gum::KTBNInference< GUM_SCALAR >:
[legend]

Classes

struct  _Series_
 A cached marginal time-series for one base: owned variable descriptors paired with their marginals, indexed by slice (single entry for an atemporal base). Descriptors are owned so tensors get a stable per-slice name rather than the reused ring-slot name they came from. More...
struct  _Slot_
 One node of a window template: a base (index into baseNames) at a lag behind the window's current slice. lag == ATEMPORAL marks an atemporal base, which sits in every interface and never ages. More...
struct  _Window_
 A compiled window: the junction tree of \(H_t = I_{t-1} \cup V_t\), rooted at the clique holding \(I_t\), plus everything needed to fill and message-pass it. Built once; windows 0..k-2 are the initial ones, window k-1 is the repeating one, re-entered from slice k-1 on. More...

Public Types

using NodeKey = std::variant< std::string, std::pair< std::string, int > >
 A node designated either by its engine name ("X[2]", "C") or by its (base, slice) identity.

Public Member Functions

Constructors / Destructor
 KTBNInference (const KTBN< GUM_SCALAR > *ktbn)
 Constructor.
 ~KTBNInference ()=default
 Destructor.
 KTBNInference (const KTBNInference< GUM_SCALAR > &)=delete
 Copy is disabled (owns per-run variable descriptors and cached tensors).
KTBNInference< GUM_SCALAR > & operator= (const KTBNInference< GUM_SCALAR > &)=delete
 Constructor.
Interventions
void addIntervention (std::string_view base, int slice, const KTBNModality &value)
 Records a hard intervention \(do(base[slice]=value)\).
void addIntervention (std::string_view node_name, const KTBNModality &value)
 Same, using an engine name ("X[2]", "C", …).
void addIntervention (const std::vector< std::pair< NodeKey, KTBNModality > > &interventions)
 Records several interventions in one call.
void eraseIntervention (std::string_view base, int slice)
 Removes a recorded intervention (silent no-op if absent).
void eraseIntervention (std::string_view node_name)
 Same, using an engine name ("X[2]", "C", …).
void clearInterventions ()
 Removes all recorded interventions.
bool hasIntervention (std::string_view base, int slice) const
bool hasIntervention (std::string_view node_name) const
 Same, using an engine name ("X[2]", "C", …).
Observations
void addObservation (std::string_view base, int slice, const KTBNModality &value)
 Records a hard observation \(base[slice]=value\).
void addObservation (std::string_view node_name, const KTBNModality &value)
 Same, using an engine name ("X[2]", "C", …).
void addObservation (std::string_view base, int slice, const std::vector< GUM_SCALAR > &likelihood)
 Records a soft (likelihood) observation on \(base[slice]\).
void addObservation (std::string_view node_name, const std::vector< GUM_SCALAR > &likelihood)
 Same, using an engine name ("X[2]", "C", …).
void addObservations (const std::vector< std::pair< NodeKey, KTBNModality > > &observations)
 Records several observations in one call, all-or-nothing.
void eraseObservation (std::string_view base, int slice)
 Removes a recorded observation (silent no-op if absent).
void eraseObservation (std::string_view node_name)
 Same, using an engine name ("X[2]", "C", …).
void clearObservation ()
 Removes all recorded observations.
bool hasObservation (std::string_view base, int slice) const
bool hasObservation (std::string_view node_name) const
 Same, using an engine name ("X[2]", "C", …).
bool hasObservation () const
Targets
void addTarget (std::string_view base)
 Declares a target: a base variable whose marginals we want.
void eraseTarget (std::string_view base)
 Removes a target; when the last one is removed, default-all-targets mode is restored.
void clearTargets ()
 Removes all targets (restores default-all-targets mode).
bool isTarget (std::string_view base) const
bool isInTargetMode () const
Inference
void makeInference (Size nbTimeSlices)
 Runs the interface algorithm over nbTimeSlices slices ( \(0..nbTimeSlices-1\)) and caches, for every targeted base, its marginal at every slice.
const Tensor< GUM_SCALAR > & posterior (std::string_view base, int slice)
 Returns \(P(base[slice] \mid \text{obs}, do(\cdot))\).
const Tensor< GUM_SCALAR > & posterior (std::string_view node_name)
 Same, using an engine name ("X[2]", "C", …).
const std::vector< Tensor< GUM_SCALAR > > & posteriors (std::string_view base)
 The whole marginal time-series of a targeted base: tensors[t] is \(P(base[t] \mid \cdot)\) for \(t = 0..T-1\) (a single-element vector, holding the atemporal marginal, for an atemporal base).
GUM_SCALAR logObservationProbability ()
 \(\log P(\text{obs} \mid do(\cdot))\) for the last run: the likelihood of the observations under the (possibly mutilated) model. 0 when nothing is observed. Lazily (re)runs makeInference() if out of date.
GUM_SCALAR observationProbability ()
 \(P(\text{obs} \mid do(\cdot))\), i.e. exp of logObservationProbability(). Underflows to 0 on long horizons; prefer the log form.
Various
const KTBN< GUM_SCALAR > & ktbn () const
std::string toString () const
const JunctionTree & windowJunctionTree () const
 The junction tree of the repeating window – the one compiled from the k-slice template and re-entered at every step from slice \(k-1\) on. Introspection only.
Size interfaceSize () const
 Size of the forward interface of the repeating window: how many node occurrences have to cross each slice boundary. Introspection only.

Static Public Attributes

static constexpr int ATEMPORAL = KTBN< GUM_SCALAR >::ATEMPORAL
 Convenience alias for the atemporal-slice sentinel.

Private Member Functions

Size _psiKey_ (int t) const
 Cache slot for slice t: the initial slices keep their own, the repeating window contributes one per phase.
Runtime
const _Window_ & _windowAt_ (int t) const
 The window for absolute slice t: its own while t is inside the initial block, the repeating one (index k) from then on.
const std::unordered_map< NodeId, Tensor< GUM_SCALAR > > & _windowPotentials_ (const _Window_ &w, int t) const
 Clique potentials of the window at slice t: every requisite family's CPT (or, under an intervention, a point mass severing it from its causes), times every observation's likelihood.
void _applyTemporalObservations_ (const _Window_ &w, int t, std::unordered_map< NodeId, Tensor< GUM_SCALAR > > &psi) const
 Multiplies slice t's temporal observation likelihoods into an already-built base. Atemporal ones are skipped: the base holds them.
void _fillWindow_ (const _Window_ &w, int t, std::unordered_map< NodeId, Tensor< GUM_SCALAR > > &psi, bool withTemporalEvidence=true) const
void _propagate_ (const _Window_ &w, int t, const std::unordered_map< NodeId, Tensor< GUM_SCALAR > > &psi, const Tensor< GUM_SCALAR > *inPrev, const Tensor< GUM_SCALAR > *inNext, bool distribute, std::map< std::pair< NodeId, NodeId >, Tensor< GUM_SCALAR > > &msgs) const
 Shafer-Shenoy pass over a filled window. inPrev / inNext are the interface messages arriving at rootD / rootC (null when absent); division-free, so deterministic potentials need no special casing.
Tensor< GUM_SCALAR > _belief_ (const _Window_ &w, const std::unordered_map< NodeId, Tensor< GUM_SCALAR > > &psi, const std::map< std::pair< NodeId, NodeId >, Tensor< GUM_SCALAR > > &msgs, const Tensor< GUM_SCALAR > *inPrev, const Tensor< GUM_SCALAR > *inNext, NodeId c, NodeId skipNeighbour) const
 The belief of clique c: its potential times every message reaching it, interface messages included.
void _snapshot_ (const std::string &base, int slice, const Tensor< GUM_SCALAR > &marginal)
 Snapshots marginal onto an owned, stably-named descriptor and appends it to that base's series (index == slice for a temporal base). Positional fillWith, not name-matched: marginal's axis is a shared ring object, reused across residue-k slices, whose name generally isn't base[slice].
const _Series_ & _series_ (const std::string &base)
 The cached series of a targeted base, running makeInference() lazily (with the last horizon) if out of date. Shared by both accessors.

Private Attributes

const KTBN< GUM_SCALAR > * _ktbn_
 The k-DBN (referenced, not owned).
int _k_
 The order k, cached as int for slice arithmetic.
std::map< std::string, Idx > _interventions_
 Recorded interventions, keyed by engine name -> forced value.
std::map< std::string, std::vector< GUM_SCALAR > > _observations_
 Recorded observations, keyed by engine name -> likelihood vector (one-hot for a hard observation).
std::set< std::string > _targets_
 Recorded targets (base names). Empty <=> default-all-targets mode.
bool _targeted_mode_ {false}
 Whether at least one explicit target has been declared.
Size _horizon_ {0}
 Horizon (nbTimeSlices) of the last/next run; 0 <=> makeInference never run.
bool _done_ {false}
 Whether the cached posteriors are up to date.
GUM_SCALAR _logObservation_ {0}
 log P(observation | do) of the last run.
std::unordered_map< std::string, _Series_ > _posteriors_
 Cached marginal series of the last run, keyed by base name.
std::vector< std::string > _temporalSorted_
 Temporal / atemporal base names in a deterministic (sorted) order, cached once at construction (the KTBN's own sets are unordered).
std::vector< std::string > _atemporalSorted_
std::vector< std::string > _baseNames_
 All bases: temporal first (indices 0.._nbTemporal_-1), then atemporal. Window slots index into this.
std::size_t _nbTemporal_ {0}
std::unordered_map< std::string, int > _baseIdx_
 name -> index into baseNames
std::vector< int > _maxLag_
 maxLag[i]: largest lag at which the transition kernel still consumes temporal base i – how long an occurrence must stay in the interface, which is what makes |I| finite and the window template time-invariant.
std::vector< _Window_ > _windows_
 The compiled windows: index t for t <= k-2 (initial), index k-1 for the repeating window, reused by every slice from k-1 on. Built once.
std::vector< bool > _requisite_
 Bases actually folded by the current run: the targets, the observed nodes and all their ancestors. Rebuilt per makeInference() from the current target/observation sets; anything outside is barren and cannot move an answer.
std::vector< std::unordered_map< NodeId, Tensor< GUM_SCALAR > > > _psiCache_
 Memoized clique potentials for the slices that carry no temporal evidence, indexed by psiKey(t). Sized 2k: the k initial slices have their own windows and their own CPTs, the repeating one contributes k phases. O(k) whatever the horizon – unlike keeping one per slice, which would make smoothing grow linearly in T.
std::vector< bool > _psiCached_
std::unordered_set< int > _observationSlices_
 Slices carrying a temporal observation. Their potentials are the periodic ones times that slice's likelihood, so they are served by copying the cached base and multiplying the evidence in – cheaper than a rebuild, which would redo the unit fill and every CPT product.
std::unordered_set< int > _interventionSlices_
 Slices carrying a temporal intervention. These need a full rebuild: do(X=x) replaces the node's CPT, which the cached base has already multiplied in, so the base is unusable rather than merely incomplete. Atemporal evidence appears in neither set – it applies at every slice alike and so belongs to the periodic structure.
std::unordered_map< NodeId, Tensor< GUM_SCALAR > > _psiScratch_
 Potentials of an evidence-carrying slice, rebuilt on each visit.
std::map< std::pair< std::string, int >, Tensor< GUM_SCALAR > > _kernelCache_
 Memoized transition kernels, keyed by (process, t % k).

Structural helpers

bool _isTemporal_ (const std::string &base) const
bool _isAtemporal_ (const std::string &base) const
std::pair< std::string, int > _determineNode_ (const std::string &name) const
 Cache-aware classification of an engine name -> (base, slice): a name registered as atemporal (incl. bracket-shaped) maps to ATEMPORAL, every other name is parsed syntactically. Mirrors KTBN::determineNode.
void _validateNode_ (const std::string &base, int slice) const
 Validates that (base, slice) denotes a legal node (future slices ok).
const DiscreteVariable & _templateVar_ (const std::string &base, int slice) const
 A representative template variable of base for domain/cloning.
const DiscreteVariable * _varOfSlot_ (const _Slot_ &s, int t) const
 The variable a window slot stands for at absolute slice t: ring slot \((t-\text{lag}) \bmod k\) for a temporal base, the atemporal object otherwise. This is where "advance one slice" happens – a relabelling, not an allocation.
const Tensor< GUM_SCALAR > & _buildKernel_ (const std::string &p, int t) const
 Transition-kernel tensor of process p at slice t ( \(t \geq k\)): the template kernel remapped onto the k-DBN's own per-slice objects, reused via slice % k (no allocation).
std::vector< _Slot_ > _familySlots_ (int baseIdx, int t) const
 Parents of base at a window whose current slice is t, as slots (lag = t - parentSlice). Uses the initial CPT structure for \(t \leq k-2\), the transition kernel from \(k-1\) on – which is why the repeating window is compiled from slice k-1's families.
std::vector< _Slot_ > _interfaceAfter_ (int t) const
 The forward interface after slice t, as slots relative to t: every requisite occurrence at a slice \(\leq t\) still coupled to the future, plus every requisite atemporal base. From \(t=k-2\) on this is the steady \(\{(p,\delta): \delta < maxLag(p)\}\) – why one repeating window suffices.
int _lastConsumerSlice_ (int baseIdx, int s) const
 The last slice at which occurrence base[s] is still consumed (-1 if never), over both the initial families and the transition kernel.
void _buildWindows_ ()
 Compiles the k window junction trees, once, from the constructor: moralise each window's families, force \(I_{t-1}\) and \(I_t\) into cliques, triangulate, root at \(C_t\), and assign every family factor to a clique that covers it.
_Window_ _compileWindow_ (const std::vector< _Slot_ > &Iprev, const std::vector< _Slot_ > &Icur, int t, bool withAtemporalFamilies) const
 Compiles one window over the given slot set / interfaces.
void _markRequisite_ ()
 Marks the requisite bases of the current run (targets, observed bases and all their ancestors) into requisite.
static std::string _encode_ (const std::string &base, int slice)
 Encodes (base, slice) -> engine name (base[slice] or bare base).

Detailed Description

template<GUM_Numeric GUM_SCALAR>
class gum::KTBNInference< GUM_SCALAR >

Exact inference on a gum::KTBN with observations and interventions, by the interface algorithm.

Usage
ie.addObservation("Y", 3, 1); // observe Y[3]=1
ie.addIntervention("X", 2, 1); // do(X[2]=1)
ie.addTarget("X"); // want the whole P(X[t] | ...) series
ie.makeInference(10); // compute slices 0..9
const Tensor<double>& p3 = ie.posterior("X", 3);
const std::vector<Tensor<double>>& px = ie.posteriors("X");
double logpe = ie.logObservationProbability();
KTBNInference(const KTBN< GUM_SCALAR > *ktbn)
Constructor.
const KTBN< GUM_SCALAR > & ktbn() const
aGrUM's Tensor is a multi-dimensional array with tensor operators.
Definition tensor.h:85
Targets (the query)
A target is a base variable (temporal or atemporal) whose marginals you want. makeInference(T) computes, for every targeted temporal base, its marginal at every slice \(0..T-1\) (a single entry at ATEMPORAL for an atemporal base). With none declared, every base is a target (mirrors gum::MarginalTargetedInference's default-all-targets mode). Declaring targets restricts the roll to the requisite subnetwork – the targets, the observed nodes, and their ancestors; everything else is barren. posterior()/posteriors() throw for anything not a target.
Warning
The KTBN is referenced, not copied (aGrUM's rule); it must outlive the inference engine.

Definition at line 147 of file KTBNInference.h.

Member Typedef Documentation

◆ NodeKey

template<GUM_Numeric GUM_SCALAR>
using gum::KTBNInference< GUM_SCALAR >::NodeKey = std::variant< std::string, std::pair< std::string, int > >

A node designated either by its engine name ("X[2]", "C") or by its (base, slice) identity.

Definition at line 175 of file KTBNInference.h.

Constructor & Destructor Documentation

◆ KTBNInference() [1/2]

template<GUM_Numeric GUM_SCALAR>
gum::KTBNInference< GUM_SCALAR >::KTBNInference ( const KTBN< GUM_SCALAR > * ktbn)
explicit

Constructor.

Parameters
ktbnThe k-DBN to reason about (referenced, not copied).
Exceptions
InvalidArgumentif ktbn is null.

Definition at line 74 of file KTBNInference_tpl.h.

74 : _ktbn_(ktbn) {
75 if (ktbn == nullptr) GUM_ERROR(InvalidArgument, "KTBNInference: the k-DBN is null.")
76 _k_ = static_cast< int >(ktbn->k());
77
78 // a deterministic base order (the KTBN's own sets are unordered): temporal
79 // first, then atemporal, so a slot's base index alone says which it is.
84
88 for (std::size_t i = 0; i < _baseNames_.size(); ++i)
90
91 // compile windows for the default (all-bases) query; _markRequisite_()
92 // recompiles only if a later target/observation set changes the subset
95 }
Exact inference on a gum::KTBN with observations and interventions, by the interface algorithm.
std::vector< bool > _requisite_
Bases actually folded by the current run: the targets, the observed nodes and all their ancestors....
std::vector< std::string > _baseNames_
All bases: temporal first (indices 0.._nbTemporal_-1), then atemporal. Window slots index into this.
const KTBN< GUM_SCALAR > * _ktbn_
The k-DBN (referenced, not owned).
std::size_t _nbTemporal_
std::vector< std::string > _atemporalSorted_
void _buildWindows_()
Compiles the k window junction trees, once, from the constructor: moralise each window's families,...
int _k_
The order k, cached as int for slice arithmetic.
std::vector< std::string > _temporalSorted_
Temporal / atemporal base names in a deterministic (sorted) order, cached once at construction (the K...
std::unordered_map< std::string, int > _baseIdx_
name -> index into baseNames

References _atemporalSorted_, _baseIdx_, _baseNames_, _buildWindows_(), _k_, _ktbn_, _nbTemporal_, _requisite_, _temporalSorted_, GUM_ERROR, and ktbn().

Referenced by KTBNInference(), and operator=().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ~KTBNInference()

template<GUM_Numeric GUM_SCALAR>
gum::KTBNInference< GUM_SCALAR >::~KTBNInference ( )
default

Destructor.

◆ KTBNInference() [2/2]

template<GUM_Numeric GUM_SCALAR>
gum::KTBNInference< GUM_SCALAR >::KTBNInference ( const KTBNInference< GUM_SCALAR > & )
delete

Copy is disabled (owns per-run variable descriptors and cached tensors).

References KTBNInference().

Here is the call graph for this function:

Member Function Documentation

◆ _applyTemporalObservations_()

template<GUM_Numeric GUM_SCALAR>
void gum::KTBNInference< GUM_SCALAR >::_applyTemporalObservations_ ( const _Window_ & w,
int t,
std::unordered_map< NodeId, Tensor< GUM_SCALAR > > & psi ) const
private

Multiplies slice t's temporal observation likelihoods into an already-built base. Atemporal ones are skipped: the base holds them.

Definition at line 761 of file KTBNInference_tpl.h.

764 {
765 for (const auto& [c, bases]: w.factorsOf)
766 for (const int b: bases) {
767 if (b >= static_cast< int >(_nbTemporal_)) continue; // atemporal: already in the base
768 const auto ite = _observations_.find(_encode_(_baseNames_[b], t));
769 if (ite == _observations_.end()) continue;
771 ev << _ktbn_->variable(_baseNames_[b], t % _k_);
772 ev.fillWith(ite->second);
773 psi.at(c) *= ev;
774 }
775 }
static std::string _encode_(const std::string &base, int slice)
Encodes (base, slice) -> engine name (base[slice] or bare base).
std::map< std::string, std::vector< GUM_SCALAR > > _observations_
Recorded observations, keyed by engine name -> likelihood vector (one-hot for a hard observation).

References _baseNames_, _encode_(), _k_, _ktbn_, _nbTemporal_, _observations_, and gum::KTBNInference< GUM_SCALAR >::_Window_::factorsOf.

Referenced by _windowPotentials_().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _belief_()

template<GUM_Numeric GUM_SCALAR>
Tensor< GUM_SCALAR > gum::KTBNInference< GUM_SCALAR >::_belief_ ( const _Window_ & w,
const std::unordered_map< NodeId, Tensor< GUM_SCALAR > > & psi,
const std::map< std::pair< NodeId, NodeId >, Tensor< GUM_SCALAR > > & msgs,
const Tensor< GUM_SCALAR > * inPrev,
const Tensor< GUM_SCALAR > * inNext,
NodeId c,
NodeId skipNeighbour ) const
private

The belief of clique c: its potential times every message reaching it, interface messages included.

Definition at line 892 of file KTBNInference_tpl.h.

899 {
901 for (const NodeId n: w.jt.neighbours(c)) {
902 if (n == skip) continue;
903 const auto it = msgs.find({n, c});
904 if (it != msgs.end()) out *= it->second;
905 }
906 // the two virtual leaves: past hangs off D_t, future off C_t. Leaving one
907 // out is how Shafer-Shenoy avoids echoing a message back -- no division needed
908 if (inPrev != nullptr && c == w.rootD && skip != KTBN_SKIP_PREV) out *= *inPrev;
909 if (inNext != nullptr && c == w.rootC && skip != KTBN_SKIP_NEXT) out *= *inNext;
910 return out;
911 }

References gum::KTBNInference< GUM_SCALAR >::_Window_::jt, gum::EdgeGraphPart::neighbours(), gum::KTBNInference< GUM_SCALAR >::_Window_::rootC, and gum::KTBNInference< GUM_SCALAR >::_Window_::rootD.

Referenced by _propagate_(), and makeInference().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _buildKernel_()

template<GUM_Numeric GUM_SCALAR>
const Tensor< GUM_SCALAR > & gum::KTBNInference< GUM_SCALAR >::_buildKernel_ ( const std::string & p,
int t ) const
private

Transition-kernel tensor of process p at slice t ( \(t \geq k\)): the template kernel remapped onto the k-DBN's own per-slice objects, reused via slice % k (no allocation).

Memoized in kernelCache: the result depends on t only through t % k, so at most \(k\) tensors exist per process however long the horizon. Returns a reference into the cache – valid until the next makeInference(), which clears it.

Definition at line 725 of file KTBNInference_tpl.h.

726 {
727 // The result depends on t only through t % k -- for the child directly, and
728 // for each parent as (t - lag) % k, which is fixed once t % k is. So one
729 // tensor per (process, phase) serves every slice of that phase.
730 const std::pair< std::string, int > key{p, static_cast< int >(t % _k_)};
731 const auto hit = _kernelCache_.find(key);
732 if (hit != _kernelCache_.end()) return hit->second;
733
734 // Builds P(p[t] | its absolute-time parents) by rolling the template
735 // kernel P(p[k-1] | parents) forward onto slice t. The kernel's own axes
736 // use ABSOLUTE-slice identity (variable(base, slice % k), the KTBN's
737 // cycled per-process objects); srcNames uses the TEMPLATE's time-invariant
738 // names, only to pick which axis of cpt(p, k-1) to copy from -- fillWith's
739 // mapSrc maps by position, not name, so the two need not match.
742
743 kernel << _ktbn_->variable(p, t % _k_);
744 srcNames.push_back(_encode_(p, _k_ - 1));
745
746 for (const auto& [parBase, parSlice]: _ktbn_->parents(p, _k_ - 1)) {
747 if (parSlice == ATEMPORAL) {
748 kernel << _ktbn_->variable(parBase, ATEMPORAL);
749 srcNames.push_back(parBase);
750 } else {
751 const int lag = (_k_ - 1) - parSlice;
752 kernel << _ktbn_->variable(parBase, (t - lag) % _k_);
753 srcNames.push_back(_encode_(parBase, parSlice));
754 }
755 }
756 kernel.fillWith(_ktbn_->cpt(p, _k_ - 1), srcNames);
757 return _kernelCache_.emplace(key, std::move(kernel)).first->second;
758 }
std::map< std::pair< std::string, int >, Tensor< GUM_SCALAR > > _kernelCache_
Memoized transition kernels, keyed by (process, t % k).
static constexpr int ATEMPORAL
Convenience alias for the atemporal-slice sentinel.

References _encode_(), _k_, _kernelCache_, _ktbn_, and ATEMPORAL.

Referenced by _fillWindow_().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _buildWindows_()

template<GUM_Numeric GUM_SCALAR>
void gum::KTBNInference< GUM_SCALAR >::_buildWindows_ ( )
private

Compiles the k window junction trees, once, from the constructor: moralise each window's families, force \(I_{t-1}\) and \(I_t\) into cliques, triangulate, root at \(C_t\), and assign every family factor to a clique that covers it.

Definition at line 634 of file KTBNInference_tpl.h.

634 {
635 // how long an occurrence must survive: the deepest lag the requisite
636 // kernel still reads it at -- what bounds |I|
637 _maxLag_.assign(_nbTemporal_, 0);
638 for (int c = 0; c < static_cast< int >(_nbTemporal_); ++c) {
639 if (!_requisite_[c]) continue;
640 for (const auto& [pb, ps]: _ktbn_->parents(_baseNames_[c], _k_ - 1)) {
641 if (ps == ATEMPORAL) continue;
642 const int i = _baseIdx_.at(pb);
643 const int lag = (_k_ - 1) - ps;
644 if (lag >= 1 && lag > _maxLag_[i]) _maxLag_[i] = lag;
645 }
646 }
647
648 const auto shift = [](std::vector< _Slot_ > v) {
649 for (auto& s: v)
650 if (s.lag != ATEMPORAL) ++s.lag;
651 return v;
652 };
653
654 // windows 0..k-1 cover slices 0..k-1; window k is the repeating one, used
655 // from slice k on. For k>=2, k-1 and k are structurally identical, so the
656 // extra compile is free; for k==1 they must differ, since slice 0 alone
657 // carries the atemporal layer in.
658 _windows_.clear();
659 _windows_.reserve(_k_ + 1);
660 std::vector< _Slot_ > prev; // I_{t-1}, in slice-t coordinates
661 for (int t = 0; t <= _k_; ++t) {
663 _windows_.push_back(_compileWindow_(prev, cur, t, /*withAtemporalFamilies=*/t == 0));
665 }
666 }
_Window_ _compileWindow_(const std::vector< _Slot_ > &Iprev, const std::vector< _Slot_ > &Icur, int t, bool withAtemporalFamilies) const
Compiles one window over the given slot set / interfaces.
std::vector< int > _maxLag_
maxLag[i]: largest lag at which the transition kernel still consumes temporal base i – how long an oc...
std::vector< _Slot_ > _interfaceAfter_(int t) const
The forward interface after slice t, as slots relative to t: every requisite occurrence at a slice s...
std::vector< _Window_ > _windows_
The compiled windows: index t for t <= k-2 (initial), index k-1 for the repeating window,...

References _baseIdx_, _baseNames_, _compileWindow_(), _interfaceAfter_(), _k_, _ktbn_, _maxLag_, _nbTemporal_, _requisite_, _windows_, and ATEMPORAL.

Referenced by KTBNInference(), and _markRequisite_().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _compileWindow_()

template<GUM_Numeric GUM_SCALAR>
KTBNInference< GUM_SCALAR >::_Window_ gum::KTBNInference< GUM_SCALAR >::_compileWindow_ ( const std::vector< _Slot_ > & Iprev,
const std::vector< _Slot_ > & Icur,
int t,
bool withAtemporalFamilies ) const
private

Compiles one window over the given slot set / interfaces.

Definition at line 458 of file KTBNInference_tpl.h.

461 {
462 _Window_ w;
463 w.Iprev = Iprev;
464 w.Icur = Icur;
465
466 // ---- node set: H_t = I_{t-1} U V_t (plus the atemporal layer at t == 0) --
470
471 const auto ensure = [&](const _Slot_& s) -> NodeId {
472 const auto key = std::make_pair(s.base, s.lag);
473 const auto it = id.find(key);
474 if (it != id.end()) return it->second;
475 const NodeId nid = graph.addNode();
476 id[key] = nid;
477 if (static_cast< std::size_t >(nid) >= w.slotOfNode.size()) w.slotOfNode.resize(nid + 1);
478 w.slotOfNode[nid] = s;
479 domSizes.insert(nid,
480 (s.lag == ATEMPORAL)
481 ? _ktbn_->variable(_baseNames_[s.base], ATEMPORAL).domainSize()
482 : _ktbn_->variable(_baseNames_[s.base], _k_ - 1).domainSize());
483 return nid;
484 };
485
486 for (const auto& s: Iprev)
487 ensure(s);
488 for (int i = 0; i < static_cast< int >(_nbTemporal_); ++i)
489 if (_requisite_[i]) ensure({i, 0});
491 for (int i = static_cast< int >(_nbTemporal_); i < static_cast< int >(_baseNames_.size());
492 ++i)
493 if (_requisite_[i]) ensure({i, ATEMPORAL});
494 for (const auto& s: Icur)
495 ensure(s);
496
497 if (graph.size() == 0) return w; // nothing requisite: an empty window
498
499 // ---- moralise: every family becomes a clique ----------------------------
501 const auto addFamily = [&](int b) {
503 for (const auto& s: _familySlots_(b, t))
504 scope.push_back(ensure(s));
505 for (std::size_t a = 0; a < scope.size(); ++a)
506 for (std::size_t c = a + 1; c < scope.size(); ++c)
507 if (!graph.existsEdge(scope[a], scope[c])) graph.addEdge(scope[a], scope[c]);
508 families.emplace_back(b, std::move(scope));
509 };
510 for (int i = 0; i < static_cast< int >(_nbTemporal_); ++i)
511 if (_requisite_[i]) addFamily(i);
513 for (int i = static_cast< int >(_nbTemporal_); i < static_cast< int >(_baseNames_.size());
514 ++i)
515 if (_requisite_[i]) addFamily(i);
516
517 // ---- force each interface to be a clique --------------------------------
518 // Murphy's constraint: one clique must hold the whole interface, so the
519 // message crossing the slice boundary is a single potential.
520 const auto makeClique = [&](const std::vector< _Slot_ >& slots) {
522 for (const auto& s: slots)
523 ids.push_back(ensure(s));
524 for (std::size_t a = 0; a < ids.size(); ++a)
525 for (std::size_t c = a + 1; c < ids.size(); ++c)
526 if (!graph.existsEdge(ids[a], ids[c])) graph.addEdge(ids[a], ids[c]);
527 };
530
531 // ---- triangulate once; this tree is reused for every slice and horizon --
533 w.jt = tri.junctionTree();
534
535 const auto covers = [&](NodeId c, const std::vector< _Slot_ >& slots) {
536 const NodeSet& cl = w.jt.clique(c);
537 for (const auto& s: slots)
538 if (!cl.contains(id.at({s.base, s.lag}))) return false;
539 return true;
540 };
541
542 bool foundC = false, foundD = false;
543 for (const NodeId c: w.jt.nodes()) {
544 if (!foundC && covers(c, Icur)) {
545 w.rootC = c;
546 foundC = true;
547 }
548 if (!foundD && covers(c, Iprev)) {
549 w.rootD = c;
550 foundD = true;
551 }
552 }
553 if (!foundC || !foundD)
554 GUM_ERROR(FatalError, "KTBNInference: the window interfaces did not end up in a clique.")
555
556 // ---- assign every family factor to one clique that covers it ------------
557 for (const auto& [b, scope]: families) {
558 bool placed = false;
559 for (const NodeId c: w.jt.nodes()) {
560 const NodeSet& cl = w.jt.clique(c);
561 bool ok = true;
562 for (const NodeId n: scope)
563 if (!cl.contains(n)) {
564 ok = false;
565 break;
566 }
567 if (ok) {
568 w.factorsOf[c].push_back(b);
569 placed = true;
570 break;
571 }
572 }
573 if (!placed)
574 GUM_ERROR(FatalError, "KTBNInference: family of '" << _baseNames_[b] << "' fits no clique.")
575 }
576
577 // ---- where to read each base's own marginal (and place its observation) ----
578 for (const auto& [key, nid]: id) {
579 const int base = key.first;
580 for (const NodeId c: w.jt.nodes())
581 if (w.jt.clique(c).contains(nid)) {
582 if (key.second == 0 || key.second == ATEMPORAL) w.selfClique[base] = c;
583 break;
584 }
585 }
586
587 // ---- stitch a junction FOREST into a junction tree ----------------------
588 // Barren processes and unused atemporal variables sit in their own
589 // components. Linking each to C_t with an EMPTY separator makes one tree
590 // without touching any marginal -- the crossing message is just a scalar.
591 {
593 int nc = 0;
594 for (const NodeId s: w.jt.nodes()) {
595 if (comp.count(s) != 0) continue;
597 comp[s] = nc;
598 for (std::size_t i = 0; i < q.size(); ++i)
599 for (const NodeId nb: w.jt.neighbours(q[i]))
600 if (comp.count(nb) == 0) {
601 comp[nb] = nc;
602 q.push_back(nb);
603 }
604 ++nc;
605 }
606 if (nc > 1) {
608 for (const NodeId n: w.jt.nodes())
609 if (linked.insert(comp.at(n)).second) w.jt.addEdge(w.rootC, n);
610 }
611 }
612
613 // ---- root the tree at C_t and record a BFS order ------------------------
615 w.bfs.push_back(w.rootC);
616 seen.insert(w.rootC);
617 w.parentOf[w.rootC] = w.rootC;
618 for (std::size_t qi = 0; qi < w.bfs.size(); ++qi) {
619 const NodeId cur = w.bfs[qi];
620 for (const NodeId nb: w.jt.neighbours(cur))
621 if (seen.insert(nb).second) {
622 w.parentOf[nb] = cur;
623 w.bfs.push_back(nb);
624 }
625 }
626 // the forced interface cliques rule out a disconnected forest; assert it anyway
627 if (w.bfs.size() != w.jt.size())
628 GUM_ERROR(FatalError, "KTBNInference: the window junction tree is not connected.")
629
630 return w;
631 }
std::vector< _Slot_ > _familySlots_(int baseIdx, int t) const
Parents of base at a window whose current slice is t, as slots (lag = t - parentSlice)....
One node of a window template: a base (index into baseNames) at a lag behind the window's current sli...
A compiled window: the junction tree of , rooted at the clique holding , plus everything needed to fi...

References _baseNames_, _familySlots_(), _k_, _ktbn_, _nbTemporal_, _requisite_, gum::CliqueGraph::addEdge(), ATEMPORAL, gum::KTBNInference< GUM_SCALAR >::_Window_::bfs, gum::CliqueGraph::clique(), gum::Set< Key >::contains(), gum::KTBNInference< GUM_SCALAR >::_Window_::factorsOf, GUM_ERROR, gum::KTBNInference< GUM_SCALAR >::_Window_::Icur, gum::HashTable< Key, Val >::insert(), gum::KTBNInference< GUM_SCALAR >::_Window_::Iprev, gum::KTBNInference< GUM_SCALAR >::_Window_::jt, gum::StaticTriangulation::junctionTree(), gum::EdgeGraphPart::neighbours(), gum::NodeGraphPart::nodes(), gum::KTBNInference< GUM_SCALAR >::_Window_::parentOf, gum::KTBNInference< GUM_SCALAR >::_Window_::rootC, gum::KTBNInference< GUM_SCALAR >::_Window_::rootD, gum::KTBNInference< GUM_SCALAR >::_Window_::selfClique, gum::NodeGraphPart::size(), and gum::KTBNInference< GUM_SCALAR >::_Window_::slotOfNode.

Referenced by _buildWindows_().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _determineNode_()

template<GUM_Numeric GUM_SCALAR>
std::pair< std::string, int > gum::KTBNInference< GUM_SCALAR >::_determineNode_ ( const std::string & name) const
private

Cache-aware classification of an engine name -> (base, slice): a name registered as atemporal (incl. bracket-shaped) maps to ATEMPORAL, every other name is parsed syntactically. Mirrors KTBN::determineNode.

Definition at line 109 of file KTBNInference_tpl.h.

109 {
110 // a name registered as atemporal is atemporal, even if bracket-shaped
111 if (_ktbn_->atemporalVarNames().find(name) != _ktbn_->atemporalVarNames().end())
112 return {name, ATEMPORAL};
113
114 // only a well-formed "base[digits]" suffix is temporal (mirrors
115 // KTBN::_decodeName_); anything else falls through to atemporal
116 const auto lb = name.rfind('[');
117 if (lb == std::string::npos || name.back() != ']') return {name, ATEMPORAL};
118
119 const std::string inner = name.substr(lb + 1, name.size() - lb - 2);
120 if (inner.empty()) return {name, ATEMPORAL};
121 for (const char c: inner)
122 if (std::isdigit(static_cast< unsigned char >(c)) == 0) return {name, ATEMPORAL};
123
124 int slice{};
125 try {
127 } catch (const std::out_of_range&) {
129 "Node name '" << name << "' has a slice index too large to represent as int.")
130 }
131 return {name.substr(0, lb), slice};
132 }

References _ktbn_, ATEMPORAL, and GUM_ERROR.

Referenced by _markRequisite_(), addIntervention(), addIntervention(), addObservation(), addObservation(), addObservations(), eraseIntervention(), eraseObservation(), hasIntervention(), hasObservation(), makeInference(), and posterior().

Here is the caller graph for this function:

◆ _encode_()

template<GUM_Numeric GUM_SCALAR>
std::string gum::KTBNInference< GUM_SCALAR >::_encode_ ( const std::string & base,
int slice )
staticprivate

Encodes (base, slice) -> engine name (base[slice] or bare base).

Definition at line 102 of file KTBNInference_tpl.h.

102 {
103 if (slice == ATEMPORAL) return base;
104 return base + "[" + std::to_string(slice) + "]";
105 }

References ATEMPORAL.

Referenced by _applyTemporalObservations_(), _buildKernel_(), _fillWindow_(), _snapshot_(), addIntervention(), addIntervention(), addObservation(), addObservation(), addObservations(), eraseIntervention(), eraseObservation(), hasIntervention(), and hasObservation().

Here is the caller graph for this function:

◆ _familySlots_()

template<GUM_Numeric GUM_SCALAR>
std::vector< typename KTBNInference< GUM_SCALAR >::_Slot_ > gum::KTBNInference< GUM_SCALAR >::_familySlots_ ( int baseIdx,
int t ) const
private

Parents of base at a window whose current slice is t, as slots (lag = t - parentSlice). Uses the initial CPT structure for \(t \leq k-2\), the transition kernel from \(k-1\) on – which is why the repeating window is compiled from slice k-1's families.

Definition at line 387 of file KTBNInference_tpl.h.

387 {
390
391 if (baseIdx >= static_cast< int >(_nbTemporal_)) {
392 out.push_back({baseIdx, ATEMPORAL});
393 for (const auto& [pb, ps]: _ktbn_->parents(b, ATEMPORAL)) {
394 (void)ps;
395 out.push_back({_baseIdx_.at(pb), ATEMPORAL});
396 }
397 return out;
398 }
399
400 // initial CPTs while inside the initial block, else the (time-invariant)
401 // transition kernel -- why one window template serves every later t
402 const int structSlice = (t <= _k_ - 1) ? t : _k_ - 1;
403 out.push_back({baseIdx, 0});
404 for (const auto& [pb, ps]: _ktbn_->parents(b, structSlice)) {
405 if (ps == ATEMPORAL) out.push_back({_baseIdx_.at(pb), ATEMPORAL});
406 else out.push_back({_baseIdx_.at(pb), structSlice - ps});
407 }
408 return out;
409 }

References _baseIdx_, _baseNames_, _k_, _ktbn_, _nbTemporal_, and ATEMPORAL.

Referenced by _compileWindow_().

Here is the caller graph for this function:

◆ _fillWindow_()

template<GUM_Numeric GUM_SCALAR>
void gum::KTBNInference< GUM_SCALAR >::_fillWindow_ ( const _Window_ & w,
int t,
std::unordered_map< NodeId, Tensor< GUM_SCALAR > > & psi,
bool withTemporalEvidence = true ) const
private
Parameters
withTemporalEvidencefalse builds the base: the periodic part only, i.e. CPTs/kernels plus atemporal evidence, with slice t's own observations and interventions left out. That is what psiCache stores.

Definition at line 813 of file KTBNInference_tpl.h.

817 {
818 psi.clear();
819
820 // every clique starts at the unit potential: even one that receives no
821 // factor must contribute its variables' free mass to a summed-out message
822 for (const NodeId c: w.jt.nodes()) {
824 for (const NodeId n: w.jt.clique(c))
825 p << *_varOfSlot_(w.slotOfNode[n], t);
826 p.fillWith(GUM_SCALAR(1));
827 psi.emplace(c, std::move(p));
828 }
829
830 const auto applyBase = [&](int b, NodeId owner) {
831 const std::string& base = _baseNames_[b];
832 const bool atemp = b >= static_cast< int >(_nbTemporal_);
834 const DiscreteVariable& var
835 = atemp ? _ktbn_->variable(base, ATEMPORAL) : _ktbn_->variable(base, t % _k_);
836
837 // do(base[t]=v): the point mass REPLACES the CPT, severing the node
838 // from its own causes -- an observation never does this
839 // In a base build the slice's own evidence is left out, so a temporal
840 // node keeps its CPT and no likelihood is applied; atemporal evidence is
841 // slice-independent and stays, being part of the periodic structure.
843
844 const auto itv = sliceEvidence ? _interventions_.find(name) : _interventions_.end();
845 if (itv != _interventions_.end()) {
847 } else if (atemp) {
848 psi.at(owner) *= _ktbn_->cpt(base, ATEMPORAL);
849 } else if (t <= _k_ - 1) {
850 psi.at(owner) *= _ktbn_->cpt(base, t); // initial CPT: already on ring slots
851 } else {
852 psi.at(owner) *= _buildKernel_(base, t);
853 }
854
855 // base[t] observed: pure conditioning, multiplied in ON TOP of the CPT.
856 const auto ite = sliceEvidence ? _observations_.find(name) : _observations_.end();
857 if (ite != _observations_.end()) {
859 ev << var;
860 ev.fillWith(ite->second);
861 psi.at(owner) *= ev;
862 }
863 };
864
865 for (const auto& [c, bases]: w.factorsOf)
866 for (const int b: bases)
867 applyBase(b, c);
868 }
std::map< std::string, Idx > _interventions_
Recorded interventions, keyed by engine name -> forced value.
const Tensor< GUM_SCALAR > & _buildKernel_(const std::string &p, int t) const
Transition-kernel tensor of process p at slice t ( ): the template kernel remapped onto the k-DBN's o...
const DiscreteVariable * _varOfSlot_(const _Slot_ &s, int t) const
The variable a window slot stands for at absolute slice t: ring slot for a temporal base,...
static Tensor< GUM_SCALAR > deterministicTensor(const DiscreteVariable &var, Idx value)

References _baseNames_, _buildKernel_(), _encode_(), _interventions_, _k_, _ktbn_, _nbTemporal_, _observations_, _varOfSlot_(), ATEMPORAL, gum::CliqueGraph::clique(), gum::Tensor< GUM_SCALAR >::deterministicTensor(), gum::KTBNInference< GUM_SCALAR >::_Window_::factorsOf, gum::KTBNInference< GUM_SCALAR >::_Window_::jt, gum::NodeGraphPart::nodes(), and gum::KTBNInference< GUM_SCALAR >::_Window_::slotOfNode.

Referenced by _windowPotentials_().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _interfaceAfter_()

template<GUM_Numeric GUM_SCALAR>
std::vector< typename KTBNInference< GUM_SCALAR >::_Slot_ > gum::KTBNInference< GUM_SCALAR >::_interfaceAfter_ ( int t) const
private

The forward interface after slice t, as slots relative to t: every requisite occurrence at a slice \(\leq t\) still coupled to the future, plus every requisite atemporal base. From \(t=k-2\) on this is the steady \(\{(p,\delta): \delta < maxLag(p)\}\) – why one repeating window suffices.

Definition at line 442 of file KTBNInference_tpl.h.

442 {
444 for (int i = 0; i < static_cast< int >(_nbTemporal_); ++i) {
445 if (!_requisite_[i]) continue;
446 for (int s = t; s >= 0 && t - s <= _k_ - 1; --s)
447 if (_lastConsumerSlice_(i, s) > t) out.push_back({i, t - s});
448 }
449 // an atemporal variable is a parent at every slice, so it never leaves
450 // the interface -- the one thing that couples the whole horizon
451 for (int i = static_cast< int >(_nbTemporal_); i < static_cast< int >(_baseNames_.size()); ++i)
452 if (_requisite_[i]) out.push_back({i, ATEMPORAL});
453 return out;
454 }
int _lastConsumerSlice_(int baseIdx, int s) const
The last slice at which occurrence base[s] is still consumed (-1 if never), over both the initial fam...

References _baseNames_, _k_, _lastConsumerSlice_(), _nbTemporal_, _requisite_, and ATEMPORAL.

Referenced by _buildWindows_().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _isAtemporal_()

template<GUM_Numeric GUM_SCALAR>
INLINE bool gum::KTBNInference< GUM_SCALAR >::_isAtemporal_ ( const std::string & base) const
private
Returns
whether base is an atemporal variable of the k-DBN.

Definition at line 881 of file KTBNInference_tpl.h.

881 {
882 return _ktbn_->atemporalVarNames().find(base) != _ktbn_->atemporalVarNames().end();
883 }

References _ktbn_.

Referenced by _validateNode_(), addTarget(), isTarget(), and posterior().

Here is the caller graph for this function:

◆ _isTemporal_()

template<GUM_Numeric GUM_SCALAR>
INLINE bool gum::KTBNInference< GUM_SCALAR >::_isTemporal_ ( const std::string & base) const
private
Returns
whether base is a temporal process of the k-DBN.

Definition at line 876 of file KTBNInference_tpl.h.

876 {
877 return _ktbn_->temporalVarNames().find(base) != _ktbn_->temporalVarNames().end();
878 }

References _ktbn_.

Referenced by _validateNode_(), addTarget(), and isTarget().

Here is the caller graph for this function:

◆ _lastConsumerSlice_()

template<GUM_Numeric GUM_SCALAR>
int gum::KTBNInference< GUM_SCALAR >::_lastConsumerSlice_ ( int baseIdx,
int s ) const
private

The last slice at which occurrence base[s] is still consumed (-1 if never), over both the initial families and the transition kernel.

Definition at line 412 of file KTBNInference_tpl.h.

412 {
413 int last = -1;
414
415 // consumers inside the initial block (their parent sets are per-slice)
416 for (int tc = 0; tc <= _k_ - 2; ++tc) {
417 if (tc <= last) continue;
418 for (int c = 0; c < static_cast< int >(_nbTemporal_); ++c) {
419 if (!_requisite_[c]) continue;
420 for (const auto& [pb, ps]: _ktbn_->parents(_baseNames_[c], tc)) {
421 if (ps == ATEMPORAL) continue;
422 if (ps == s && _baseIdx_.at(pb) == baseIdx) {
423 last = tc;
424 break;
425 }
426 }
427 if (tc == last) break;
428 }
429 }
430
431 // repeating block: the kernel reaches back _maxLag_ slices, so the last
432 // consumer sits at s + maxLag
433 if (_maxLag_[baseIdx] >= 1) {
434 const int tc = s + _maxLag_[baseIdx];
435 if (tc >= _k_ - 1 && tc > last) last = tc;
436 }
437 return last;
438 }

References _baseIdx_, _baseNames_, _k_, _ktbn_, _maxLag_, _nbTemporal_, _requisite_, and ATEMPORAL.

Referenced by _interfaceAfter_().

Here is the caller graph for this function:

◆ _markRequisite_()

template<GUM_Numeric GUM_SCALAR>
void gum::KTBNInference< GUM_SCALAR >::_markRequisite_ ( )
private

Marks the requisite bases of the current run (targets, observed bases and all their ancestors) into requisite.

Definition at line 669 of file KTBNInference_tpl.h.

669 {
670 std::vector< bool > req(_baseNames_.size(), false);
672
673 const auto push = [&](const std::string& b) {
674 const auto it = _baseIdx_.find(b);
675 if (it == _baseIdx_.end()) return;
676 if (!req[it->second]) {
677 req[it->second] = true;
678 stack.push_back(it->second);
679 }
680 };
681
682 if (!_targeted_mode_) {
683 for (const auto& b: _baseNames_)
684 push(b);
685 } else {
686 for (const auto& b: _targets_)
687 push(b);
688 }
689 // an observed node is requisite even if barren: its likelihood is what
690 // revises everything upstream of it.
691 for (const auto& [name, like]: _observations_) {
692 (void)like;
694 }
695
696 while (!stack.empty()) {
697 const int i = stack.back();
698 stack.pop_back();
699 const std::string& b = _baseNames_[i];
700 if (i < static_cast< int >(_nbTemporal_)) {
701 for (int t = 0; t <= _k_ - 1; ++t)
702 for (const auto& [pb, ps]: _ktbn_->parents(b, t)) {
703 (void)ps;
704 push(pb);
705 }
706 } else {
707 for (const auto& [pb, ps]: _ktbn_->parents(b, ATEMPORAL)) {
708 (void)ps;
709 push(pb);
710 }
711 }
712 }
713
714 if (req != _requisite_) {
717 }
718 }
std::set< std::string > _targets_
Recorded targets (base names). Empty <=> default-all-targets mode.
bool _targeted_mode_
Whether at least one explicit target has been declared.
std::pair< std::string, int > _determineNode_(const std::string &name) const
Cache-aware classification of an engine name -> (base, slice): a name registered as atemporal (incl....

References _baseIdx_, _baseNames_, _buildWindows_(), _determineNode_(), _k_, _ktbn_, _nbTemporal_, _observations_, _requisite_, _targeted_mode_, _targets_, and ATEMPORAL.

Referenced by makeInference().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _propagate_()

template<GUM_Numeric GUM_SCALAR>
void gum::KTBNInference< GUM_SCALAR >::_propagate_ ( const _Window_ & w,
int t,
const std::unordered_map< NodeId, Tensor< GUM_SCALAR > > & psi,
const Tensor< GUM_SCALAR > * inPrev,
const Tensor< GUM_SCALAR > * inNext,
bool distribute,
std::map< std::pair< NodeId, NodeId >, Tensor< GUM_SCALAR > > & msgs ) const
private

Shafer-Shenoy pass over a filled window. inPrev / inNext are the interface messages arriving at rootD / rootC (null when absent); division-free, so deterministic potentials need no special casing.

Parameters
msgsout: (from,to) clique message. Collect-only when distribute is false – enough for the forward message alone.

Definition at line 914 of file KTBNInference_tpl.h.

921 {
922 msgs.clear();
923 if (w.bfs.empty()) return;
924
925 const auto sepVars = [&](NodeId a, NodeId b) {
927 for (const NodeId n: w.jt.separator(a, b))
928 keep.insert(_varOfSlot_(w.slotOfNode[n], t));
929 return keep;
930 };
931
932 // collect: reverse BFS visits every child before its parent
933 for (auto it = w.bfs.rbegin(); it != w.bfs.rend(); ++it) {
934 const NodeId j = *it;
935 if (j == w.rootC) continue;
936 const NodeId p = w.parentOf.at(j);
937 msgs[{j, p}] = _belief_(w, psi, msgs, inPrev, inNext, j, p).sumIn(sepVars(j, p));
938 }
939 if (!distribute) return;
940
941 // distribute: BFS visits every parent before its children
942 for (const NodeId j: w.bfs)
943 for (const NodeId i: w.jt.neighbours(j)) {
944 if (i == j || w.parentOf.at(i) != j) continue; // children only
945 msgs[{j, i}] = _belief_(w, psi, msgs, inPrev, inNext, j, i).sumIn(sepVars(j, i));
946 }
947 }
Tensor< GUM_SCALAR > _belief_(const _Window_ &w, const std::unordered_map< NodeId, Tensor< GUM_SCALAR > > &psi, const std::map< std::pair< NodeId, NodeId >, Tensor< GUM_SCALAR > > &msgs, const Tensor< GUM_SCALAR > *inPrev, const Tensor< GUM_SCALAR > *inNext, NodeId c, NodeId skipNeighbour) const
The belief of clique c: its potential times every message reaching it, interface messages included.

References _belief_(), _varOfSlot_(), gum::KTBNInference< GUM_SCALAR >::_Window_::bfs, gum::Set< Key >::insert(), gum::KTBNInference< GUM_SCALAR >::_Window_::jt, gum::EdgeGraphPart::neighbours(), gum::KTBNInference< GUM_SCALAR >::_Window_::parentOf, gum::KTBNInference< GUM_SCALAR >::_Window_::rootC, gum::CliqueGraph::separator(), and gum::KTBNInference< GUM_SCALAR >::_Window_::slotOfNode.

Referenced by makeInference().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _psiKey_()

template<GUM_Numeric GUM_SCALAR>
INLINE Size gum::KTBNInference< GUM_SCALAR >::_psiKey_ ( int t) const
private

Cache slot for slice t: the initial slices keep their own, the repeating window contributes one per phase.

Definition at line 778 of file KTBNInference_tpl.h.

778 {
779 // t < k: the initial windows, each its own. t >= k: the repeating window,
780 // one slot per phase. Keying on t % k alone would collide slice 2 with
781 // slice 5 at k=3 -- same phase, different window and different CPTs.
782 return (t < static_cast< int >(_k_)) ? static_cast< Size >(t)
783 : _k_ + static_cast< Size >(t % static_cast< int >(_k_));
784 }

References _k_.

Referenced by _windowPotentials_().

Here is the caller graph for this function:

◆ _series_()

template<GUM_Numeric GUM_SCALAR>
const KTBNInference< GUM_SCALAR >::_Series_ & gum::KTBNInference< GUM_SCALAR >::_series_ ( const std::string & base)
private

The cached series of a targeted base, running makeInference() lazily (with the last horizon) if out of date. Shared by both accessors.

Definition at line 1156 of file KTBNInference_tpl.h.

1156 {
1157 // lazily (re)run with the last horizon; a horizon of 0 means never run
1158 if (!_done_) {
1159 if (_horizon_ == 0)
1160 GUM_ERROR(OperationNotAllowed, "call makeInference(nbTimeSlices) before querying.")
1162 }
1164 if (it == _posteriors_.end())
1165 GUM_ERROR(UndefinedElement, "'" << base << "' is not a target of this inference.")
1166 return it->second;
1167 }
void makeInference(Size nbTimeSlices)
Runs the interface algorithm over nbTimeSlices slices ( ) and caches, for every targeted base,...
Size _horizon_
Horizon (nbTimeSlices) of the last/next run; 0 <=> makeInference never run.
std::unordered_map< std::string, _Series_ > _posteriors_
Cached marginal series of the last run, keyed by base name.
bool _done_
Whether the cached posteriors are up to date.

References _done_, _horizon_, _posteriors_, GUM_ERROR, and makeInference().

Referenced by posterior(), and posteriors().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _snapshot_()

template<GUM_Numeric GUM_SCALAR>
void gum::KTBNInference< GUM_SCALAR >::_snapshot_ ( const std::string & base,
int slice,
const Tensor< GUM_SCALAR > & marginal )
private

Snapshots marginal onto an owned, stably-named descriptor and appends it to that base's series (index == slice for a temporal base). Positional fillWith, not name-matched: marginal's axis is a shared ring object, reused across residue-k slices, whose name generally isn't base[slice].

Definition at line 1131 of file KTBNInference_tpl.h.

1133 {
1134 // owned, stably-named descriptor: marginal's axis is a reused ring object
1135 // whose name isn't base[slice], so fillWith is positional, not name-matched
1136 const std::size_t idx = (slice == ATEMPORAL) ? 0u : static_cast< std::size_t >(slice);
1138 if (series.tensors.size() <= idx) {
1139 series.tensors.resize(idx + 1);
1140 series.vars.resize(idx + 1);
1141 }
1142
1144 outVar->setName(_encode_(base, slice));
1145
1147 out << *outVar;
1148 out.fillWith(marginal, {marginal.variablesSequence().atPos(0)->name()});
1149
1150 series.vars[idx] = std::move(outVar);
1151 series.tensors[idx] = std::move(out);
1152 }
const DiscreteVariable & _templateVar_(const std::string &base, int slice) const
A representative template variable of base for domain/cloning.
A cached marginal time-series for one base: owned variable descriptors paired with their marginals,...

References _encode_(), _posteriors_, _templateVar_(), ATEMPORAL, gum::KTBNInference< GUM_SCALAR >::_Series_::tensors, and gum::KTBNInference< GUM_SCALAR >::_Series_::vars.

Referenced by makeInference().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _templateVar_()

template<GUM_Numeric GUM_SCALAR>
const DiscreteVariable & gum::KTBNInference< GUM_SCALAR >::_templateVar_ ( const std::string & base,
int slice ) const
private

A representative template variable of base for domain/cloning.

Definition at line 147 of file KTBNInference_tpl.h.

148 {
149 if (slice == ATEMPORAL) return _ktbn_->variable(base, ATEMPORAL);
150 return _ktbn_->variable(base, (slice < _k_) ? slice : _k_ - 1);
151 }

References _k_, _ktbn_, and ATEMPORAL.

Referenced by _snapshot_(), addIntervention(), addIntervention(), addObservation(), addObservation(), and addObservations().

Here is the caller graph for this function:

◆ _validateNode_()

template<GUM_Numeric GUM_SCALAR>
void gum::KTBNInference< GUM_SCALAR >::_validateNode_ ( const std::string & base,
int slice ) const
private

Validates that (base, slice) denotes a legal node (future slices ok).

Definition at line 135 of file KTBNInference_tpl.h.

135 {
136 const bool temporal = _isTemporal_(base);
137 const bool atemporal = _isAtemporal_(base);
138
139 if (!temporal && !atemporal) GUM_ERROR(NotFound, "Unknown variable '" << base << "'.")
140 if (temporal && slice < 0)
141 GUM_ERROR(InvalidArgument, "Temporal variable '" << base << "' requires a slice >= 0.")
143 GUM_ERROR(InvalidArgument, "Atemporal variable '" << base << "' has no time slice.")
144 }
bool _isTemporal_(const std::string &base) const
bool _isAtemporal_(const std::string &base) const

References _isAtemporal_(), _isTemporal_(), ATEMPORAL, and GUM_ERROR.

Referenced by addIntervention(), addIntervention(), addObservation(), addObservation(), and addObservations().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _varOfSlot_()

template<GUM_Numeric GUM_SCALAR>
const DiscreteVariable * gum::KTBNInference< GUM_SCALAR >::_varOfSlot_ ( const _Slot_ & s,
int t ) const
private

The variable a window slot stands for at absolute slice t: ring slot \((t-\text{lag}) \bmod k\) for a temporal base, the atemporal object otherwise. This is where "advance one slice" happens – a relabelling, not an allocation.

Definition at line 154 of file KTBNInference_tpl.h.

154 {
155 if (s.lag == ATEMPORAL) return &_ktbn_->variable(_baseNames_[s.base], ATEMPORAL);
156 return &_ktbn_->variable(_baseNames_[s.base], (t - s.lag) % _k_);
157 }

References _baseNames_, _k_, _ktbn_, ATEMPORAL, gum::KTBNInference< GUM_SCALAR >::_Slot_::base, and gum::KTBNInference< GUM_SCALAR >::_Slot_::lag.

Referenced by _fillWindow_(), _propagate_(), and makeInference().

Here is the caller graph for this function:

◆ _windowAt_()

template<GUM_Numeric GUM_SCALAR>
INLINE const KTBNInference< GUM_SCALAR >::_Window_ & gum::KTBNInference< GUM_SCALAR >::_windowAt_ ( int t) const
private

The window for absolute slice t: its own while t is inside the initial block, the repeating one (index k) from then on.

Definition at line 887 of file KTBNInference_tpl.h.

887 {
888 return _windows_[static_cast< std::size_t >(t < _k_ ? t : _k_)];
889 }

References _k_, and _windows_.

Referenced by makeInference().

Here is the caller graph for this function:

◆ _windowPotentials_()

template<GUM_Numeric GUM_SCALAR>
const std::unordered_map< NodeId, Tensor< GUM_SCALAR > > & gum::KTBNInference< GUM_SCALAR >::_windowPotentials_ ( const _Window_ & w,
int t ) const
private

Clique potentials of the window at slice t: every requisite family's CPT (or, under an intervention, a point mass severing it from its causes), times every observation's likelihood.

Parameters
psiout: clique -> its potential (absent clique == unit potential).

Clique potentials for slice t, memoized where they repeat.

psi(t) reads t in exactly four places: windowAt (fixed at windows[k] once t >= k), varOfSlot ((t - lag) % k), buildKernel (t % k) and the encode(base, t) lookups into observations / interventions. The first three are periodic in t % k, so on a slice carrying no temporal evidence the whole map is too: one entry serves every slice of that phase.

Atemporal evidence needs no special case – it applies identically at every slice, so it is part of the periodic structure.

Returns a reference into the cache (or into a scratch map for a slice that does carry evidence). Valid until the next call, and until the next makeInference(), which clears the cache. Callers only read it: propagate and belief both take psi by const reference.

Definition at line 788 of file KTBNInference_tpl.h.

788 {
789 // do(X=x) replaces a CPT the base already applied, so the base is unusable
790 if (_interventionSlices_.contains(t)) {
791 _fillWindow_(w, t, _psiScratch_, true);
792 return _psiScratch_;
793 }
794
795 const Size key = _psiKey_(t);
796 if (!_psiCached_[key]) {
797 _fillWindow_(w, t, _psiCache_[key], false); // base: no slice-specific evidence
798 _psiCached_[key] = true;
799 }
800
801 // unobserved: the base IS the answer, handed over without a copy
802 if (!_observationSlices_.contains(t)) return _psiCache_[key];
803
804 // observed: an observation multiplies ON TOP of the CPT, so the base still
805 // holds. Copying it and applying the likelihood costs one pass per clique,
806 // where a rebuild would redo the unit fill and every CPT product.
809 return _psiScratch_;
810 }
void _fillWindow_(const _Window_ &w, int t, std::unordered_map< NodeId, Tensor< GUM_SCALAR > > &psi, bool withTemporalEvidence=true) const
void _applyTemporalObservations_(const _Window_ &w, int t, std::unordered_map< NodeId, Tensor< GUM_SCALAR > > &psi) const
Multiplies slice t's temporal observation likelihoods into an already-built base. Atemporal ones are ...
std::vector< bool > _psiCached_
std::unordered_map< NodeId, Tensor< GUM_SCALAR > > _psiScratch_
Potentials of an evidence-carrying slice, rebuilt on each visit.
std::unordered_set< int > _observationSlices_
Slices carrying a temporal observation. Their potentials are the periodic ones times that slice's lik...
std::vector< std::unordered_map< NodeId, Tensor< GUM_SCALAR > > > _psiCache_
Memoized clique potentials for the slices that carry no temporal evidence, indexed by psiKey(t)....
Size _psiKey_(int t) const
Cache slot for slice t: the initial slices keep their own, the repeating window contributes one per p...
std::unordered_set< int > _interventionSlices_
Slices carrying a temporal intervention. These need a full rebuild: do(X=x) replaces the node's CPT,...

References _applyTemporalObservations_(), _fillWindow_(), _interventionSlices_, _observationSlices_, _psiCache_, _psiCached_, _psiKey_(), and _psiScratch_.

Referenced by makeInference().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ addIntervention() [1/3]

template<GUM_Numeric GUM_SCALAR>
void gum::KTBNInference< GUM_SCALAR >::addIntervention ( const std::vector< std::pair< NodeKey, KTBNModality > > & interventions)

Records several interventions in one call.

Each entry is a (node, value) pair: node keyed by engine name or by (base, slice), value an index or a label – freely mixed.

All-or-nothing: every entry is validated before any is recorded. A node listed twice keeps its last value.

ie.addIntervention({{"X[5]", 0}, {std::pair{"Z", 2}, "high"}});
Warning
A (base, slice) key needs an explicit std::pair{...}: a bare {"Z", 2} cannot implicitly construct the variant (same rule as gum::KTBN::fillCPT()).
Exceptions
NotFound/ InvalidArgument / OutOfBounds – same as the single-node form, raised before anything is recorded.

Definition at line 181 of file KTBNInference_tpl.h.

182 {
183 // resolve and validate the whole batch first, so a bad entry cannot leave
184 // the engine with the preceding entries already applied
186 resolved.reserve(interventions.size());
187 for (const auto& [key, value]: interventions) {
190 : std::get< std::pair< std::string, int > >(key);
192 resolved.emplace_back(_encode_(b, s), value.toIndex(_templateVar_(b, s)));
193 }
194
195 for (const auto& [name, idx]: resolved)
197 if (!resolved.empty()) _done_ = false;
198 }
void _validateNode_(const std::string &base, int slice) const
Validates that (base, slice) denotes a legal node (future slices ok).

References _determineNode_(), _done_, _encode_(), _interventions_, _templateVar_(), and _validateNode_().

Here is the call graph for this function:

◆ addIntervention() [2/3]

template<GUM_Numeric GUM_SCALAR>
void gum::KTBNInference< GUM_SCALAR >::addIntervention ( std::string_view base,
int slice,
const KTBNModality & value )

Records a hard intervention \(do(base[slice]=value)\).

Recorded only; applied at makeInference(). Re-intervening overwrites. value is a modality index or label (see gum::KTBNModality).

ie.addIntervention("X", 5, 0); // by index
ie.addIntervention("X", 5, "high"); // by label
Exceptions
NotFoundif base is unknown.
InvalidArgumentif the slice is invalid for the variable kind.
OutOfBounds/ NotFound if value is out of the variable's domain or is not one of its modality labels.

Definition at line 164 of file KTBNInference_tpl.h.

166 {
167 const std::string b(base);
170 _done_ = false;
171 }

References _done_, _encode_(), _interventions_, _templateVar_(), _validateNode_(), and gum::KTBNModality::toIndex().

Referenced by addIntervention().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ addIntervention() [3/3]

template<GUM_Numeric GUM_SCALAR>
void gum::KTBNInference< GUM_SCALAR >::addIntervention ( std::string_view node_name,
const KTBNModality & value )

Same, using an engine name ("X[2]", "C", …).

Definition at line 174 of file KTBNInference_tpl.h.

175 {
176 const auto [b, s] = _determineNode_(std::string(node_name));
178 }
void addIntervention(std::string_view base, int slice, const KTBNModality &value)
Records a hard intervention .

References _determineNode_(), and addIntervention().

Here is the call graph for this function:

◆ addObservation() [1/4]

template<GUM_Numeric GUM_SCALAR>
void gum::KTBNInference< GUM_SCALAR >::addObservation ( std::string_view base,
int slice,
const KTBNModality & value )

Records a hard observation \(base[slice]=value\).

Unlike an intervention, an observation is conditioning: it revises the whole network, ancestors included. Recorded only; applied at makeInference(). Re-observing overwrites. value is a modality index or label (see gum::KTBNModality).

ie.addObservation("X", 5, 0); // by index
ie.addObservation("X", 5, "high"); // by label
Exceptions
NotFoundif base is unknown.
InvalidArgumentif the slice is invalid for the variable kind.
OutOfBounds/ NotFound if value is not a legal modality.

Definition at line 234 of file KTBNInference_tpl.h.

236 {
237 const std::string b(base);
240 const Idx idx = value.toIndex(v);
241 std::vector< GUM_SCALAR > like(v.domainSize(), GUM_SCALAR(0));
242 like[idx] = GUM_SCALAR(1);
244 _done_ = false;
245 }

References _done_, _encode_(), _observations_, _templateVar_(), _validateNode_(), gum::DiscreteVariable::domainSize(), and gum::KTBNModality::toIndex().

Referenced by addObservation(), and addObservation().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ addObservation() [2/4]

template<GUM_Numeric GUM_SCALAR>
void gum::KTBNInference< GUM_SCALAR >::addObservation ( std::string_view base,
int slice,
const std::vector< GUM_SCALAR > & likelihood )

Records a soft (likelihood) observation on \(base[slice]\).

likelihood[i] is the likelihood of the observation given that the node takes its i-th modality; it need not sum to 1, but must be non-negative and not all-zero. A one-hot vector is equivalent to a hard observation.

Exceptions
NotFound/ InvalidArgument as above.
InvalidArgumentif likelihood has the wrong length, holds a negative entry, or is all zeros.

Definition at line 255 of file KTBNInference_tpl.h.

257 {
258 const std::string b(base);
261 if (likelihood.size() != v.domainSize())
263 "Soft observation on '" << _encode_(b, slice) << "' needs " << v.domainSize()
264 << " values, got " << likelihood.size() << ".")
267 if (x < GUM_SCALAR(0))
269 "Soft observation on '" << _encode_(b, slice) << "' has a negative entry.")
270 total += x;
271 }
272 if (total <= GUM_SCALAR(0))
274 "Soft observation on '" << _encode_(b, slice) << "' is all zeros: impossible.")
276 _done_ = false;
277 }

References _done_, _encode_(), _observations_, _templateVar_(), _validateNode_(), gum::DiscreteVariable::domainSize(), and GUM_ERROR.

Here is the call graph for this function:

◆ addObservation() [3/4]

template<GUM_Numeric GUM_SCALAR>
void gum::KTBNInference< GUM_SCALAR >::addObservation ( std::string_view node_name,
const KTBNModality & value )

Same, using an engine name ("X[2]", "C", …).

Definition at line 248 of file KTBNInference_tpl.h.

249 {
250 const auto [b, s] = _determineNode_(std::string(node_name));
252 }
void addObservation(std::string_view base, int slice, const KTBNModality &value)
Records a hard observation .

References _determineNode_(), and addObservation().

Here is the call graph for this function:

◆ addObservation() [4/4]

template<GUM_Numeric GUM_SCALAR>
void gum::KTBNInference< GUM_SCALAR >::addObservation ( std::string_view node_name,
const std::vector< GUM_SCALAR > & likelihood )

Same, using an engine name ("X[2]", "C", …).

Definition at line 280 of file KTBNInference_tpl.h.

281 {
282 const auto [b, s] = _determineNode_(std::string(node_name));
284 }

References _determineNode_(), and addObservation().

Here is the call graph for this function:

◆ addObservations()

template<GUM_Numeric GUM_SCALAR>
void gum::KTBNInference< GUM_SCALAR >::addObservations ( const std::vector< std::pair< NodeKey, KTBNModality > > & observations)

Records several observations in one call, all-or-nothing.

Same keying rules (and the same std::pair{...} warning) as the batch addIntervention().

Definition at line 287 of file KTBNInference_tpl.h.

288 {
289 // all-or-nothing, like the batch addIntervention()
291 resolved.reserve(observations.size());
292 for (const auto& [key, value]: observations) {
295 : std::get< std::pair< std::string, int > >(key);
298 std::vector< GUM_SCALAR > like(v.domainSize(), GUM_SCALAR(0));
299 like[value.toIndex(v)] = GUM_SCALAR(1);
300 resolved.emplace_back(_encode_(b, s), std::move(like));
301 }
302 for (auto& [name, like]: resolved)
304 if (!resolved.empty()) _done_ = false;
305 }

References _determineNode_(), _done_, _encode_(), _observations_, _templateVar_(), _validateNode_(), and gum::DiscreteVariable::domainSize().

Here is the call graph for this function:

◆ addTarget()

template<GUM_Numeric GUM_SCALAR>
void gum::KTBNInference< GUM_SCALAR >::addTarget ( std::string_view base)

Declares a target: a base variable whose marginals we want.

A temporal base yields one marginal per slice \(0..T-1\) at makeInference(T); an atemporal base yields its single marginal. The first declared target switches the engine out of default-all-targets mode. Recorded only; applied at makeInference().

Exceptions
NotFoundif base is neither a temporal process nor an atemporal variable of the k-DBN.

Definition at line 346 of file KTBNInference_tpl.h.

346 {
347 const std::string b(base);
348 if (!_isTemporal_(b) && !_isAtemporal_(b))
349 GUM_ERROR(NotFound, "Unknown variable '" << b << "'.")
352 _done_ = false;
353 }

References _done_, _isAtemporal_(), _isTemporal_(), _targeted_mode_, _targets_, and GUM_ERROR.

Here is the call graph for this function:

◆ clearInterventions()

template<GUM_Numeric GUM_SCALAR>
void gum::KTBNInference< GUM_SCALAR >::clearInterventions ( )

Removes all recorded interventions.

Definition at line 213 of file KTBNInference_tpl.h.

213 {
214 _interventions_.clear();
215 _done_ = false;
216 }

References _done_, and _interventions_.

◆ clearObservation()

template<GUM_Numeric GUM_SCALAR>
void gum::KTBNInference< GUM_SCALAR >::clearObservation ( )

Removes all recorded observations.

Definition at line 320 of file KTBNInference_tpl.h.

320 {
321 _observations_.clear();
322 _done_ = false;
323 }

References _done_, and _observations_.

◆ clearTargets()

template<GUM_Numeric GUM_SCALAR>
void gum::KTBNInference< GUM_SCALAR >::clearTargets ( )

Removes all targets (restores default-all-targets mode).

Definition at line 363 of file KTBNInference_tpl.h.

363 {
364 _targets_.clear();
365 _targeted_mode_ = false;
366 _done_ = false;
367 }

References _done_, _targeted_mode_, and _targets_.

◆ eraseIntervention() [1/2]

template<GUM_Numeric GUM_SCALAR>
void gum::KTBNInference< GUM_SCALAR >::eraseIntervention ( std::string_view base,
int slice )

Removes a recorded intervention (silent no-op if absent).

Definition at line 201 of file KTBNInference_tpl.h.

201 {
203 _done_ = false;
204 }

References _done_, _encode_(), and _interventions_.

Referenced by eraseIntervention().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ eraseIntervention() [2/2]

template<GUM_Numeric GUM_SCALAR>
void gum::KTBNInference< GUM_SCALAR >::eraseIntervention ( std::string_view node_name)

Same, using an engine name ("X[2]", "C", …).

Definition at line 207 of file KTBNInference_tpl.h.

207 {
208 const auto [b, s] = _determineNode_(std::string(node_name));
210 }
void eraseIntervention(std::string_view base, int slice)
Removes a recorded intervention (silent no-op if absent).

References _determineNode_(), and eraseIntervention().

Here is the call graph for this function:

◆ eraseObservation() [1/2]

template<GUM_Numeric GUM_SCALAR>
void gum::KTBNInference< GUM_SCALAR >::eraseObservation ( std::string_view base,
int slice )

Removes a recorded observation (silent no-op if absent).

Definition at line 308 of file KTBNInference_tpl.h.

308 {
310 _done_ = false;
311 }

References _done_, _encode_(), and _observations_.

Referenced by eraseObservation().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ eraseObservation() [2/2]

template<GUM_Numeric GUM_SCALAR>
void gum::KTBNInference< GUM_SCALAR >::eraseObservation ( std::string_view node_name)

Same, using an engine name ("X[2]", "C", …).

Definition at line 314 of file KTBNInference_tpl.h.

314 {
315 const auto [b, s] = _determineNode_(std::string(node_name));
317 }
void eraseObservation(std::string_view base, int slice)
Removes a recorded observation (silent no-op if absent).

References _determineNode_(), and eraseObservation().

Here is the call graph for this function:

◆ eraseTarget()

template<GUM_Numeric GUM_SCALAR>
void gum::KTBNInference< GUM_SCALAR >::eraseTarget ( std::string_view base)

Removes a target; when the last one is removed, default-all-targets mode is restored.

Definition at line 356 of file KTBNInference_tpl.h.

356 {
357 _targets_.erase(std::string(base));
358 if (_targets_.empty()) _targeted_mode_ = false;
359 _done_ = false;
360 }

References _done_, _targeted_mode_, and _targets_.

◆ hasIntervention() [1/2]

template<GUM_Numeric GUM_SCALAR>
bool gum::KTBNInference< GUM_SCALAR >::hasIntervention ( std::string_view base,
int slice ) const
Returns
true if base at slice carries an intervention.

Definition at line 219 of file KTBNInference_tpl.h.

219 {
221 }

References _encode_(), and _interventions_.

Referenced by hasIntervention().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ hasIntervention() [2/2]

template<GUM_Numeric GUM_SCALAR>
bool gum::KTBNInference< GUM_SCALAR >::hasIntervention ( std::string_view node_name) const

Same, using an engine name ("X[2]", "C", …).

Definition at line 224 of file KTBNInference_tpl.h.

224 {
225 const auto [b, s] = _determineNode_(std::string(node_name));
226 return hasIntervention(b, s);
227 }
bool hasIntervention(std::string_view base, int slice) const

References _determineNode_(), and hasIntervention().

Here is the call graph for this function:

◆ hasObservation() [1/3]

template<GUM_Numeric GUM_SCALAR>
bool gum::KTBNInference< GUM_SCALAR >::hasObservation ( ) const
Returns
true if any observation is recorded. When false, makeInference() needs no backward pass and runs in horizon-independent memory.

Definition at line 337 of file KTBNInference_tpl.h.

337 {
338 return !_observations_.empty();
339 }

References _observations_.

◆ hasObservation() [2/3]

template<GUM_Numeric GUM_SCALAR>
bool gum::KTBNInference< GUM_SCALAR >::hasObservation ( std::string_view base,
int slice ) const
Returns
true if base at slice carries an observation.

Definition at line 326 of file KTBNInference_tpl.h.

326 {
328 }

References _encode_(), and _observations_.

Referenced by hasObservation(), and makeInference().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ hasObservation() [3/3]

template<GUM_Numeric GUM_SCALAR>
bool gum::KTBNInference< GUM_SCALAR >::hasObservation ( std::string_view node_name) const

Same, using an engine name ("X[2]", "C", …).

Definition at line 331 of file KTBNInference_tpl.h.

331 {
332 const auto [b, s] = _determineNode_(std::string(node_name));
333 return hasObservation(b, s);
334 }
bool hasObservation(std::string_view base, int slice) const

References _determineNode_(), and hasObservation().

Here is the call graph for this function:

◆ interfaceSize()

template<GUM_Numeric GUM_SCALAR>
Size gum::KTBNInference< GUM_SCALAR >::interfaceSize ( ) const

Size of the forward interface of the repeating window: how many node occurrences have to cross each slice boundary. Introspection only.

Definition at line 1229 of file KTBNInference_tpl.h.

1229 {
1230 return static_cast< Size >(_windows_[static_cast< std::size_t >(_k_)].Icur.size());
1231 }

References _k_, and _windows_.

Referenced by toString().

Here is the caller graph for this function:

◆ isInTargetMode()

template<GUM_Numeric GUM_SCALAR>
bool gum::KTBNInference< GUM_SCALAR >::isInTargetMode ( ) const
Returns
true iff at least one explicit target has been declared; when false, every base is a target. Mirrors gum::MarginalTargetedInference::isInTargetMode().

Definition at line 377 of file KTBNInference_tpl.h.

377 {
378 return _targeted_mode_;
379 }

References _targeted_mode_.

◆ isTarget()

template<GUM_Numeric GUM_SCALAR>
bool gum::KTBNInference< GUM_SCALAR >::isTarget ( std::string_view base) const
Returns
true if base is a target of the next inference.

Definition at line 370 of file KTBNInference_tpl.h.

370 {
371 const std::string b(base);
372 if (!_targeted_mode_) return _isTemporal_(b) || _isAtemporal_(b);
373 return _targets_.contains(b);
374 }

References _isAtemporal_(), _isTemporal_(), _targeted_mode_, and _targets_.

Here is the call graph for this function:

◆ ktbn()

template<GUM_Numeric GUM_SCALAR>
const KTBN< GUM_SCALAR > & gum::KTBNInference< GUM_SCALAR >::ktbn ( ) const
Returns
The k-DBN reasoned about.

Definition at line 1219 of file KTBNInference_tpl.h.

1219 {
1220 return *_ktbn_;
1221 }

References _ktbn_.

Referenced by KTBNInference().

Here is the caller graph for this function:

◆ logObservationProbability()

template<GUM_Numeric GUM_SCALAR>
GUM_SCALAR gum::KTBNInference< GUM_SCALAR >::logObservationProbability ( )

\(\log P(\text{obs} \mid do(\cdot))\) for the last run: the likelihood of the observations under the (possibly mutilated) model. 0 when nothing is observed. Lazily (re)runs makeInference() if out of date.

Exceptions
OperationNotAllowedif makeInference() has never been run.

Definition at line 1200 of file KTBNInference_tpl.h.

1200 {
1201 if (!_done_) {
1202 if (_horizon_ == 0)
1203 GUM_ERROR(OperationNotAllowed, "call makeInference(nbTimeSlices) before querying.")
1205 }
1207 }
GUM_SCALAR _logObservation_
log P(observation | do) of the last run.

References _done_, _horizon_, _logObservation_, GUM_ERROR, and makeInference().

Referenced by observationProbability().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ makeInference()

template<GUM_Numeric GUM_SCALAR>
void gum::KTBNInference< GUM_SCALAR >::makeInference ( Size nbTimeSlices)

Runs the interface algorithm over nbTimeSlices slices ( \(0..nbTimeSlices-1\)) and caches, for every targeted base, its marginal at every slice.

A forward sweep passes \(m_t\) window to window; if any observation is recorded, a backward sweep passes \(r_t\) the other way and the two combine into exact smoothed posteriors. With no observation the backward pass is skipped and only one window is ever live.

Window junction trees are compiled once, at construction, and reused by every call regardless of horizon or target set; only the requisite subnetwork is recomputed per run. Observations/interventions at slices \(\geq\) nbTimeSlices fall outside the roll and are ignored.

Idempotent. Called lazily by posterior()/posteriors() (last horizon) when out of date. nbTimeSlices becomes the stored horizon.

Exceptions
InvalidArgumentif nbTimeSlices is 0.
FatalErrorif the observations have probability 0 under the model.

Definition at line 954 of file KTBNInference_tpl.h.

954 {
955 if (nbTimeSlices == 0) GUM_ERROR(InvalidArgument, "makeInference: nbTimeSlices must be >= 1.")
956
957 const int T = static_cast< int >(nbTimeSlices);
961 _kernelCache_.clear(); // CPT values may have been edited since the last run
962
963 // Potential cache: same reason to clear, plus the evidence may have moved.
964 _psiCache_.assign(2 * _k_, {});
965 _psiCached_.assign(2 * _k_, false);
966 _psiScratch_.clear();
967
968 // Which slices are not the periodic ones. Decoding the (few) evidence keys
969 // costs O(#evidence); probing every slice would cost what the cache saves.
970 // Atemporal evidence decodes to ATEMPORAL and marks no slice: it applies at
971 // every t alike and so belongs to the periodic structure.
972 _observationSlices_.clear();
973 _interventionSlices_.clear();
974 for (const auto& [name, value]: _observations_) {
975 (void)value;
976 const int s = _determineNode_(name).second;
977 if (s != ATEMPORAL) _observationSlices_.insert(s);
978 }
979 for (const auto& [name, value]: _interventions_) {
980 (void)value;
981 const int s = _determineNode_(name).second;
982 if (s != ATEMPORAL) _interventionSlices_.insert(s);
983 }
985
986 const bool defaultAll = !_targeted_mode_;
987 const auto targeted = [&](const std::string& b) { return defaultAll || _targets_.contains(b); };
988 // nothing observed => backward messages are provably uniform, so the
989 // sweep is skipped and only one window is ever live
990 const bool smoothing = hasObservation();
991
992 // pre-size the series: with smoothing the slices are filled back-to-front
993 for (int i = 0; i < static_cast< int >(_baseNames_.size()); ++i) {
994 const std::string& b = _baseNames_[i];
995 if (!_requisite_[i] || !targeted(b)) continue;
997 const std::size_t n
998 = (i < static_cast< int >(_nbTemporal_)) ? static_cast< std::size_t >(T) : std::size_t(1);
999 s.vars.resize(n);
1000 s.tensors.resize(n);
1001 }
1002
1003 const auto ifaceVars = [&](const std::vector< _Slot_ >& slots, int slice) {
1005 for (const auto& s: slots)
1006 keep.insert(_varOfSlot_(s, slice));
1007 return keep;
1008 };
1009
1010 const auto readPosteriors
1011 = [&](const _Window_& w,
1012 int slice,
1017 const int lastBase = (slice == 0) ? static_cast< int >(_baseNames_.size())
1018 : static_cast< int >(_nbTemporal_);
1019 for (int i = 0; i < lastBase; ++i) {
1020 if (!_requisite_[i] || !targeted(_baseNames_[i])) continue;
1021 const auto itc = w.selfClique.find(i);
1022 if (itc == w.selfClique.end()) continue;
1023 const bool atemp = i >= static_cast< int >(_nbTemporal_);
1025 keep.insert(atemp ? &_ktbn_->variable(_baseNames_[i], ATEMPORAL)
1026 : &_ktbn_->variable(_baseNames_[i], slice % _k_));
1028 = _belief_(w, psi, msgs, inPrev, inNext, itc->second, KTBN_SKIP_NONE).sumIn(keep);
1029 m.normalize();
1031 }
1032 };
1033
1035
1036 // ---- forward sweep: carry m_t across each slice boundary ----------------
1037 // only the interface is retained per slice, and only if smoothing needs it
1039 if (smoothing && T > 1) fwd.resize(static_cast< std::size_t >(T - 1));
1040
1042 bool hasPrev = false;
1043
1044 for (int t = 0; t < T; ++t) {
1045 const _Window_& w = _windowAt_(t);
1046 if (w.bfs.empty()) break; // nothing requisite at all
1047
1048 const auto& psi = _windowPotentials_(w, t);
1049 _propagate_(w, t, psi, hasPrev ? &prev : nullptr, nullptr, !smoothing, msgs);
1050
1051 if (!smoothing) readPosteriors(w, t, psi, msgs, hasPrev ? &prev : nullptr, nullptr);
1052
1054 = _belief_(w, psi, msgs, hasPrev ? &prev : nullptr, nullptr, w.rootC, KTBN_SKIP_NEXT)
1055 .sumIn(ifaceVars(w.Icur, t));
1056 const GUM_SCALAR mass = m.sum();
1057 if (!(mass > GUM_SCALAR(0)))
1059 "makeInference: the observations have probability 0 under this model "
1060 "(impossible at slice "
1061 << t << ").")
1062 m.scale(GUM_SCALAR(1) / mass);
1064
1065 if (t + 1 < T) {
1066 if (smoothing) fwd[static_cast< std::size_t >(t)] = m;
1067 prev = std::move(m);
1068 hasPrev = true;
1069 }
1070 }
1071
1072 // ---- backward sweep: carry r_t the other way and combine ---------------
1073 if (smoothing) {
1075 bool hasNext = false;
1076 for (int t = T - 1; t >= 0; --t) {
1077 const _Window_& w = _windowAt_(t);
1078 if (w.bfs.empty()) break;
1079
1080 const auto& psi = _windowPotentials_(w, t);
1082 = (t > 0) ? &fwd[static_cast< std::size_t >(t - 1)] : nullptr;
1083 const Tensor< GUM_SCALAR >* inNext = hasNext ? &nxt : nullptr;
1084 _propagate_(w, t, psi, inPrev, inNext, true, msgs);
1085
1087
1088 if (t > 0) {
1090 .sumIn(ifaceVars(w.Iprev, t));
1091 const GUM_SCALAR mass = r.sum();
1092 // Symmetric with the forward sweep. In exact arithmetic this cannot
1093 // fire -- a null backward mass would mean P(e_{t:T} | I) = 0 for every
1094 // interface state, hence P(e) = 0, which the forward sweep just
1095 // disproved. In floating point it fires readily: the intra-window
1096 // product is formed BEFORE any normalisation, so near-deterministic
1097 // CPTs flush it to zero while the true value is merely tiny
1098 // (reproduced with transition probabilities of 1e-100 at T=4).
1099 //
1100 // Left unguarded the failure is silent and total: r stays all-zero,
1101 // becomes the next inNext, and zeroes every belief from here back to
1102 // slice 0 -- and readPosteriors' normalize() is a no-op on a zero sum,
1103 // so posterior() would return tensors of zeros looking like
1104 // distributions. Do NOT reuse the forward sweep's wording here: the
1105 // observations are *not* impossible, they underflowed.
1106 if (!(mass > GUM_SCALAR(0)))
1108 "makeInference: the backward message leaving slice "
1109 << t
1110 << " underflowed to zero. The observations are not impossible -- the "
1111 "forward sweep accepted them (log P(obs) = "
1113 << ") -- but the model's probabilities are too extreme for this horizon "
1114 "to be resolved in double precision. Soften the near-deterministic "
1115 "CPT entries, or shorten the horizon / reduce the observations.")
1116 r.scale(GUM_SCALAR(1) / mass);
1117 nxt = std::move(r);
1118 hasNext = true;
1119 }
1120 }
1121 }
1122
1123 _done_ = true;
1124 }
void _snapshot_(const std::string &base, int slice, const Tensor< GUM_SCALAR > &marginal)
Snapshots marginal onto an owned, stably-named descriptor and appends it to that base's series (index...
void _markRequisite_()
Marks the requisite bases of the current run (targets, observed bases and all their ancestors) into r...
const std::unordered_map< NodeId, Tensor< GUM_SCALAR > > & _windowPotentials_(const _Window_ &w, int t) const
Clique potentials of the window at slice t: every requisite family's CPT (or, under an intervention,...
void _propagate_(const _Window_ &w, int t, const std::unordered_map< NodeId, Tensor< GUM_SCALAR > > &psi, const Tensor< GUM_SCALAR > *inPrev, const Tensor< GUM_SCALAR > *inNext, bool distribute, std::map< std::pair< NodeId, NodeId >, Tensor< GUM_SCALAR > > &msgs) const
Shafer-Shenoy pass over a filled window. inPrev / inNext are the interface messages arriving at rootD...
const _Window_ & _windowAt_(int t) const
The window for absolute slice t: its own while t is inside the initial block, the repeating one (inde...

References _baseNames_, _belief_(), _determineNode_(), _done_, _horizon_, _interventions_, _interventionSlices_, _k_, _kernelCache_, _ktbn_, _logObservation_, _markRequisite_(), _nbTemporal_, _observations_, _observationSlices_, _posteriors_, _propagate_(), _psiCache_, _psiCached_, _psiScratch_, _requisite_, _snapshot_(), _targeted_mode_, _targets_, _varOfSlot_(), _windowAt_(), _windowPotentials_(), ATEMPORAL, gum::KTBNInference< GUM_SCALAR >::_Window_::bfs, GUM_ERROR, hasObservation(), gum::KTBNInference< GUM_SCALAR >::_Window_::Icur, gum::Set< Key >::insert(), gum::KTBNInference< GUM_SCALAR >::_Window_::Iprev, gum::KTBNInference< GUM_SCALAR >::_Window_::rootC, gum::KTBNInference< GUM_SCALAR >::_Window_::rootD, gum::KTBNInference< GUM_SCALAR >::_Window_::selfClique, gum::KTBNInference< GUM_SCALAR >::_Series_::tensors, and gum::KTBNInference< GUM_SCALAR >::_Series_::vars.

Referenced by _series_(), and logObservationProbability().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ observationProbability()

template<GUM_Numeric GUM_SCALAR>
GUM_SCALAR gum::KTBNInference< GUM_SCALAR >::observationProbability ( )

\(P(\text{obs} \mid do(\cdot))\), i.e. exp of logObservationProbability(). Underflows to 0 on long horizons; prefer the log form.

Definition at line 1210 of file KTBNInference_tpl.h.

1210 {
1211 return static_cast< GUM_SCALAR >(std::exp(static_cast< double >(logObservationProbability())));
1212 }
GUM_SCALAR logObservationProbability()
for the last run: the likelihood of the observations under the (possibly mutilated) model....

References logObservationProbability().

Here is the call graph for this function:

◆ operator=()

template<GUM_Numeric GUM_SCALAR>
KTBNInference< GUM_SCALAR > & gum::KTBNInference< GUM_SCALAR >::operator= ( const KTBNInference< GUM_SCALAR > & )
delete

Constructor.

Parameters
ktbnThe k-DBN to reason about (referenced, not copied).
Exceptions
InvalidArgumentif ktbn is null.

References KTBNInference().

Here is the call graph for this function:

◆ posterior() [1/2]

template<GUM_Numeric GUM_SCALAR>
const Tensor< GUM_SCALAR > & gum::KTBNInference< GUM_SCALAR >::posterior ( std::string_view base,
int slice )

Returns \(P(base[slice] \mid \text{obs}, do(\cdot))\).

Use ATEMPORAL as slice for an atemporal base. Lazily (re)runs makeInference() with the last horizon if out of date. The returned reference is owned by the engine and is invalidated by the next makeInference(); copy it to keep it.

Exceptions
OperationNotAllowedif makeInference() has never been run.
UndefinedElementif base is not a target.
OutOfBoundsif slice was not computed ( \(\geq\) the horizon).

Definition at line 1170 of file KTBNInference_tpl.h.

1171 {
1172 const std::string b(base);
1173 const _Series_& series = _series_(b);
1174
1175 if (_isAtemporal_(b)) {
1176 if (slice != ATEMPORAL)
1177 GUM_ERROR(InvalidArgument, "Atemporal variable '" << b << "' has no time slice.")
1179 }
1180 if (slice < 0 || static_cast< Size >(slice) >= series.tensors.size())
1182 "Slice " << slice << " for '" << b << "' was not computed (horizon " << _horizon_
1183 << ").")
1185 }
const _Series_ & _series_(const std::string &base)
The cached series of a targeted base, running makeInference() lazily (with the last horizon) if out o...

References _horizon_, _isAtemporal_(), _series_(), ATEMPORAL, GUM_ERROR, and gum::KTBNInference< GUM_SCALAR >::_Series_::tensors.

Referenced by posterior().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ posterior() [2/2]

template<GUM_Numeric GUM_SCALAR>
const Tensor< GUM_SCALAR > & gum::KTBNInference< GUM_SCALAR >::posterior ( std::string_view node_name)

Same, using an engine name ("X[2]", "C", …).

Definition at line 1188 of file KTBNInference_tpl.h.

1188 {
1189 const auto [b, s] = _determineNode_(std::string(node_name));
1190 return posterior(b, s);
1191 }
const Tensor< GUM_SCALAR > & posterior(std::string_view base, int slice)
Returns .

References _determineNode_(), and posterior().

Here is the call graph for this function:

◆ posteriors()

template<GUM_Numeric GUM_SCALAR>
const std::vector< Tensor< GUM_SCALAR > > & gum::KTBNInference< GUM_SCALAR >::posteriors ( std::string_view base)

The whole marginal time-series of a targeted base: tensors[t] is \(P(base[t] \mid \cdot)\) for \(t = 0..T-1\) (a single-element vector, holding the atemporal marginal, for an atemporal base).

Zero-copy: returns a reference to the engine-owned vector, invalidated by the next makeInference(). Lazily (re)runs makeInference() if out of date.

Exceptions
OperationNotAllowedif makeInference() has never been run.
UndefinedElementif base is not a target.

Definition at line 1195 of file KTBNInference_tpl.h.

1195 {
1196 return _series_(std::string(base)).tensors;
1197 }

References _series_(), and gum::KTBNInference< GUM_SCALAR >::_Series_::tensors.

Here is the call graph for this function:

◆ toString()

template<GUM_Numeric GUM_SCALAR>
std::string gum::KTBNInference< GUM_SCALAR >::toString ( ) const
Returns
A human-readable summary of the engine's state.

Definition at line 1234 of file KTBNInference_tpl.h.

1234 {
1236 s << "KTBNInference (k=" << _k_ << ", interface=" << interfaceSize() << ")\n";
1237 s << " interventions: {";
1238 bool first = true;
1239 for (const auto& [name, val]: _interventions_) {
1240 s << (first ? "" : ", ") << "do(" << name << "=" << val << ")";
1241 first = false;
1242 }
1243 s << "}\n observations: {";
1244 first = true;
1245 for (const auto& [name, like]: _observations_) {
1246 (void)like;
1247 s << (first ? "" : ", ") << name;
1248 first = false;
1249 }
1250 s << "}\n targets: ";
1251 if (!_targeted_mode_) s << "<all bases>";
1252 else {
1253 s << "{";
1254 first = true;
1255 for (const auto& name: _targets_) {
1256 s << (first ? "" : ", ") << name;
1257 first = false;
1258 }
1259 s << "}";
1260 }
1261 s << "\n state: " << (_done_ ? "computed" : "not computed yet") << "\n";
1262 return s.str();
1263 }
Size interfaceSize() const
Size of the forward interface of the repeating window: how many node occurrences have to cross each s...

References _done_, _interventions_, _k_, _observations_, _targeted_mode_, _targets_, and interfaceSize().

Here is the call graph for this function:

◆ windowJunctionTree()

template<GUM_Numeric GUM_SCALAR>
const JunctionTree & gum::KTBNInference< GUM_SCALAR >::windowJunctionTree ( ) const

The junction tree of the repeating window – the one compiled from the k-slice template and re-entered at every step from slice \(k-1\) on. Introspection only.

Definition at line 1224 of file KTBNInference_tpl.h.

1224 {
1225 return _windows_[static_cast< std::size_t >(_k_)].jt;
1226 }

References _k_, and _windows_.

Member Data Documentation

◆ _atemporalSorted_

template<GUM_Numeric GUM_SCALAR>
std::vector< std::string > gum::KTBNInference< GUM_SCALAR >::_atemporalSorted_
private

Definition at line 502 of file KTBNInference.h.

Referenced by KTBNInference().

◆ _baseIdx_

template<GUM_Numeric GUM_SCALAR>
std::unordered_map< std::string, int > gum::KTBNInference< GUM_SCALAR >::_baseIdx_
private

name -> index into baseNames

Definition at line 509 of file KTBNInference.h.

Referenced by KTBNInference(), _buildWindows_(), _familySlots_(), _lastConsumerSlice_(), and _markRequisite_().

◆ _baseNames_

template<GUM_Numeric GUM_SCALAR>
std::vector< std::string > gum::KTBNInference< GUM_SCALAR >::_baseNames_
private

All bases: temporal first (indices 0.._nbTemporal_-1), then atemporal. Window slots index into this.

Definition at line 506 of file KTBNInference.h.

Referenced by KTBNInference(), _applyTemporalObservations_(), _buildWindows_(), _compileWindow_(), _familySlots_(), _fillWindow_(), _interfaceAfter_(), _lastConsumerSlice_(), _markRequisite_(), _varOfSlot_(), and makeInference().

◆ _done_

template<GUM_Numeric GUM_SCALAR>
bool gum::KTBNInference< GUM_SCALAR >::_done_ {false}
private

◆ _horizon_

template<GUM_Numeric GUM_SCALAR>
Size gum::KTBNInference< GUM_SCALAR >::_horizon_ {0}
private

Horizon (nbTimeSlices) of the last/next run; 0 <=> makeInference never run.

Definition at line 488 of file KTBNInference.h.

Referenced by _series_(), logObservationProbability(), makeInference(), and posterior().

◆ _interventions_

template<GUM_Numeric GUM_SCALAR>
std::map< std::string, Idx > gum::KTBNInference< GUM_SCALAR >::_interventions_
private

Recorded interventions, keyed by engine name -> forced value.

Definition at line 475 of file KTBNInference.h.

Referenced by _fillWindow_(), addIntervention(), addIntervention(), clearInterventions(), eraseIntervention(), hasIntervention(), makeInference(), and toString().

◆ _interventionSlices_

template<GUM_Numeric GUM_SCALAR>
std::unordered_set< int > gum::KTBNInference< GUM_SCALAR >::_interventionSlices_
mutableprivate

Slices carrying a temporal intervention. These need a full rebuild: do(X=x) replaces the node's CPT, which the cached base has already multiplied in, so the base is unusable rather than merely incomplete. Atemporal evidence appears in neither set – it applies at every slice alike and so belongs to the periodic structure.

Definition at line 544 of file KTBNInference.h.

Referenced by _windowPotentials_(), and makeInference().

◆ _k_

◆ _kernelCache_

template<GUM_Numeric GUM_SCALAR>
std::map< std::pair< std::string, int >, Tensor< GUM_SCALAR > > gum::KTBNInference< GUM_SCALAR >::_kernelCache_
mutableprivate

Memoized transition kernels, keyed by (process, t % k).

buildKernel reads t only through t % k (directly for the child, as (t - lag) % k for each parent – and t >= k with lag <= k-1 there, so the subtraction never goes negative). The kernel is therefore periodic in t with period k: at most k distinct tensors per process, whatever the horizon. Without this, a run rebuilds one per slice per process – and twice per slice once smoothing is on.

Cleared at the top of every makeInference(): cpt() hands out a const reference whose content is mutable, so a caller may edit CPT values between runs. Nothing can change during a run.

Definition at line 565 of file KTBNInference.h.

Referenced by _buildKernel_(), and makeInference().

◆ _ktbn_

template<GUM_Numeric GUM_SCALAR>
const KTBN< GUM_SCALAR >* gum::KTBNInference< GUM_SCALAR >::_ktbn_
private

◆ _logObservation_

template<GUM_Numeric GUM_SCALAR>
GUM_SCALAR gum::KTBNInference< GUM_SCALAR >::_logObservation_ {0}
private

log P(observation | do) of the last run.

Definition at line 494 of file KTBNInference.h.

Referenced by logObservationProbability(), and makeInference().

◆ _maxLag_

template<GUM_Numeric GUM_SCALAR>
std::vector< int > gum::KTBNInference< GUM_SCALAR >::_maxLag_
private

maxLag[i]: largest lag at which the transition kernel still consumes temporal base i – how long an occurrence must stay in the interface, which is what makes |I| finite and the window template time-invariant.

Definition at line 514 of file KTBNInference.h.

Referenced by _buildWindows_(), and _lastConsumerSlice_().

◆ _nbTemporal_

template<GUM_Numeric GUM_SCALAR>
std::size_t gum::KTBNInference< GUM_SCALAR >::_nbTemporal_ {0}
private

◆ _observations_

template<GUM_Numeric GUM_SCALAR>
std::map< std::string, std::vector< GUM_SCALAR > > gum::KTBNInference< GUM_SCALAR >::_observations_
private

Recorded observations, keyed by engine name -> likelihood vector (one-hot for a hard observation).

Definition at line 479 of file KTBNInference.h.

Referenced by _applyTemporalObservations_(), _fillWindow_(), _markRequisite_(), addObservation(), addObservation(), addObservations(), clearObservation(), eraseObservation(), hasObservation(), hasObservation(), makeInference(), and toString().

◆ _observationSlices_

template<GUM_Numeric GUM_SCALAR>
std::unordered_set< int > gum::KTBNInference< GUM_SCALAR >::_observationSlices_
mutableprivate

Slices carrying a temporal observation. Their potentials are the periodic ones times that slice's likelihood, so they are served by copying the cached base and multiplying the evidence in – cheaper than a rebuild, which would redo the unit fill and every CPT product.

Definition at line 537 of file KTBNInference.h.

Referenced by _windowPotentials_(), and makeInference().

◆ _posteriors_

template<GUM_Numeric GUM_SCALAR>
std::unordered_map< std::string, _Series_ > gum::KTBNInference< GUM_SCALAR >::_posteriors_
private

Cached marginal series of the last run, keyed by base name.

Definition at line 497 of file KTBNInference.h.

Referenced by _series_(), _snapshot_(), and makeInference().

◆ _psiCache_

template<GUM_Numeric GUM_SCALAR>
std::vector< std::unordered_map< NodeId, Tensor< GUM_SCALAR > > > gum::KTBNInference< GUM_SCALAR >::_psiCache_
mutableprivate

Memoized clique potentials for the slices that carry no temporal evidence, indexed by psiKey(t). Sized 2k: the k initial slices have their own windows and their own CPTs, the repeating one contributes k phases. O(k) whatever the horizon – unlike keeping one per slice, which would make smoothing grow linearly in T.

Definition at line 530 of file KTBNInference.h.

Referenced by _windowPotentials_(), and makeInference().

◆ _psiCached_

template<GUM_Numeric GUM_SCALAR>
std::vector< bool > gum::KTBNInference< GUM_SCALAR >::_psiCached_
mutableprivate

Definition at line 531 of file KTBNInference.h.

Referenced by _windowPotentials_(), and makeInference().

◆ _psiScratch_

template<GUM_Numeric GUM_SCALAR>
std::unordered_map< NodeId, Tensor< GUM_SCALAR > > gum::KTBNInference< GUM_SCALAR >::_psiScratch_
mutableprivate

Potentials of an evidence-carrying slice, rebuilt on each visit.

Definition at line 547 of file KTBNInference.h.

Referenced by _windowPotentials_(), and makeInference().

◆ _requisite_

template<GUM_Numeric GUM_SCALAR>
std::vector< bool > gum::KTBNInference< GUM_SCALAR >::_requisite_
private

Bases actually folded by the current run: the targets, the observed nodes and all their ancestors. Rebuilt per makeInference() from the current target/observation sets; anything outside is barren and cannot move an answer.

Definition at line 523 of file KTBNInference.h.

Referenced by KTBNInference(), _buildWindows_(), _compileWindow_(), _interfaceAfter_(), _lastConsumerSlice_(), _markRequisite_(), and makeInference().

◆ _targeted_mode_

template<GUM_Numeric GUM_SCALAR>
bool gum::KTBNInference< GUM_SCALAR >::_targeted_mode_ {false}
private

Whether at least one explicit target has been declared.

Definition at line 485 of file KTBNInference.h.

Referenced by _markRequisite_(), addTarget(), clearTargets(), eraseTarget(), isInTargetMode(), isTarget(), makeInference(), and toString().

◆ _targets_

template<GUM_Numeric GUM_SCALAR>
std::set< std::string > gum::KTBNInference< GUM_SCALAR >::_targets_
private

Recorded targets (base names). Empty <=> default-all-targets mode.

Definition at line 482 of file KTBNInference.h.

Referenced by _markRequisite_(), addTarget(), clearTargets(), eraseTarget(), isTarget(), makeInference(), and toString().

◆ _temporalSorted_

template<GUM_Numeric GUM_SCALAR>
std::vector< std::string > gum::KTBNInference< GUM_SCALAR >::_temporalSorted_
private

Temporal / atemporal base names in a deterministic (sorted) order, cached once at construction (the KTBN's own sets are unordered).

Definition at line 501 of file KTBNInference.h.

Referenced by KTBNInference().

◆ _windows_

template<GUM_Numeric GUM_SCALAR>
std::vector< _Window_ > gum::KTBNInference< GUM_SCALAR >::_windows_
private

The compiled windows: index t for t <= k-2 (initial), index k-1 for the repeating window, reused by every slice from k-1 on. Built once.

Definition at line 518 of file KTBNInference.h.

Referenced by _buildWindows_(), _windowAt_(), interfaceSize(), and windowJunctionTree().

◆ ATEMPORAL

template<GUM_Numeric GUM_SCALAR>
int gum::KTBNInference< GUM_SCALAR >::ATEMPORAL = KTBN< GUM_SCALAR >::ATEMPORAL
staticconstexpr

The documentation for this class was generated from the following files: