64 constexpr NodeId KTBN_SKIP_NONE = std::numeric_limits< NodeId >::max();
65 constexpr NodeId KTBN_SKIP_PREV = std::numeric_limits< NodeId >::max() - 1;
66 constexpr NodeId KTBN_SKIP_NEXT = std::numeric_limits< NodeId >::max() - 2;
73 template < GUM_Numeric GUM_SCALAR >
76 _k_ =
static_cast< int >(
ktbn->k());
88 for (std::size_t i = 0; i <
_baseNames_.size(); ++i)
101 template < GUM_Numeric GUM_SCALAR >
104 return base +
"[" + std::to_string(slice) +
"]";
107 template < GUM_Numeric GUM_SCALAR >
108 std::pair< std::string, int >
111 if (
_ktbn_->atemporalVarNames().find(name) !=
_ktbn_->atemporalVarNames().end())
116 const auto lb = name.rfind(
'[');
117 if (lb == std::string::npos || name.back() !=
']')
return {name,
ATEMPORAL};
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};
126 slice = std::stoi(inner);
127 }
catch (
const std::out_of_range&) {
129 "Node name '" << name <<
"' has a slice index too large to represent as int.")
131 return {name.substr(0, lb), slice};
134 template < GUM_Numeric GUM_SCALAR >
139 if (!temporal && !atemporal)
GUM_ERROR(
NotFound,
"Unknown variable '" << base <<
"'.")
140 if (temporal && slice < 0)
146 template < GUM_Numeric GUM_SCALAR >
150 return _ktbn_->variable(base, (slice <
_k_) ? slice :
_k_ - 1);
153 template < GUM_Numeric GUM_SCALAR >
163 template < GUM_Numeric GUM_SCALAR >
167 const std::string b(base);
173 template < GUM_Numeric GUM_SCALAR >
180 template < GUM_Numeric GUM_SCALAR >
182 const std::vector< std::pair< NodeKey, KTBNModality > >& interventions) {
185 std::vector< std::pair< std::string, Idx > > resolved;
186 resolved.reserve(interventions.size());
187 for (
const auto& [key, value]: interventions) {
188 const auto [b, s] = std::holds_alternative< std::string >(key)
190 : std::get< std::pair< std::string, int > >(key);
195 for (
const auto& [name, idx]: resolved)
197 if (!resolved.empty())
_done_ =
false;
200 template < GUM_Numeric GUM_SCALAR >
206 template < GUM_Numeric GUM_SCALAR >
212 template < GUM_Numeric GUM_SCALAR >
218 template < GUM_Numeric GUM_SCALAR >
223 template < GUM_Numeric GUM_SCALAR >
233 template < GUM_Numeric GUM_SCALAR >
237 const std::string b(base);
241 std::vector< GUM_SCALAR > like(v.
domainSize(), GUM_SCALAR(0));
242 like[idx] = GUM_SCALAR(1);
247 template < GUM_Numeric GUM_SCALAR >
254 template < GUM_Numeric GUM_SCALAR >
257 const std::vector< GUM_SCALAR >& likelihood) {
258 const std::string b(base);
264 <<
" values, got " << likelihood.size() <<
".")
265 GUM_SCALAR total = GUM_SCALAR(0);
266 for (
const auto x: likelihood) {
267 if (x < GUM_SCALAR(0))
269 "Soft observation on '" <<
_encode_(b, slice) <<
"' has a negative entry.")
272 if (total <= GUM_SCALAR(0))
274 "Soft observation on '" <<
_encode_(b, slice) <<
"' is all zeros: impossible.")
279 template < GUM_Numeric GUM_SCALAR >
281 const std::vector< GUM_SCALAR >& likelihood) {
286 template < GUM_Numeric GUM_SCALAR >
288 const std::vector< std::pair< NodeKey, KTBNModality > >& observations) {
290 std::vector< std::pair< std::string, std::vector< GUM_SCALAR > > > resolved;
291 resolved.reserve(observations.size());
292 for (
const auto& [key, value]: observations) {
293 const auto [b, s] = std::holds_alternative< std::string >(key)
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));
302 for (
auto& [name, like]: resolved)
304 if (!resolved.empty())
_done_ =
false;
307 template < GUM_Numeric GUM_SCALAR >
313 template < GUM_Numeric GUM_SCALAR >
319 template < GUM_Numeric GUM_SCALAR >
325 template < GUM_Numeric GUM_SCALAR >
330 template < GUM_Numeric GUM_SCALAR >
336 template < GUM_Numeric GUM_SCALAR >
345 template < GUM_Numeric GUM_SCALAR >
347 const std::string b(base);
355 template < GUM_Numeric GUM_SCALAR >
362 template < GUM_Numeric GUM_SCALAR >
369 template < GUM_Numeric GUM_SCALAR >
371 const std::string b(base);
376 template < GUM_Numeric GUM_SCALAR >
385 template < GUM_Numeric GUM_SCALAR >
386 std::vector< typename KTBNInference< GUM_SCALAR >::_Slot_ >
388 std::vector< _Slot_ > out;
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)) {
406 else out.push_back({
_baseIdx_.at(pb), structSlice - ps});
411 template < GUM_Numeric GUM_SCALAR >
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) {
422 if (ps == s &&
_baseIdx_.at(pb) == baseIdx) {
427 if (tc == last)
break;
434 const int tc = s +
_maxLag_[baseIdx];
435 if (tc >=
_k_ - 1 && tc > last) last = tc;
440 template < GUM_Numeric GUM_SCALAR >
441 std::vector< typename KTBNInference< GUM_SCALAR >::_Slot_ >
443 std::vector< _Slot_ > out;
444 for (
int i = 0; i < static_cast< int >(
_nbTemporal_); ++i) {
446 for (
int s = t; s >= 0 && t - s <=
_k_ - 1; --s)
456 template < GUM_Numeric GUM_SCALAR >
459 const std::vector< _Slot_ >& Icur,
461 bool withAtemporalFamilies)
const {
467 std::map< std::pair< int, int >,
NodeId > id;
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;
486 for (
const auto& s: Iprev)
488 for (
int i = 0; i < static_cast< int >(
_nbTemporal_); ++i)
490 if (withAtemporalFamilies)
494 for (
const auto& s: Icur)
497 if (
graph.size() == 0)
return w;
500 std::vector< std::pair< int, std::vector< NodeId > > > families;
501 const auto addFamily = [&](
int b) {
502 std::vector< NodeId > scope;
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));
510 for (
int i = 0; i < static_cast< int >(
_nbTemporal_); ++i)
512 if (withAtemporalFamilies)
520 const auto makeClique = [&](
const std::vector< _Slot_ >& slots) {
521 std::vector< NodeId > ids;
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]);
535 const auto covers = [&](
NodeId c,
const std::vector< _Slot_ >& slots) {
537 for (
const auto& s: slots)
538 if (!cl.
contains(
id.at({s.base, s.lag})))
return false;
542 bool foundC =
false, foundD =
false;
544 if (!foundC && covers(c, Icur)) {
548 if (!foundD && covers(c, Iprev)) {
553 if (!foundC || !foundD)
557 for (
const auto& [b, scope]: families) {
562 for (
const NodeId n: scope)
578 for (
const auto& [key, nid]:
id) {
579 const int base = key.first;
592 std::unordered_map< NodeId, int > comp;
595 if (comp.count(s) != 0)
continue;
596 std::vector< NodeId > q{s};
598 for (std::size_t i = 0; i < q.size(); ++i)
600 if (comp.count(nb) == 0) {
607 std::unordered_set< int > linked{comp.at(w.
rootC)};
614 std::unordered_set< NodeId > seen;
616 seen.insert(w.
rootC);
618 for (std::size_t qi = 0; qi < w.
bfs.size(); ++qi) {
621 if (seen.insert(nb).second) {
633 template < GUM_Numeric GUM_SCALAR >
638 for (
int c = 0; c < static_cast< int >(
_nbTemporal_); ++c) {
643 const int lag = (
_k_ - 1) - ps;
648 const auto shift = [](std::vector< _Slot_ > v) {
660 std::vector< _Slot_ > prev;
661 for (
int t = 0; t <=
_k_; ++t) {
664 prev = shift(std::move(cur));
668 template < GUM_Numeric GUM_SCALAR >
670 std::vector< bool > req(
_baseNames_.size(),
false);
671 std::vector< int > stack;
673 const auto push = [&](
const std::string& b) {
676 if (!req[it->second]) {
677 req[it->second] =
true;
678 stack.push_back(it->second);
696 while (!stack.empty()) {
697 const int i = stack.back();
701 for (
int t = 0; t <=
_k_ - 1; ++t)
702 for (
const auto& [pb, ps]:
_ktbn_->parents(b, t)) {
724 template < GUM_Numeric GUM_SCALAR >
730 const std::pair< std::string, int > key{p,
static_cast< int >(t %
_k_)};
740 Tensor< GUM_SCALAR > kernel;
741 std::vector< std::string > srcNames;
746 for (
const auto& [parBase, parSlice]:
_ktbn_->parents(p,
_k_ - 1)) {
749 srcNames.push_back(parBase);
751 const int lag = (
_k_ - 1) - parSlice;
752 kernel <<
_ktbn_->variable(parBase, (t - lag) %
_k_);
753 srcNames.push_back(
_encode_(parBase, parSlice));
756 kernel.fillWith(
_ktbn_->cpt(p,
_k_ - 1), srcNames);
757 return _kernelCache_.emplace(key, std::move(kernel)).first->second;
760 template < GUM_Numeric GUM_SCALAR >
764 std::unordered_map<
NodeId, Tensor< GUM_SCALAR > >& psi)
const {
765 for (
const auto& [c, bases]: w.
factorsOf)
766 for (
const int b: bases) {
770 Tensor< GUM_SCALAR > ev;
772 ev.fillWith(ite->second);
777 template < GUM_Numeric GUM_SCALAR >
782 return (t <
static_cast< int >(
_k_)) ?
static_cast< Size >(t)
783 :
_k_ +
static_cast< Size >(t %
static_cast< int >(
_k_));
786 template < GUM_Numeric GUM_SCALAR >
787 const std::unordered_map< NodeId, Tensor< GUM_SCALAR > >&
812 template < GUM_Numeric GUM_SCALAR >
816 std::unordered_map<
NodeId, Tensor< GUM_SCALAR > >& psi,
817 bool withTemporalEvidence)
const {
823 Tensor< GUM_SCALAR > p;
826 p.fillWith(GUM_SCALAR(1));
827 psi.emplace(c, std::move(p));
830 const auto applyBase = [&](
int b,
NodeId owner) {
832 const bool atemp = b >=
static_cast< int >(
_nbTemporal_);
842 const bool sliceEvidence = atemp || withTemporalEvidence;
849 }
else if (t <=
_k_ - 1) {
850 psi.at(owner) *=
_ktbn_->cpt(base, t);
858 Tensor< GUM_SCALAR > ev;
860 ev.fillWith(ite->second);
865 for (
const auto& [c, bases]: w.
factorsOf)
866 for (
const int b: bases)
870 template < GUM_Numeric GUM_SCALAR >
875 template < GUM_Numeric GUM_SCALAR >
877 return _ktbn_->temporalVarNames().find(base) !=
_ktbn_->temporalVarNames().end();
880 template < GUM_Numeric GUM_SCALAR >
882 return _ktbn_->atemporalVarNames().find(base) !=
_ktbn_->atemporalVarNames().end();
885 template < GUM_Numeric GUM_SCALAR >
891 template < GUM_Numeric GUM_SCALAR >
894 const std::unordered_map<
NodeId, Tensor< GUM_SCALAR > >& psi,
895 const std::map< std::pair< NodeId, NodeId >, Tensor< GUM_SCALAR > >& msgs,
896 const Tensor< GUM_SCALAR >* inPrev,
897 const Tensor< GUM_SCALAR >* inNext,
900 Tensor< GUM_SCALAR > out = psi.at(c);
902 if (n == skip)
continue;
903 const auto it = msgs.find({n, c});
904 if (it != msgs.end()) out *= it->second;
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;
913 template < GUM_Numeric GUM_SCALAR >
917 const std::unordered_map<
NodeId, Tensor< GUM_SCALAR > >& psi,
918 const Tensor< GUM_SCALAR >* inPrev,
919 const Tensor< GUM_SCALAR >* inNext,
921 std::map< std::pair< NodeId, NodeId >, Tensor< GUM_SCALAR > >& msgs)
const {
923 if (w.
bfs.empty())
return;
933 for (
auto it = w.
bfs.rbegin(); it != w.
bfs.rend(); ++it) {
935 if (j == w.
rootC)
continue;
937 msgs[{j, p}] =
_belief_(w, psi, msgs, inPrev, inNext, j, p).sumIn(sepVars(j, p));
939 if (!distribute)
return;
944 if (i == j || w.
parentOf.at(i) != j)
continue;
945 msgs[{j, i}] =
_belief_(w, psi, msgs, inPrev, inNext, j, i).sumIn(sepVars(j, i));
953 template < GUM_Numeric GUM_SCALAR >
957 const int T =
static_cast< int >(nbTimeSlices);
987 const auto targeted = [&](
const std::string& b) {
return defaultAll ||
_targets_.contains(b); };
993 for (
int i = 0; i < static_cast< int >(
_baseNames_.size()); ++i) {
998 = (i < static_cast< int >(
_nbTemporal_)) ?
static_cast< std::size_t
>(T) : std::size_t(1);
1003 const auto ifaceVars = [&](
const std::vector< _Slot_ >& slots,
int slice) {
1005 for (
const auto& s: slots)
1010 const auto readPosteriors
1013 const std::unordered_map< NodeId, Tensor< GUM_SCALAR > >& psi,
1014 const std::map< std::pair< NodeId, NodeId >, Tensor< GUM_SCALAR > >& msgs,
1015 const Tensor< GUM_SCALAR >* inPrev,
1016 const Tensor< GUM_SCALAR >* inNext) {
1017 const int lastBase = (slice == 0) ?
static_cast< int >(
_baseNames_.size())
1019 for (
int i = 0; i < lastBase; ++i) {
1023 const bool atemp = i >=
static_cast< int >(
_nbTemporal_);
1027 Tensor< GUM_SCALAR > m
1028 =
_belief_(w, psi, msgs, inPrev, inNext, itc->second, KTBN_SKIP_NONE).sumIn(keep);
1034 std::map< std::pair< NodeId, NodeId >, Tensor< GUM_SCALAR > > msgs;
1038 std::vector< Tensor< GUM_SCALAR > > fwd;
1039 if (smoothing && T > 1) fwd.resize(
static_cast< std::size_t
>(T - 1));
1041 Tensor< GUM_SCALAR > prev;
1042 bool hasPrev =
false;
1044 for (
int t = 0; t < T; ++t) {
1046 if (w.
bfs.empty())
break;
1049 _propagate_(w, t, psi, hasPrev ? &prev :
nullptr,
nullptr, !smoothing, msgs);
1051 if (!smoothing) readPosteriors(w, t, psi, msgs, hasPrev ? &prev :
nullptr,
nullptr);
1053 Tensor< GUM_SCALAR > m
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 "
1062 m.scale(GUM_SCALAR(1) / mass);
1063 _logObservation_ +=
static_cast< GUM_SCALAR
>(std::log(
static_cast< double >(mass)));
1066 if (smoothing) fwd[
static_cast< std::size_t
>(t)] = m;
1067 prev = std::move(m);
1074 Tensor< GUM_SCALAR > nxt;
1075 bool hasNext =
false;
1076 for (
int t = T - 1; t >= 0; --t) {
1078 if (w.
bfs.empty())
break;
1081 const Tensor< GUM_SCALAR >* inPrev
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);
1086 readPosteriors(w, t, psi, msgs, inPrev, inNext);
1089 Tensor< GUM_SCALAR > r =
_belief_(w, psi, msgs, inPrev, inNext, w.
rootD, KTBN_SKIP_PREV)
1090 .sumIn(ifaceVars(w.
Iprev, t));
1091 const GUM_SCALAR mass = r.sum();
1106 if (!(mass > GUM_SCALAR(0)))
1108 "makeInference: the backward message leaving slice "
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);
1130 template < GUM_Numeric GUM_SCALAR >
1133 const Tensor< GUM_SCALAR >& marginal) {
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);
1143 std::unique_ptr< DiscreteVariable > outVar(
_templateVar_(base, slice).clone());
1144 outVar->setName(
_encode_(base, slice));
1146 Tensor< GUM_SCALAR > out;
1148 out.fillWith(marginal, {marginal.variablesSequence().atPos(0)->name()});
1150 series.
vars[idx] = std::move(outVar);
1151 series.
tensors[idx] = std::move(out);
1154 template < GUM_Numeric GUM_SCALAR >
1169 template < GUM_Numeric GUM_SCALAR >
1172 const std::string b(base);
1178 return series.
tensors.front();
1180 if (slice < 0 ||
static_cast< Size >(slice) >= series.
tensors.size())
1182 "Slice " << slice <<
" for '" << b <<
"' was not computed (horizon " <<
_horizon_
1184 return series.
tensors[
static_cast< std::size_t
>(slice)];
1187 template < GUM_Numeric GUM_SCALAR >
1193 template < GUM_Numeric GUM_SCALAR >
1194 const std::vector< Tensor< GUM_SCALAR > >&
1199 template < GUM_Numeric GUM_SCALAR >
1209 template < GUM_Numeric GUM_SCALAR >
1218 template < GUM_Numeric GUM_SCALAR >
1223 template < GUM_Numeric GUM_SCALAR >
1228 template < GUM_Numeric GUM_SCALAR >
1230 return static_cast< Size >(
_windows_[
static_cast< std::size_t
>(
_k_)].Icur.size());
1233 template < GUM_Numeric GUM_SCALAR >
1235 std::stringstream s;
1236 s <<
"KTBNInference (k=" <<
_k_ <<
", interface=" <<
interfaceSize() <<
")\n";
1237 s <<
" interventions: {";
1240 s << (first ?
"" :
", ") <<
"do(" << name <<
"=" << val <<
")";
1243 s <<
"}\n observations: {";
1247 s << (first ?
"" :
", ") << name;
1250 s <<
"}\n targets: ";
1256 s << (first ?
"" :
", ") << name;
1261 s <<
"\n state: " << (
_done_ ?
"computed" :
"not computed yet") <<
"\n";
Exact inference for k-order dynamic Bayesian networks: Murphy's interface algorithm,...
const NodeSet & separator(const Edge &edge) const
returns the separator included in a given edge
void addEdge(NodeId first, NodeId second) override
inserts a new edge between two cliques
const NodeSet & clique(const NodeId idClique) const
returns the set of nodes included into a given clique
The default triangulation algorithm used by aGrUM.
Base class for discrete random variable.
virtual Size domainSize() const =0
const NodeSet & neighbours(NodeId id) const
returns the set of node neighbours to a given node
Exception : fatal (unknown ?) error.
value_type & insert(const Key &key, const Val &val)
Adds a new element (actually a copy of this element) into the hash table.
Exception: at least one argument passed to a function is not what was expected.
void _fillWindow_(const _Window_ &w, int t, std::unordered_map< NodeId, Tensor< GUM_SCALAR > > &psi, bool withTemporalEvidence=true) const
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 makeInference(Size nbTimeSlices)
Runs the interface algorithm over nbTimeSlices slices ( ) and caches, for every targeted base,...
void addObservation(std::string_view base, int slice, const KTBNModality &value)
Records a hard observation .
KTBNInference(const KTBN< GUM_SCALAR > *ktbn)
Constructor.
void _markRequisite_()
Marks the requisite bases of the current run (targets, observed bases and all their ancestors) into r...
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 ...
void clearObservation()
Removes all recorded observations.
std::vector< bool > _psiCached_
std::unordered_map< NodeId, Tensor< GUM_SCALAR > > _psiScratch_
Potentials of an evidence-carrying slice, rebuilt on each visit.
void eraseIntervention(std::string_view base, int slice)
Removes a recorded intervention (silent no-op if absent).
bool isInTargetMode() const
Size _horizon_
Horizon (nbTimeSlices) of the last/next run; 0 <=> makeInference never run.
const DiscreteVariable & _templateVar_(const std::string &base, int slice) const
A representative template variable of base for domain/cloning.
std::vector< bool > _requisite_
Bases actually folded by the current run: the targets, the observed nodes and all their ancestors....
void clearTargets()
Removes all targets (restores default-all-targets mode).
void addTarget(std::string_view base)
Declares a target: a base variable whose marginals we want.
GUM_SCALAR _logObservation_
log P(observation | do) of the last run.
std::unordered_set< int > _observationSlices_
Slices carrying a temporal observation. Their potentials are the periodic ones times that slice's lik...
GUM_SCALAR logObservationProbability()
for the last run: the likelihood of the observations under the (possibly mutilated) model....
_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::set< std::string > _targets_
Recorded targets (base names). Empty <=> default-all-targets mode.
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.
std::vector< int > _maxLag_
maxLag[i]: largest lag at which the transition kernel still consumes temporal base i – how long an oc...
std::vector< std::string > _baseNames_
All bases: temporal first (indices 0.._nbTemporal_-1), then atemporal. Window slots index into this.
bool _targeted_mode_
Whether at least one explicit target has been declared.
std::unordered_map< std::string, _Series_ > _posteriors_
Cached marginal series of the last run, keyed by base name.
const JunctionTree & windowJunctionTree() const
The junction tree of the repeating window – the one compiled from the k-slice template and re-entered...
void addObservations(const std::vector< std::pair< NodeKey, KTBNModality > > &observations)
Records several observations in one call, all-or-nothing.
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 _validateNode_(const std::string &base, int slice) const
Validates that (base, slice) denotes a legal node (future slices ok).
void clearInterventions()
Removes all recorded interventions.
bool hasIntervention(std::string_view base, int slice) const
const KTBN< GUM_SCALAR > & ktbn() const
Size interfaceSize() const
Size of the forward interface of the repeating window: how many node occurrences have to cross each s...
std::string toString() const
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)....
static std::string _encode_(const std::string &base, int slice)
Encodes (base, slice) -> engine name (base[slice] or bare base).
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 KTBN< GUM_SCALAR > * _ktbn_
The k-DBN (referenced, not owned).
bool isTarget(std::string_view base) const
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,...
GUM_SCALAR observationProbability()
, i.e. exp of logObservationProbability(). Underflows to 0 on long horizons; prefer the log form.
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...
Size _psiKey_(int t) const
Cache slot for slice t: the initial slices keep their own, the repeating window contributes one per p...
void eraseObservation(std::string_view base, int slice)
Removes a recorded observation (silent no-op if absent).
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 _Series_ & _series_(const std::string &base)
The cached series of a targeted base, running makeInference() lazily (with the last horizon) if out o...
std::vector< _Window_ > _windows_
The compiled windows: index t for t <= k-2 (initial), index k-1 for the repeating window,...
bool _done_
Whether the cached posteriors are up to date.
void addIntervention(std::string_view base, int slice, const KTBNModality &value)
Records a hard intervention .
void eraseTarget(std::string_view base)
Removes a target; when the last one is removed, default-all-targets mode is restored.
std::unordered_set< int > _interventionSlices_
Slices carrying a temporal intervention. These need a full rebuild: do(X=x) replaces the node's CPT,...
std::vector< std::string > _atemporalSorted_
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...
void _buildWindows_()
Compiles the k window junction trees, once, from the constructor: moralise each window's families,...
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)....
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....
std::map< std::pair< std::string, int >, Tensor< GUM_SCALAR > > _kernelCache_
Memoized transition kernels, keyed by (process, t % k).
int _k_
The order k, cached as int for slice arithmetic.
const std::vector< Tensor< GUM_SCALAR > > & posteriors(std::string_view base)
The whole marginal time-series of a targeted base: tensors[t] is for (a single-element vector,...
bool _isTemporal_(const std::string &base) const
std::vector< std::string > _temporalSorted_
Temporal / atemporal base names in a deterministic (sorted) order, cached once at construction (the K...
std::map< std::string, std::vector< GUM_SCALAR > > _observations_
Recorded observations, keyed by engine name -> likelihood vector (one-hot for a hard observation).
bool hasObservation(std::string_view base, int slice) const
bool hasObservation() const
std::unordered_map< std::string, int > _baseIdx_
name -> index into baseNames
static constexpr int ATEMPORAL
Convenience alias for the atemporal-slice sentinel.
const Tensor< GUM_SCALAR > & posterior(std::string_view base, int slice)
Returns .
bool _isAtemporal_(const std::string &base) const
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...
Size size() const
alias for sizeNodes
const NodeGraphPart & nodes() const
return *this as a NodeGraphPart
Exception : the element we looked for cannot be found.
Exception : operation not allowed.
Exception : out of bound.
bool contains(const Key &k) const
Indicates whether a given elements belong to the set.
void insert(const Key &k)
Inserts a new element into the set.
const CliqueGraph & junctionTree() override
returns a compatible junction tree
static Tensor< GUM_SCALAR > deterministicTensor(const DiscreteVariable &var, Idx value)
Exception : a looked-for element could not be found.
Base class for undirected graphs.
#define GUM_ERROR(type, msg)
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Size Idx
Type for indexes.
Size NodeId
Type for node ids.
HashTable< NodeId, VAL > NodeProperty
Property on graph elements.
Set< NodeId > NodeSet
Some typdefs and define for shortcuts ...
gum is the global namespace for all aGrUM entities
Set< const DiscreteVariable * > VariableSet
CliqueGraph JunctionTree
a junction tree is a clique graph satisfying the running intersection property and such that no cliqu...
A cached marginal time-series for one base: owned variable descriptors paired with their marginals,...
std::vector< std::unique_ptr< DiscreteVariable > > vars
std::vector< Tensor< GUM_SCALAR > > tensors
One node of a window template: a base (index into baseNames) at a lag behind the window's current sli...
bool operator==(const _Slot_ &o) const
A compiled window: the junction tree of , rooted at the clique holding , plus everything needed to fi...
std::unordered_map< NodeId, NodeId > parentOf
NodeId rootC
clique containing the whole outgoing interface I_t
std::unordered_map< NodeId, std::vector< int > > factorsOf
clique -> base indices whose family factor is multiplied in there
std::vector< _Slot_ > Icur
std::vector< _Slot_ > Iprev
the two interfaces, as slot lists
NodeId rootD
clique containing the whole incoming interface I_{t-1}
JunctionTree jt
the junction tree over those nodes
std::vector< _Slot_ > slotOfNode
template graph NodeId -> slot it stands for
std::vector< NodeId > bfs
cliques in BFS order from rootC, and each one's parent in that rooting
std::unordered_map< int, NodeId > selfClique
base index -> clique holding that base's own slot (lag 0 / atemporal), for reading its posterior and ...
A parent's value in gum::KTBN::fillCPT(): a modality index or a modality label.
Idx toIndex(const DiscreteVariable &var) const
Resolves this value to a modality index of var, looking the label up when one was given.