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

Class representing a k-order dynamic Bayesian network (k-DBN). More...

#include <agrum/KTBN/KTBN.h>

Public Member Functions

Constructors and Destructor
 KTBN (Size k=2)
 Default constructor.
virtual ~KTBN ()
 Destructor.
 KTBN (const KTBN< GUM_SCALAR > &source)
 Copy constructor.
 KTBN (KTBN< GUM_SCALAR > &&source) noexcept
 Move constructor.
KTBN< GUM_SCALAR > & operator= (const KTBN< GUM_SCALAR > &source)
 Copy assignment operator.
KTBN< GUM_SCALAR > & operator= (KTBN< GUM_SCALAR > &&source) noexcept
 Move assignment operator.
Accessors
Size k () const
Size size () const
Size sizeArcs () const
bool empty () const
void clear ()
 Removes all variables and arcs, keeping the order \(k\).
Variable management
void add (const DiscreteVariable &var, bool temporal=true)
 Adds a variable to the k-DBN.
void add (std::string_view fast_description, bool temporal=true, unsigned int default_nbrmod=2)
 Adds a variable using the "fast" textual description syntax.
void addTemporal (const DiscreteVariable &var)
 Convenience shortcut for add(var, true).
void addAtemporal (const DiscreteVariable &var)
 Convenience shortcut for add(var, false).
void addTemporal (std::string_view fast_description, unsigned int default_nbrmod=2)
 Convenience shortcut for add(fast_description, true, default_nbrmod).
void addAtemporal (std::string_view fast_description, unsigned int default_nbrmod=2)
 Convenience shortcut for add(fast_description, false, default_nbrmod).
void erase (std::string_view base)
 Removes a variable and all its incident arcs.
void changeVariableName (std::string_view oldBase, std::string_view newBase)
 Renames a variable (temporal process or atemporal variable).
Variable queries
bool exists (std::string_view base) const
const std::unordered_set< std::string > & temporalVarNames () const
const std::unordered_set< std::string > & atemporalVarNames () const
Size nbTemporalVars () const
Size nbAtemporalVars () const
std::vector< std::pair< std::string, int > > nodes () const
std::vector< std::pair< std::string, int > > parents (std::string_view base, int slice) const
 Parents of a node as (base, slice) pairs (ATEMPORAL if atemporal).
std::vector< std::pair< std::string, int > > parents (std::string_view node_name) const
 Returns the parents using an engine name ("X[1]", "C", …).
std::vector< std::pair< std::string, int > > children (std::string_view base, int slice) const
 Children of a node as (base, slice) pairs (ATEMPORAL if atemporal).
std::vector< std::pair< std::string, int > > children (std::string_view node_name) const
 Returns the children using an engine name ("X[1]", "C", …).
const DiscreteVariable & variable (std::string_view base, int slice) const
 Returns the gum::DiscreteVariable of a (process, slice) couple.
const DiscreteVariable & variable (std::string_view node_name) const
 Same, using an engine name: resolved via _determineNode_, so "X[1]" and bare "C" are both accepted.
int timeSlice (const DiscreteVariable &var) const
 The time slice of var, or ATEMPORAL if it is atemporal.
std::string baseName (const DiscreteVariable &var) const
 Returns the base name (without bracket encoding) of var.
Arc management
void addArc (std::string_view tailBase, int tailSlice, std::string_view headBase, int headSlice)
 Adds an arc between two (process, slice) endpoints.
void addArc (std::string_view tail, std::string_view head)
 Adds an arc using internal engine names (bracket notation: "X[0]", "C", …).
void eraseArc (std::string_view tailBase, int tailSlice, std::string_view headBase, int headSlice)
 Removes an arc between two (process, slice) endpoints.
void eraseArc (std::string_view tail, std::string_view head)
 Removes an arc using internal engine names (bracket notation).
bool existsArc (std::string_view tailBase, int tailSlice, std::string_view headBase, int headSlice) const
bool existsArc (std::string_view tail, std::string_view head) const
std::vector< std::pair< std::pair< std::string, int >, std::pair< std::string, int > > > arcs () const
Conditional probability tables
const Tensor< GUM_SCALAR > & cpt (std::string_view base, int slice) const
 Returns the CPT of a (process, slice) couple.
const Tensor< GUM_SCALAR > & cpt (std::string_view node_name) const
 Returns the CPT using an engine name ("X[1]", "C", …).
void fillCPT (std::string_view base, int slice, const std::map< std::pair< std::string, int >, KTBNModality > &parents, const std::vector< GUM_SCALAR > &distribution) const
 Fills one conditional distribution P(node | parent configuration).
void fillCPT (std::string_view node_name, const std::map< std::variant< std::string, std::pair< std::string, int > >, KTBNModality > &parents, const std::vector< GUM_SCALAR > &distribution) const
 Fills one conditional distribution using internal bracket-notation engine name for the target node — the bracket notation visible in toDot() and toString(): "X[t]" for a temporal variable at slice t, and the bare variable name for an atemporal variable (e.g.
void generateCPTs () const
 Randomly generates every CPT of the template.
void generateCPT (std::string_view base, int slice) const
 Randomly generates the CPT of a single node.
void generateCPT (std::string_view node_name) const
 Same, using an engine name ("X[1]", "C", …).
Transformations
BayesNet< GUM_SCALAR > toBN () const
BayesNet< GUM_SCALAR > unroll (Size nbTimeSlices) const
 Unrolls the k-DBN into a standard gum::BayesNet.
Various
std::string toString () const
std::string toDot () const
 Returns a Graphviz DOT string with one cluster per time slice.
std::string toUnrolledDot (Size T, bool highlightReplicated=false) const
 Returns a Graphviz DOT string of the k-DBN unrolled over T time slices.
std::string bnToDot () const
 Returns the Graphviz DOT string of the underlying storage BayesNet.
std::string summaryGraph () const
 Returns the Graphviz DOT string of the summary graph: the projection of the transition kernel alone (the pattern that actually repeats through time when unrolling), not the whole template.

Static Public Attributes

static constexpr int ATEMPORAL = -1
 Conventional time-slice value denoting an atemporal (static) variable.

Private Member Functions

std::string _encode_ (std::string_view base, int slice) const
 Encodes (base, slice) → engine name: base[t], or base if atemporal.
std::pair< std::string, int > _decodeName_ (std::string_view name) const
 Purely syntactic parse of an engine name → (base, slice). Slice is ATEMPORAL when there is no [digits] suffix. Does not consult the cached name sets, so a bracket-named atemporal node decodes as temporal here.
std::pair< std::string, int > _determineNode_ (const std::string &name) const
 Cache-aware classification of a node name → (base, slice): nodes registered in _atemporal_ (atemporal and orphan-bracket) map to ATEMPORAL, every other name is parsed by decodeName.
std::vector< std::pair< std::string, int > > _determineNodeSet_ (const NodeSet &ids) const
 Maps a set of node ids to (base, slice) pairs (via determineNode).
NodeId _validateVariable_ (std::string_view base, int slice) const
 Resolves and validates a (base, slice) endpoint into its NodeId.
void _validateAdd_ (const std::string &base, bool temporal) const
 Checks that a variable named base can be added.
void _determineNodesFromBN_ (const std::unordered_set< std::string > &atemporalNodes, std::vector< std::string > *warnings)
 Rebuilds the cached name sets from the storage engine content (used by fromBN()/load(); decodes names once).
std::string _timeSlicesToDot_ (const BayesNet< GUM_SCALAR > &bn, bool highlightReplicated) const
 Renders bn as time-slice-clustered DOT. Shared engine behind toDot() (on _bn_) and toUnrolledDot() (on unroll(T)).

Static Private Member Functions

static std::pair< std::string, bool > _resolveGumFormat_ (std::string_view filename)
 Resolves a user filename to (filepath, binary): ensures a .jgum/.bgum extension (.bgum appended by default) and reports whether the format is binary. Single source of truth for the GUM format convention shared by save() and load().
static std::string _escapeDot_ (std::string_view name)
 Escapes double quotes for a DOT identifier or label. Shared by timeSlicesToDot() and summaryGraph().

Private Attributes

Size _k_
 The order (number of time slices in the template).
BayesNet< GUM_SCALAR > _bn_
 The underlying Bayesian network used as a storage engine for the template.
std::unordered_set< std::string > _temporal_
 Base names of the registered temporal processes.
std::unordered_set< std::string > _atemporal_
 Base names of the registered atemporal variables.

Persistence and conversion

void save (std::string_view filename) const
 Saves the template in the GUM format (text .jgum or binary .bgum).
static KTBN< GUM_SCALAR > load (std::string_view filename)
 Loads a k-DBN from a GUM file produced by save().
static KTBN< GUM_SCALAR > fromBN (const BayesNet< GUM_SCALAR > &bn, const std::unordered_set< std::string > &atemporalNodes={}, std::vector< std::string > *warnings=nullptr)
 Builds a k-DBN from an existing gum::BayesNet, reading its node names under one of two mutually exclusive conventions.

Detailed Description

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

Class representing a k-order dynamic Bayesian network (k-DBN).

A k-order dynamic Bayesian network (k-DBN, sometimes called a k-Time-slice Bayesian Network or k-TBN) generalizes the classical 2-TBN: the conditional distribution of a variable at time \(t\) may depend on the \(k\) most recent time slices \(t, t-1, \ldots, t-k+1\) instead of a single step backward.

Rather than storing an (infinite) unrolled network, a KTBN only stores a template made of exactly \(k\) time slices. This template captures both:

  • the initial distribution, encoded by slices \(0, \ldots, k-2\) (the initial slices, where the full \(k\)-history is not yet available), and
  • the transition kernel, encoded by slice \(k-1\) as a function of slices \(0, \ldots, k-1\). Because the process is time-homogeneous, this kernel is reused for every \(t \geq k-1\) when unrolling.

Two kinds of random variables are distinguished:

  • temporal variables (also called processes here), which evolve through time and are therefore represented by \(k\) instances (one per slice) in the template;
  • atemporal variables, which are constant through time (e.g. a static context parameter) and are represented by a single instance.
Node identity: the bracket notation
The identity of a node in the underlying engine is its name. Temporal nodes are stored using bracket notation: the \(t\)-th instance of a process base is named base[t] (e.g. "X[0]", "X[1]", ...). Atemporal variables keep their bare name. The class holds no gum::NodeId index table: a (process, slice) couple is resolved to its node by encoding it and asking the engine, and a node's slice is recovered by decoding its name. The public API is always expressed in terms of (process, slice) couples; the bracket encoding is an internal detail.
Internal storage
Structure and parameters are stored in an underlying gum::BayesNet (the template) used purely as a storage engine, so that gum::Tensor CPTs, cycle detection, topological ordering and the existing I/O readers/writers are reused as-is. The gum::BayesNet is the single source of truth; the class only caches two name sets (the temporal processes and the atemporal variables) for fast O(1) membership queries. These sets are std::unordered_set, so their iteration order is unspecified; methods that need a deterministic order must sort explicitly.
Name collision rule
All base names must be globally unique: a name cannot be used for both a temporal process and an atemporal variable simultaneously. In addition, an atemporal variable named "X[0]" is forbidden when a temporal process "X" exists (and vice-versa), because both would map to the same BN node "X[0]". The reservation covers every bracket-suffixed name over an existing process, not only the slices that process actually holds: "X[999]" is refused for \(k=3\) as well, since the slice index is parsed without an upper bound and such a name would otherwise shadow the process. add() and changeVariableName() enforce these rules and throw DuplicateLabel or InvalidArgument on violation.
Arc semantics
Arcs must respect temporal causality. For an arc \(u \rightarrow v\):
  • if v is atemporal, then u must be atemporal too (a static variable cannot depend on a time-varying one);
  • if v is temporal at slice \(t_v\) and u is temporal at slice \(t_u\), then \(t_u \leq t_v\) (no arc from the future to the past). The lag of the arc is \(t_v - t_u \in [0, k-1]\);
  • an atemporal u may point to a temporal v at any slice.
Warning
Unlike the Python prototype, unroll() correctly handles arcs of arbitrary lag (e.g. an arc X[0] -> X[2] for \(k=3\)), not only arcs between consecutive slices.

Definition at line 197 of file KTBN.h.

Constructor & Destructor Documentation

◆ KTBN() [1/3]

template<GUM_Numeric GUM_SCALAR>
gum::KTBN< GUM_SCALAR >::KTBN ( Size k = 2)
explicit

Default constructor.

Parameters
kThe order of the k-DBN, i.e. the number of time slices stored in the template. Must be \(\geq 1\).
Exceptions
InvalidArgumentif k is 0.

Definition at line 131 of file KTBN_tpl.h.

131 : _k_(k) {
132 if (k == 0) GUM_ERROR(InvalidArgument, "A k-DBN must have an order k >= 1.")
134 }
Class representing a k-order dynamic Bayesian network (k-DBN).
Definition KTBN.h:197
KTBN(Size k=2)
Default constructor.
Definition KTBN_tpl.h:131
Size _k_
The order (number of time slices in the template).
Definition KTBN.h:704
Size k() const
Definition KTBN_tpl.h:184

References KTBN(), _k_, GUM_ERROR, and k().

Referenced by KTBN(), KTBN(), KTBN(), ~KTBN(), operator=(), and operator=().

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

◆ ~KTBN()

template<GUM_Numeric GUM_SCALAR>
gum::KTBN< GUM_SCALAR >::~KTBN ( )
virtual

Destructor.

Definition at line 137 of file KTBN_tpl.h.

137 {
139 }

References KTBN().

Here is the call graph for this function:

◆ KTBN() [2/3]

template<GUM_Numeric GUM_SCALAR>
gum::KTBN< GUM_SCALAR >::KTBN ( const KTBN< GUM_SCALAR > & source)

Copy constructor.

Definition at line 142 of file KTBN_tpl.h.

142 :
146 }
BayesNet< GUM_SCALAR > _bn_
The underlying Bayesian network used as a storage engine for the template.
Definition KTBN.h:707
std::unordered_set< std::string > _temporal_
Base names of the registered temporal processes.
Definition KTBN.h:710
std::unordered_set< std::string > _atemporal_
Base names of the registered atemporal variables.
Definition KTBN.h:713

References KTBN(), _atemporal_, _bn_, _k_, and _temporal_.

Here is the call graph for this function:

◆ KTBN() [3/3]

template<GUM_Numeric GUM_SCALAR>
gum::KTBN< GUM_SCALAR >::KTBN ( KTBN< GUM_SCALAR > && source)
noexcept

Move constructor.

Definition at line 149 of file KTBN_tpl.h.

References KTBN(), _atemporal_, _bn_, _k_, and _temporal_.

Here is the call graph for this function:

Member Function Documentation

◆ _decodeName_()

template<GUM_Numeric GUM_SCALAR>
std::pair< std::string, int > gum::KTBN< GUM_SCALAR >::_decodeName_ ( std::string_view name) const
private

Purely syntactic parse of an engine name → (base, slice). Slice is ATEMPORAL when there is no [digits] suffix. Does not consult the cached name sets, so a bracket-named atemporal node decodes as temporal here.

Definition at line 84 of file KTBN_tpl.h.

84 {
85 const std::size_t bracketPos = name.rfind('[');
87
89 if (bracketContent.empty() || bracketContent.back() != ']')
90 return {std::string{name}, ATEMPORAL};
91
93 if (digits.empty()) return {std::string{name}, ATEMPORAL};
94 for (const char c: digits)
95 if (std::isdigit(static_cast< unsigned char >(c)) == 0) return {std::string{name}, ATEMPORAL};
96
97 int slice{};
98 try {
100 } catch (const std::out_of_range&) {
102 "Node name '" << name << "' has a slice index too large to represent as int.")
103 }
104 return {std::string{name.substr(0, bracketPos)}, slice};
105 }
bool empty() const
Definition KTBN_tpl.h:199
static constexpr int ATEMPORAL
Conventional time-slice value denoting an atemporal (static) variable.
Definition KTBN.h:200
Size size() const
Definition KTBN_tpl.h:189

References ATEMPORAL, and GUM_ERROR.

Referenced by _determineNode_(), _determineNodesFromBN_(), _timeSlicesToDot_(), _validateAdd_(), changeVariableName(), and summaryGraph().

Here is the caller graph for this function:

◆ _determineNode_()

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

Cache-aware classification of a node name → (base, slice): nodes registered in _atemporal_ (atemporal and orphan-bracket) map to ATEMPORAL, every other name is parsed by decodeName.

Definition at line 109 of file KTBN_tpl.h.

109 {
110 // Atemporal and orphan-bracket nodes are registered in _atemporal_ and map to ATEMPORAL;
111 // any other name is a temporal "base[t]" parsed by _decodeName_.
112 if (_atemporal_.contains(name)) return {name, ATEMPORAL};
113 return _decodeName_(name);
114 }
std::pair< std::string, int > _decodeName_(std::string_view name) const
Purely syntactic parse of an engine name → (base, slice). Slice is ATEMPORAL when there is no [digits...
Definition KTBN_tpl.h:84

References _atemporal_, _decodeName_(), and ATEMPORAL.

Referenced by _determineNodeSet_(), _determineNodesFromBN_(), addArc(), arcs(), baseName(), children(), cpt(), eraseArc(), existsArc(), generateCPT(), parents(), timeSlice(), and variable().

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

◆ _determineNodeSet_()

template<GUM_Numeric GUM_SCALAR>
std::vector< std::pair< std::string, int > > gum::KTBN< GUM_SCALAR >::_determineNodeSet_ ( const NodeSet & ids) const
private

Maps a set of node ids to (base, slice) pairs (via determineNode).

Definition at line 118 of file KTBN_tpl.h.

118 {
120 result.reserve(ids.size());
121 for (const NodeId id: ids)
122 result.push_back(_determineNode_(_bn_.variable(id).name()));
123 return result;
124 }
std::pair< std::string, int > _determineNode_(const std::string &name) const
Cache-aware classification of a node name → (base, slice): nodes registered in _atemporal_ (atemporal...
Definition KTBN_tpl.h:109

References _bn_, _determineNode_(), and gum::Set< Key >::size().

Referenced by children(), and parents().

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

◆ _determineNodesFromBN_()

template<GUM_Numeric GUM_SCALAR>
void gum::KTBN< GUM_SCALAR >::_determineNodesFromBN_ ( const std::unordered_set< std::string > & atemporalNodes,
std::vector< std::string > * warnings )
private

Rebuilds the cached name sets from the storage engine content (used by fromBN()/load(); decodes names once).

Definition at line 890 of file KTBN_tpl.h.

892 {
893 _temporal_.clear();
894 _atemporal_.clear();
895
896 // a declared name must be a node of the BN: checked before any mutation, so
897 // a typo cannot leave the object half-built
898 for (const std::string& name: atemporalNodes)
899 if (!_bn_.exists(name))
900 GUM_ERROR(NotFound, "fromBN: '" << name << "' is not a node of the BN.")
901
902 const auto warn = [warnings](const std::string& message) {
903 if (warnings != nullptr) warnings->push_back(message);
904 };
905
906 // fromBN()'s bracket-free convention: a name ending in a run of digits
907 // denotes a temporal variable at the timeslice given by that integer (base =
908 // everything before the run); a name with no trailing digit is atemporal.
909 // Purely syntactic, like _decodeName_, but that one expects the engine's own
910 // base[t] convention. Used below only when the source BN carries no bracket
911 // at all (see hasBracket).
914 while (pos > 0 && std::isdigit(static_cast< unsigned char >(name[pos - 1])))
915 --pos;
916 if (pos == name.size()) return {std::string{name}, ATEMPORAL};
917
918 const std::string_view digits = name.substr(pos);
919 int slice{};
920 try {
922 } catch (const std::out_of_range&) {
924 "Node name '" << name << "' has a slice index too large to represent as int.")
925 }
926 return {std::string{name.substr(0, pos)}, slice};
927 };
928
929 // The two conventions never mix within one graph: if any node name already
930 // carries the engine's own base[t] bracket notation, the WHOLE graph is read
931 // that way (legacy behaviour -- what KTBNLearner and toBN() round-trips
932 // produce); only when no node carries a bracket at all does every node get
933 // read via the trailing-integer convention above. Nodes declared in
934 // atemporalNodes are skipped here: their shape says nothing about the rest
935 // of the graph's convention, so a bracket-shaped one (e.g. an orphan
936 // "Y[0]" named atemporal on purpose, see below) must not force
937 // bracket-reading onto otherwise bracket-free temporal nodes.
938 bool hasBracket = false;
939 for (const NodeId n: _bn_.nodes()) {
940 const std::string& name = _bn_.variable(n).name();
941 if (atemporalNodes.contains(name)) continue;
943 hasBracket = true;
944 break;
945 }
946 }
947
948 // first pass: determine every node, collecting the slices seen per process
949 std::vector< std::string > discovered; // temporal bases, in order
951 int maxSlice = -1;
952
953 for (const NodeId n: _bn_.nodes()) {
954 const std::string& name = _bn_.variable(n).name();
955
956 // an explicitly declared node is atemporal whatever its shape, trailing
957 // digits included: it never enters slicesPerProcess, so the completeness
958 // rules below never see it and never warn about it
959 if (atemporalNodes.contains(name)) {
960 _atemporal_.insert(name);
961 continue;
962 }
963
965
966 if (slice == ATEMPORAL) {
967 _atemporal_.insert(base);
968 } else {
971 discovered.push_back(base);
972 }
973
976 "Two variables map to process '" << base << "' at slice " << slice << ".")
977
979
981 }
982 }
983
984 _k_ = (maxSlice < 0) ? Size(1) : Size(maxSlice + 1);
985
986 // second pass: register the complete processes. A group that does not cover
987 // every slice 0..k-1 is NOT rejected: each of its nodes becomes an atemporal
988 // variable, original name kept, and a warning is recorded. Only a base used
989 // BOTH bare and temporal-shaped stays an error -- there the two readings
990 // collide on one name, and no reclassification can resolve that.
991 const char* const conventionNoun = hasBracket ? "bracket" : "digit-suffixed";
994
995 if (_atemporal_.contains(base))
997 "Base name '" << base << "' is used both as an atemporal variable (bare node '"
998 << base << "') and as a temporal process (via " << conventionNoun
999 << " nodes). " << "Rename one of them before calling fromBN().")
1000
1002 for (Size t = 0; t < _k_; ++t)
1003 if (!sliceMap.exists(static_cast< int >(t))) {
1004 if (!missing.empty()) missing += ", ";
1005 missing += std::to_string(t);
1006 }
1007
1008 if (missing.empty()) {
1009 // Every slice of a process must carry the SAME variable. add() cannot
1010 // break this -- it clones one variable into k instances -- so fromBN()
1011 // is the only way in, and nothing downstream re-checks: KTBNInference
1012 // sizes every ring slot of a process from the kernel slice alone, so a
1013 // slice with a divergent domain would be triangulated against the wrong
1014 // size and then filled with a tensor of another dimension.
1015 // Names necessarily differ between slices, and Variable::operator==
1016 // compares them, so the reference is renamed onto each slice in turn.
1017 const DiscreteVariable& ref = _bn_.variable(sliceMap[0]);
1018 std::unique_ptr< DiscreteVariable > probe(ref.clone());
1019 for (Size t = 1; t < _k_; ++t) {
1020 const DiscreteVariable& other = _bn_.variable(sliceMap[static_cast< int >(t)]);
1021 probe->setName(other.name());
1022 if (!(*probe == other))
1023 GUM_ERROR(OperationNotAllowed,
1024 "The temporal process '"
1025 << base << "' has mismatched slice variables: '" << ref.name() << "' is "
1026 << ref.domain() << " but '" << other.name() << "' is " << other.domain()
1027 << ". Every slice of a process must have the same type and domain.")
1028 }
1029 _temporal_.insert(base);
1030 if (!hasBracket) {
1031 // Under the trailing-integer convention the slices just matched above
1032 // carry no bracket notation yet: rename them onto the engine's
1033 // canonical base[t] form -- the invariant every other method (add(),
1034 // unroll(), rename(), ...) relies on. Under the bracket convention
1035 // source names are already canonical, so nothing to do here.
1036 for (Size t = 0; t < _k_; ++t)
1037 _bn_.changeVariableName(_bn_.variable(sliceMap[static_cast< int >(t)]).name(),
1038 _encode_(base, static_cast< int >(t)));
1039 }
1040 continue;
1041 }
1042
1044 for (auto it = sliceMap.cbegin(); it != sliceMap.cend(); ++it) {
1045 const std::string& nodeName = _bn_.variable(it.val()).name();
1046 _atemporal_.insert(nodeName);
1047 if (!reclassified.empty()) reclassified += ", ";
1048 reclassified += "'" + nodeName + "'";
1049 }
1050 warn("Node(s) " + reclassified + " look temporal (base='" + base + "', " + conventionNoun
1051 + " convention) but the process is missing slice(s) " + missing
1052 + " for k=" + std::to_string(_k_)
1053 + ": they are classified as atemporal variables, original name kept. Pass them in "
1054 "fromBN()'s atemporalNodes argument to make that explicit and silence this warning.");
1055 }
1056
1057 // Every surviving process holds exactly the slices 0..k-1, so k is still the
1058 // one the first pass computed -- unless none survived, in which case the
1059 // largest slice index was contributed by a group that is now atemporal and
1060 // k has nothing left to describe.
1061 if (_temporal_.empty()) _k_ = Size(1);
1062
1063
1064 // third pass: validate temporal causality of the foreign arcs.
1065 for (const auto& arc: _bn_.arcs()) {
1066 const int tailSlice = _determineNode_(_bn_.variable(arc.tail()).name()).second;
1067 const int headSlice = _determineNode_(_bn_.variable(arc.head()).name()).second;
1070 "The network has a temporal->atemporal arc into '"
1071 << _bn_.variable(arc.head()).name() << "'.")
1074 "The network has a future->past arc " << _bn_.variable(arc.tail()).name() << "->"
1075 << _bn_.variable(arc.head()).name() << ".")
1076 }
1077 }
std::vector< std::pair< std::pair< std::string, int >, std::pair< std::string, int > > > arcs() const
Definition KTBN_tpl.h:551
std::vector< std::pair< std::string, int > > nodes() const
Definition KTBN_tpl.h:344
const DiscreteVariable & variable(std::string_view base, int slice) const
Returns the gum::DiscreteVariable of a (process, slice) couple.
Definition KTBN_tpl.h:462
std::string _encode_(std::string_view base, int slice) const
Encodes (base, slice) → engine name: base[t], or base if atemporal.
Definition KTBN_tpl.h:78
bool exists(std::string_view base) const
Definition KTBN_tpl.h:318
const std::string & name() const
returns the name of the variable

References _atemporal_, _bn_, _decodeName_(), _determineNode_(), _encode_(), _k_, _temporal_, ATEMPORAL, gum::HashTable< Key, Val >::cbegin(), gum::HashTable< Key, Val >::cend(), gum::DiscreteVariable::clone(), gum::DiscreteVariable::domain(), gum::HashTable< Key, Val >::exists(), exists(), GUM_ERROR, gum::HashTable< Key, Val >::insert(), and gum::Variable::name().

Here is the call graph for this function:

◆ _encode_()

template<GUM_Numeric GUM_SCALAR>
INLINE std::string gum::KTBN< GUM_SCALAR >::_encode_ ( std::string_view base,
int slice ) const
private

Encodes (base, slice) → engine name: base[t], or base if atemporal.

Definition at line 78 of file KTBN_tpl.h.

78 {
79 if (slice == ATEMPORAL) return std::string{base};
80 return std::string{base} + '[' + std::to_string(slice) + ']';
81 }

References ATEMPORAL.

Referenced by _determineNodesFromBN_(), _timeSlicesToDot_(), _validateAdd_(), _validateVariable_(), add(), changeVariableName(), erase(), fillCPT(), and unroll().

Here is the caller graph for this function:

◆ _escapeDot_()

template<GUM_Numeric GUM_SCALAR>
std::string gum::KTBN< GUM_SCALAR >::_escapeDot_ ( std::string_view name)
staticprivate

Escapes double quotes for a DOT identifier or label. Shared by timeSlicesToDot() and summaryGraph().

Definition at line 1131 of file KTBN_tpl.h.

1131 {
1133 out.reserve(name.size());
1134 for (const char c: name) {
1135 if (c == '"') out += '\\';
1136 out += c;
1137 }
1138 return out;
1139 }

Referenced by _timeSlicesToDot_(), and summaryGraph().

Here is the caller graph for this function:

◆ _resolveGumFormat_()

template<GUM_Numeric GUM_SCALAR>
std::pair< std::string, bool > gum::KTBN< GUM_SCALAR >::_resolveGumFormat_ ( std::string_view filename)
staticprivate

Resolves a user filename to (filepath, binary): ensures a .jgum/.bgum extension (.bgum appended by default) and reports whether the format is binary. Single source of truth for the GUM format convention shared by save() and load().

Definition at line 786 of file KTBN_tpl.h.

786 {
787 // The extension selects the format: ".jgum" is text, anything else is binary
788 // and gets a ".bgum" extension appended if missing.
790 const bool text = filepath.ends_with(".jgum");
791 if (!text && !filepath.ends_with(".bgum")) filepath += ".bgum";
792 return {std::move(filepath), !text}; // .second = binary
793 }

Referenced by load(), and save().

Here is the caller graph for this function:

◆ _timeSlicesToDot_()

template<GUM_Numeric GUM_SCALAR>
std::string gum::KTBN< GUM_SCALAR >::_timeSlicesToDot_ ( const BayesNet< GUM_SCALAR > & bn,
bool highlightReplicated ) const
private

Renders bn as time-slice-clustered DOT. Shared engine behind toDot() (on _bn_) and toUnrolledDot() (on unroll(T)).

Definition at line 1146 of file KTBN_tpl.h.

1147 {
1148 // Group (full name, base label) by timeslice. std::map keeps keys sorted, and
1149 // ATEMPORAL == -1 so atemporal variables naturally sort first, followed by
1150 // increasing slice indices — mirroring pyAgrum's noTimeCluster-then-slices order.
1152 for (const NodeId n: bn.nodes()) {
1153 const std::string& name = bn.variable(n).name();
1154 const auto [base, slice] = _decodeName_(name);
1155 timeslices[slice].emplace_back(name, base);
1156 }
1157
1159 dot << "digraph KTBN {\n";
1160 dot << " rankdir=LR;\n";
1161 dot << " splines=ortho;\n";
1162 dot << " node [color=\"#000000\", fillcolor=white, style=filled];\n\n";
1163
1164 for (auto& [slice, nodes]: timeslices) {
1165 std::sort(nodes.begin(), nodes.end());
1166 if (slice == ATEMPORAL) {
1167 dot << " subgraph cluster_atemporal {\n";
1168 dot << " label=\"atemporal\";\n";
1169 dot << " style=filled;\n";
1170 dot << " bgcolor=\"lightyellow\";\n";
1171 for (const auto& [full, label]: nodes)
1172 dot << " \"" << _escapeDot_(full) << "\" [label=\"" << _escapeDot_(label) << "\"];\n";
1173 dot << " }\n";
1174 } else {
1175 const bool replicated = highlightReplicated && Size(slice) >= _k_;
1176 dot << " subgraph cluster_" << slice << " {\n";
1177 dot << " label=\"Time slice " << slice << "\";\n";
1178 dot << " style=filled;\n";
1179 dot << " bgcolor=\"" << (replicated ? "lightcyan" : "#DDDDDD") << "\";\n";
1180 for (const auto& [full, label]: nodes)
1181 dot << " \"" << _escapeDot_(full) << "\" [label=\"" << _escapeDot_(label) << "\"];\n";
1182 dot << " }\n";
1183 }
1184 dot << "\n";
1185 }
1186
1187 dot << " edge [color=black, constraint=false];\n";
1188 for (const auto& arc: bn.arcs())
1189 dot << " \"" << _escapeDot_(bn.variable(arc.tail()).name()) << "\" -> \""
1190 << _escapeDot_(bn.variable(arc.head()).name()) << "\";\n";
1191
1192 dot << "\n edge [style=invis, constraint=true];\n";
1193 if (const auto it0 = timeslices.find(0); it0 != timeslices.end()) {
1194 for (const auto& node0: it0->second) {
1195 const std::string& label = node0.second;
1196 int prec = ATEMPORAL;
1197 bool first = true;
1198 for (const auto& [slice, nodes]: timeslices) {
1199 if (slice == ATEMPORAL) continue;
1200 if (!first)
1201 dot << " \"" << _escapeDot_(_encode_(label, prec)) << "\" -> \""
1202 << _escapeDot_(_encode_(label, slice)) << "\";\n";
1203 prec = slice;
1204 first = false;
1205 }
1206 }
1207 }
1208
1209 dot << "}\n";
1210 return dot.str();
1211 }
static std::string _escapeDot_(std::string_view name)
Escapes double quotes for a DOT identifier or label. Shared by timeSlicesToDot() and summaryGraph().
Definition KTBN_tpl.h:1131

References _decodeName_(), _encode_(), _escapeDot_(), _k_, ATEMPORAL, and nodes().

Referenced by toDot(), and toUnrolledDot().

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

◆ _validateAdd_()

template<GUM_Numeric GUM_SCALAR>
void gum::KTBN< GUM_SCALAR >::_validateAdd_ ( const std::string & base,
bool temporal ) const
private

Checks that a variable named base can be added.

Parameters
temporaltrue for a temporal process, false for an atemporal variable.
Exceptions
DuplicateLabel/ InvalidArgument on a name collision (see add()).

Definition at line 215 of file KTBN_tpl.h.

215 {
216 const auto& ownSet = temporal ? _temporal_ : _atemporal_;
217 const auto& otherSet = temporal ? _atemporal_ : _temporal_;
218
219 if (ownSet.contains(base))
221 (temporal ? "A temporal process '" : "An atemporal variable '")
222 << base << "' already exists.")
225 (temporal ? "Cannot add temporal process '" : "Cannot add atemporal variable '")
226 << base << "': " << (temporal ? "an atemporal variable" : "a temporal process")
227 << " with that name already exists.")
228
229 if (temporal) {
230 for (Size t = 0; t < _k_; ++t) {
231 const std::string encoded = _encode_(base, static_cast< int >(t));
232 if (_atemporal_.contains(encoded))
234 "Temporal process '" << base << "' at slice " << t << " would produce node '"
235 << encoded << "' which conflicts with atemporal variable '"
236 << encoded << "'.")
237 }
238 } else {
239 const auto [decodedBase, decodedSlice] = _decodeName_(base);
240 if (decodedSlice != ATEMPORAL && _temporal_.contains(decodedBase)) {
241 // Bracket notation over an existing temporal process is reserved in
242 // FULL, whatever the index: _decodeName_ has no upper bound, so
243 // "X[999]" decodes to (X, 999) and would shadow the process even though
244 // no such node exists. Only the in-range case can claim an actual node
245 // collision -- promising one for an out-of-range index would send the
246 // caller looking for a node that was never there.
247 if (Size(decodedSlice) < _k_)
249 "Atemporal variable name '" << base << "' conflicts with temporal process '"
250 << decodedBase
251 << "': that name is already used by its slice "
252 "nodes.")
254 "Atemporal variable name '"
255 << base << "' is invalid: '" << decodedBase
256 << "' is a temporal process, so every bracket-suffixed name over it is "
257 "reserved -- including slice "
258 << decodedSlice << ", beyond the current order k=" << _k_ << ".")
259 }
260 }
261 }

References _atemporal_, _decodeName_(), _encode_(), _k_, _temporal_, ATEMPORAL, and GUM_ERROR.

Referenced by add().

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

◆ _validateVariable_()

template<GUM_Numeric GUM_SCALAR>
NodeId gum::KTBN< GUM_SCALAR >::_validateVariable_ ( std::string_view base,
int slice ) const
private

Resolves and validates a (base, slice) endpoint into its NodeId.

Definition at line 434 of file KTBN_tpl.h.

434 {
435 const std::string baseStr{base};
436
437 if (slice == ATEMPORAL) {
438 if (!_atemporal_.contains(baseStr)) {
439 if (_temporal_.contains(baseStr))
441 "'" << baseStr << "' is a temporal process but is used as atemporal.")
442 GUM_ERROR(NotFound, "There is no atemporal variable named '" << baseStr << "'.")
443 }
445 }
446
448 if (_atemporal_.contains(baseStr))
450 "'" << baseStr << "' is an atemporal variable but is used at slice " << slice
451 << ".")
452 GUM_ERROR(NotFound, "There is no temporal process named '" << baseStr << "'.")
453 }
454 if (slice < 0 || Size(slice) >= _k_)
456 "Slice " << slice << " is out of [0," << (_k_ - 1) << "] for process '" << baseStr
457 << "'.")
459 }

References _atemporal_, _bn_, _encode_(), _k_, _temporal_, ATEMPORAL, and GUM_ERROR.

Referenced by addArc(), children(), cpt(), eraseArc(), existsArc(), fillCPT(), generateCPT(), parents(), and variable().

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

◆ add() [1/2]

template<GUM_Numeric GUM_SCALAR>
void gum::KTBN< GUM_SCALAR >::add ( const DiscreteVariable & var,
bool temporal = true )

Adds a variable to the k-DBN.

A temporal variable (process) is cloned into \(k\) instances (one per slice); an atemporal variable is added once.

Parameters
varThe variable to add (added by copy).
temporalWhether the variable is temporal.
Exceptions
DuplicateLabelif a variable with that base name already exists (any kind — base names are globally unique).
InvalidArgumentif a temporal base[t] would collide with an existing atemporal node of that encoded name, or vice-versa.

Definition at line 264 of file KTBN_tpl.h.

264 {
265 const std::string base = var.name();
266
267 if (temporal) {
268 _validateAdd_(base, true);
269 for (Size t = 0; t < _k_; ++t) {
270 // clone() to rename; BayesNet::add() clones again internally (unavoidable via public API).
272 clone->setName(_encode_(base, static_cast< int >(t)));
273 _bn_.add(*clone);
274 }
275 _temporal_.insert(base);
276 } else {
277 _validateAdd_(base, false);
278 _bn_.add(var);
279 _atemporal_.insert(base);
280 }
281 }
void _validateAdd_(const std::string &base, bool temporal) const
Checks that a variable named base can be added.
Definition KTBN_tpl.h:215
void add(const DiscreteVariable &var, bool temporal=true)
Adds a variable to the k-DBN.
Definition KTBN_tpl.h:264

References _atemporal_, _bn_, _encode_(), _k_, _temporal_, _validateAdd_(), gum::DiscreteVariable::clone(), and gum::Variable::name().

Referenced by add(), addAtemporal(), addAtemporal(), addTemporal(), and addTemporal().

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

◆ add() [2/2]

template<GUM_Numeric GUM_SCALAR>
void gum::KTBN< GUM_SCALAR >::add ( std::string_view fast_description,
bool temporal = true,
unsigned int default_nbrmod = 2 )

Adds a variable using the "fast" textual description syntax.

See also
gum::BayesNet::add(std::string_view,unsigned int)

Definition at line 284 of file KTBN_tpl.h.

References add(), and gum::fastVariable().

Here is the call graph for this function:

◆ addArc() [1/2]

template<GUM_Numeric GUM_SCALAR>
void gum::KTBN< GUM_SCALAR >::addArc ( std::string_view tail,
std::string_view head )

Adds an arc using internal engine names (bracket notation: "X[0]", "C", …).

See also
fillCPT(node_name, …) for the same naming convention.
Exceptions
NotFoundif an endpoint name is not in the template.
OutOfBounds/ OperationNotAllowed / DuplicateElement / InvalidDirectedCycle — same conditions as the (base, slice) overload.

Definition at line 529 of file KTBN_tpl.h.

529 {
530 const auto [tb, ts] = _determineNode_(std::string{tail});
531 const auto [hb, hs] = _determineNode_(std::string{head});
532 addArc(tb, ts, hb, hs);
533 }
void addArc(std::string_view tailBase, int tailSlice, std::string_view headBase, int headSlice)
Adds an arc between two (process, slice) endpoints.
Definition KTBN_tpl.h:490

References _determineNode_(), and addArc().

Here is the call graph for this function:

◆ addArc() [2/2]

template<GUM_Numeric GUM_SCALAR>
void gum::KTBN< GUM_SCALAR >::addArc ( std::string_view tailBase,
int tailSlice,
std::string_view headBase,
int headSlice )

Adds an arc between two (process, slice) endpoints.

Parameters
tailBaseBase name of the tail variable.
tailSliceSlice of the tail (KTBN::ATEMPORAL for an atemporal tail).
headBaseBase name of the head variable.
headSliceSlice of the head (KTBN::ATEMPORAL for an atemporal head).
Exceptions
NotFoundif an endpoint does not exist.
OutOfBoundsif a slice argument is out of \([0,k-1]\) for a temporal endpoint.
OperationNotAllowedif the arc violates temporal causality (a temporal variable pointing to an atemporal one, or an arc from a future slice to a past slice).
DuplicateElementif the arc already exists.
InvalidDirectedCycleif the arc would create a cycle.

Definition at line 490 of file KTBN_tpl.h.

493 {
496
497 if (headSlice == ATEMPORAL) {
498 if (tailSlice != ATEMPORAL)
500 "A temporal variable cannot be a parent of the atemporal variable '" << headBase
501 << "'.")
502 } else if (tailSlice != ATEMPORAL && tailSlice > headSlice) {
504 "An arc cannot go from a future slice (" << tailSlice << ") to a past slice ("
505 << headSlice << ").")
506 }
507
508 _bn_.addArc(tail, head);
509 }
NodeId _validateVariable_(std::string_view base, int slice) const
Resolves and validates a (base, slice) endpoint into its NodeId.
Definition KTBN_tpl.h:434

References _bn_, _validateVariable_(), ATEMPORAL, and GUM_ERROR.

Referenced by addArc().

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

◆ addAtemporal() [1/2]

template<GUM_Numeric GUM_SCALAR>
INLINE void gum::KTBN< GUM_SCALAR >::addAtemporal ( const DiscreteVariable & var)

Convenience shortcut for add(var, false).

Definition at line 297 of file KTBN_tpl.h.

297 {
298 add(var, false);
299 }

References add().

Here is the call graph for this function:

◆ addAtemporal() [2/2]

template<GUM_Numeric GUM_SCALAR>
INLINE void gum::KTBN< GUM_SCALAR >::addAtemporal ( std::string_view fast_description,
unsigned int default_nbrmod = 2 )

Convenience shortcut for add(fast_description, false, default_nbrmod).

Definition at line 308 of file KTBN_tpl.h.

309 {
311 }

References add().

Here is the call graph for this function:

◆ addTemporal() [1/2]

template<GUM_Numeric GUM_SCALAR>
INLINE void gum::KTBN< GUM_SCALAR >::addTemporal ( const DiscreteVariable & var)

Convenience shortcut for add(var, true).

Definition at line 292 of file KTBN_tpl.h.

292 {
293 add(var, true);
294 }

References add().

Here is the call graph for this function:

◆ addTemporal() [2/2]

template<GUM_Numeric GUM_SCALAR>
INLINE void gum::KTBN< GUM_SCALAR >::addTemporal ( std::string_view fast_description,
unsigned int default_nbrmod = 2 )

Convenience shortcut for add(fast_description, true, default_nbrmod).

Definition at line 302 of file KTBN_tpl.h.

303 {
305 }

References add().

Here is the call graph for this function:

◆ arcs()

template<GUM_Numeric GUM_SCALAR>
std::vector< std::pair< std::pair< std::string, int >, std::pair< std::string, int > > > gum::KTBN< GUM_SCALAR >::arcs ( ) const
Returns
All arcs as (tail, head) pairs of (base, slice).

Definition at line 551 of file KTBN_tpl.h.

551 {
553 result.reserve(_bn_.sizeArcs());
554 for (const auto& arc: _bn_.arcs()) {
555 result.emplace_back(_determineNode_(_bn_.variable(arc.tail()).name()),
556 _determineNode_(_bn_.variable(arc.head()).name()));
557 }
558 return result;
559 }

References _bn_, and _determineNode_().

Referenced by toString().

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

◆ atemporalVarNames()

template<GUM_Numeric GUM_SCALAR>
INLINE const std::unordered_set< std::string > & gum::KTBN< GUM_SCALAR >::atemporalVarNames ( ) const
Returns
The set of atemporal variable base names.

Definition at line 329 of file KTBN_tpl.h.

329 {
330 return _atemporal_;
331 }

References _atemporal_.

◆ baseName()

template<GUM_Numeric GUM_SCALAR>
INLINE std::string gum::KTBN< GUM_SCALAR >::baseName ( const DiscreteVariable & var) const

Returns the base name (without bracket encoding) of var.

Exceptions
NotFoundif var is not a node of this k-DBN.

Definition at line 480 of file KTBN_tpl.h.

480 {
481 _bn_.idFromName(var.name()); // throws NotFound if var is not in this k-DBN
482 return _determineNode_(var.name()).first;
483 }

References _bn_, _determineNode_(), and gum::Variable::name().

Here is the call graph for this function:

◆ bnToDot()

template<GUM_Numeric GUM_SCALAR>
INLINE std::string gum::KTBN< GUM_SCALAR >::bnToDot ( ) const

Returns the Graphviz DOT string of the underlying storage BayesNet.

Nodes are labelled with their internal engine names (bracket notation: base[t] for temporal nodes, bare name for atemporal nodes). No time-slice clustering is applied.

Definition at line 1214 of file KTBN_tpl.h.

1214 {
1215 return _bn_.toDot();
1216 }

References _bn_.

◆ changeVariableName()

template<GUM_Numeric GUM_SCALAR>
void gum::KTBN< GUM_SCALAR >::changeVariableName ( std::string_view oldBase,
std::string_view newBase )

Renames a variable (temporal process or atemporal variable).

The kind is determined automatically from the registered name sets. For a temporal process all \(k\) slice names are re-encoded with the new base.

Exceptions
NotFoundif no variable named oldBase exists.
DuplicateLabelif a variable named newBase already exists (any kind).
InvalidArgumentif newBase is empty or would create a BN node-name collision.

Definition at line 398 of file KTBN_tpl.h.

398 {
401
402 if (oldStr == newStr) return;
403 if (newStr.empty()) GUM_ERROR(InvalidArgument, "New base name must not be empty.")
404
406 GUM_ERROR(NotFound, "No variable named '" << oldStr << "' in the k-DBN.")
408 GUM_ERROR(DuplicateLabel, "A variable named '" << newStr << "' already exists.")
409
411 for (Size t = 0; t < _k_; ++t)
412 if (_atemporal_.contains(_encode_(newStr, static_cast< int >(t))))
414 "Renaming to '" << newStr << "': slice " << t
415 << " collides with an atemporal variable.")
416 for (Size t = 0; t < _k_; ++t)
417 _bn_.changeVariableName(_encode_(oldStr, static_cast< int >(t)),
418 _encode_(newStr, static_cast< int >(t)));
419 _temporal_.erase(oldStr);
420 _temporal_.insert(newStr);
421 } else {
423 if (decodedSlice != ATEMPORAL && _temporal_.contains(decodedBase))
425 "'" << newStr << "' conflicts with temporal process '" << decodedBase
426 << "': that name is already used by its slice nodes.")
430 }
431 }
void erase(std::string_view base)
Removes a variable and all its incident arcs.
Definition KTBN_tpl.h:382
void changeVariableName(std::string_view oldBase, std::string_view newBase)
Renames a variable (temporal process or atemporal variable).
Definition KTBN_tpl.h:398

References _atemporal_, _bn_, _decodeName_(), _encode_(), _k_, _temporal_, ATEMPORAL, and GUM_ERROR.

Here is the call graph for this function:

◆ children() [1/2]

template<GUM_Numeric GUM_SCALAR>
std::vector< std::pair< std::string, int > > gum::KTBN< GUM_SCALAR >::children ( std::string_view base,
int slice ) const

Children of a node as (base, slice) pairs (ATEMPORAL if atemporal).

Definition at line 369 of file KTBN_tpl.h.

370 {
372 }
std::vector< std::pair< std::string, int > > _determineNodeSet_(const NodeSet &ids) const
Maps a set of node ids to (base, slice) pairs (via determineNode).
Definition KTBN_tpl.h:118

References _bn_, _determineNodeSet_(), and _validateVariable_().

Referenced by children().

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

◆ children() [2/2]

template<GUM_Numeric GUM_SCALAR>
std::vector< std::pair< std::string, int > > gum::KTBN< GUM_SCALAR >::children ( std::string_view node_name) const

Returns the children using an engine name ("X[1]", "C", …).

Definition at line 376 of file KTBN_tpl.h.

376 {
377 const auto [b, s] = _determineNode_(std::string{node_name});
378 return children(b, s);
379 }
std::vector< std::pair< std::string, int > > children(std::string_view base, int slice) const
Children of a node as (base, slice) pairs (ATEMPORAL if atemporal).
Definition KTBN_tpl.h:369

References _determineNode_(), and children().

Here is the call graph for this function:

◆ clear()

template<GUM_Numeric GUM_SCALAR>
void gum::KTBN< GUM_SCALAR >::clear ( )

Removes all variables and arcs, keeping the order \(k\).

Definition at line 204 of file KTBN_tpl.h.

204 {
205 _bn_.clear();
206 _temporal_.clear();
207 _atemporal_.clear();
208 }

References _atemporal_, _bn_, and _temporal_.

◆ cpt() [1/2]

template<GUM_Numeric GUM_SCALAR>
INLINE const Tensor< GUM_SCALAR > & gum::KTBN< GUM_SCALAR >::cpt ( std::string_view base,
int slice ) const

Returns the CPT of a (process, slice) couple.

The returned reference is const but its content is mutable — use any standard gum::Tensor method to fill it:

m.cpt("C").fillWith({0.4, 0.6});
m.cpt("X", 2).fillWith({0.8,0.2, 0.3,0.7, 0.6,0.4, 0.1,0.9});
m.cpt("Y", 1).fillWith(GUM_SCALAR(0.5)); // uniform

Values for the vector overload are in the CPT's natural iteration order: the node's own variable varies fastest, parents follow in the order they appear in cpt().variable(1..n). Use cpt().variable(i).name() to inspect the ordering before filling.

Definition at line 566 of file KTBN_tpl.h.

567 {
568 return _bn_.cpt(_validateVariable_(base, slice));
569 }

References _bn_, and _validateVariable_().

Referenced by cpt(), fillCPT(), and fillCPT().

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

◆ cpt() [2/2]

template<GUM_Numeric GUM_SCALAR>
INLINE const Tensor< GUM_SCALAR > & gum::KTBN< GUM_SCALAR >::cpt ( std::string_view node_name) const

Returns the CPT using an engine name ("X[1]", "C", …).

Definition at line 572 of file KTBN_tpl.h.

572 {
573 const auto [b, s] = _determineNode_(std::string{node_name});
574 return cpt(b, s);
575 }
const Tensor< GUM_SCALAR > & cpt(std::string_view base, int slice) const
Returns the CPT of a (process, slice) couple.
Definition KTBN_tpl.h:566

References _determineNode_(), and cpt().

Here is the call graph for this function:

◆ empty()

template<GUM_Numeric GUM_SCALAR>
INLINE bool gum::KTBN< GUM_SCALAR >::empty ( ) const
Returns
true if the template contains no variable.

Definition at line 199 of file KTBN_tpl.h.

199 {
200 return _bn_.empty();
201 }

References _bn_.

◆ erase()

template<GUM_Numeric GUM_SCALAR>
void gum::KTBN< GUM_SCALAR >::erase ( std::string_view base)

Removes a variable and all its incident arcs.

The kind (temporal or atemporal) is determined automatically from the registered name sets. For a temporal process all \(k\) slice nodes are removed.

Exceptions
NotFoundif no variable with that name exists.

Definition at line 382 of file KTBN_tpl.h.

382 {
383 const std::string baseStr{base};
384
385 if (_temporal_.contains(baseStr)) {
386 for (Size t = 0; t < _k_; ++t)
387 _bn_.erase(_encode_(baseStr, static_cast< int >(t)));
388 _temporal_.erase(baseStr);
389 } else if (_atemporal_.contains(baseStr)) {
390 _bn_.erase(baseStr);
391 _atemporal_.erase(baseStr);
392 } else {
393 GUM_ERROR(NotFound, "No variable named '" << baseStr << "' in the k-DBN.")
394 }
395 }

References _atemporal_, _bn_, _encode_(), _k_, _temporal_, and GUM_ERROR.

Here is the call graph for this function:

◆ eraseArc() [1/2]

template<GUM_Numeric GUM_SCALAR>
void gum::KTBN< GUM_SCALAR >::eraseArc ( std::string_view tail,
std::string_view head )

Removes an arc using internal engine names (bracket notation).

Exceptions
NotFoundif an endpoint name is unknown or the arc does not exist.

Definition at line 536 of file KTBN_tpl.h.

536 {
537 const auto [tb, ts] = _determineNode_(std::string{tail});
538 const auto [hb, hs] = _determineNode_(std::string{head});
539 eraseArc(tb, ts, hb, hs);
540 }
void eraseArc(std::string_view tailBase, int tailSlice, std::string_view headBase, int headSlice)
Removes an arc between two (process, slice) endpoints.
Definition KTBN_tpl.h:512

References _determineNode_(), and eraseArc().

Here is the call graph for this function:

◆ eraseArc() [2/2]

template<GUM_Numeric GUM_SCALAR>
void gum::KTBN< GUM_SCALAR >::eraseArc ( std::string_view tailBase,
int tailSlice,
std::string_view headBase,
int headSlice )

Removes an arc between two (process, slice) endpoints.

The arc must exist: erasing an absent arc throws (via the underlying CPT update), even when both endpoints are valid variables.

Exceptions
NotFound/ OperationNotAllowed / OutOfBounds if an endpoint variable does not exist (same rules as addArc()), or if the arc itself does not exist.

Definition at line 512 of file KTBN_tpl.h.

References _bn_, and _validateVariable_().

Referenced by eraseArc().

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

◆ exists()

template<GUM_Numeric GUM_SCALAR>
INLINE bool gum::KTBN< GUM_SCALAR >::exists ( std::string_view base) const
Returns
true if a variable with this base name exists.

Definition at line 318 of file KTBN_tpl.h.

318 {
319 const std::string baseStr{base};
320 return _temporal_.contains(baseStr) || _atemporal_.contains(baseStr);
321 }

References _atemporal_, and _temporal_.

Referenced by _determineNodesFromBN_().

Here is the caller graph for this function:

◆ existsArc() [1/2]

template<GUM_Numeric GUM_SCALAR>
bool gum::KTBN< GUM_SCALAR >::existsArc ( std::string_view tail,
std::string_view head ) const
Returns
true if the arc exists; uses internal engine names (bracket notation).
Exceptions
NotFoundif an endpoint name is unknown.

Definition at line 543 of file KTBN_tpl.h.

543 {
544 const auto [tb, ts] = _determineNode_(std::string{tail});
545 const auto [hb, hs] = _determineNode_(std::string{head});
546 return existsArc(tb, ts, hb, hs);
547 }
bool existsArc(std::string_view tailBase, int tailSlice, std::string_view headBase, int headSlice) const
Definition KTBN_tpl.h:520

References _determineNode_(), and existsArc().

Here is the call graph for this function:

◆ existsArc() [2/2]

template<GUM_Numeric GUM_SCALAR>
bool gum::KTBN< GUM_SCALAR >::existsArc ( std::string_view tailBase,
int tailSlice,
std::string_view headBase,
int headSlice ) const
Returns
true if the arc exists in the template.

Definition at line 520 of file KTBN_tpl.h.

523 {
524 return _bn_.existsArc(_validateVariable_(tailBase, tailSlice),
526 }

References _bn_, and _validateVariable_().

Referenced by existsArc().

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

◆ fillCPT() [1/2]

template<GUM_Numeric GUM_SCALAR>
void gum::KTBN< GUM_SCALAR >::fillCPT ( std::string_view base,
int slice,
const std::map< std::pair< std::string, int >, KTBNModality > & parents,
const std::vector< GUM_SCALAR > & distribution ) const

Fills one conditional distribution P(node | parent configuration).

The order-safe, bracket-free way to fill a CPT: the node and its parents are addressed by their (base, slice) identity, so the result does not depend on the positional order of cpt().fillWith({...}).

Each parent is a dictionary entry keyed by its (base, slice) identity (use ATEMPORAL as slice for a static parent). ALL parents of the node must be listed, in any order. distribution holds the probabilities over the node's own modalities for that parent configuration.

A parent's value is either a modality index or a modality label, and the two may be mixed — see gum::KTBNModality, whose warning on numeric modalities applies here.

m.fillCPT("C", KTBN::ATEMPORAL, {}, {0.4, 0.6}); // P(C), no parents
m.fillCPT("X", 0, {}, {0.7, 0.3}); // P(X[0]), no parents
m.fillCPT("X", 1, {{{"X", 0}, 1}, {{"C", KTBN::ATEMPORAL}, 0}},
{0.6, 0.4}); // P(X[1] | X[0]=1, C=0)
m.fillCPT("X", 1, {{{"X", 0}, "1"}, {{"C", KTBN::ATEMPORAL}, 0}},
{0.6, 0.4}); // same, X[0] by label
Parameters
baseBase name of the target node.
sliceSlice of the target node (ATEMPORAL for static).
parentsOne (base, slice) -> value entry per parent, any order.
distributionProbabilities over the node's modalities; its size must equal the node's domain size.
Exceptions
NotFound/ OutOfBounds if the node or a parent does not exist, if a parent index is out of range, or if a parent label is not one of that parent's modalities (which of the two is raised for an unknown label depends on the variable type).
SizeErrorif distribution size differs from the node domain size, or if not every parent is specified.
InvalidArgumentif an entry's node is not a parent of the target.

Definition at line 594 of file KTBN_tpl.h.

598 {
599 const NodeId id = _validateVariable_(base, slice);
600 const Tensor< GUM_SCALAR >& cpt = _bn_.cpt(id);
601 const DiscreteVariable& self = _bn_.variable(id);
602
603 if (distribution.size() != self.domainSize())
605 "fillCPT: distribution has " << distribution.size() << " value(s) but '" << base
606 << "' has " << self.domainSize() << " modalities.")
607
608 const Size nbParents = cpt.nbrDim() - 1;
609 if (parents.size() != nbParents)
611 "fillCPT: " << parents.size() << " parent value(s) given but the node has "
612 << nbParents << " parent(s); every parent must be specified.")
613
614 // Address each parent by its (base, slice) identity — order-independent.
615 // No duplicate-parent check needed: a dictionary key is unique by
616 // construction, and here each (base, slice) pair names exactly one node,
617 // so no two distinct keys can alias the same parent.
620 const auto& [parBase, parSlice] = parNode;
623 if (parId == id || !cpt.contains(parVar))
625 "fillCPT: '" << parBase << "' is not a parent of the target node.")
627 }
628
629 // Write the whole conditional distribution over the node's own modalities.
630 for (Idx m = 0; m < self.domainSize(); ++m) {
631 inst.chgVal(self, m);
632 cpt.set(inst, distribution[m]);
633 }
634 }
std::vector< std::pair< std::string, int > > parents(std::string_view base, int slice) const
Parents of a node as (base, slice) pairs (ATEMPORAL if atemporal).
Definition KTBN_tpl.h:356

References _bn_, _validateVariable_(), gum::Instantiation::chgVal(), cpt(), gum::DiscreteVariable::domainSize(), GUM_ERROR, and parents().

Here is the call graph for this function:

◆ fillCPT() [2/2]

template<GUM_Numeric GUM_SCALAR>
void gum::KTBN< GUM_SCALAR >::fillCPT ( std::string_view node_name,
const std::map< std::variant< std::string, std::pair< std::string, int > >, KTBNModality > & parents,
const std::vector< GUM_SCALAR > & distribution ) const

Fills one conditional distribution using internal bracket-notation engine name for the target node — the bracket notation visible in toDot() and toString(): "X[t]" for a temporal variable at slice t, and the bare variable name for an atemporal variable (e.g.

"C"). Each parent is keyed either by its engine name ("X[0]") or by its (base, slice) identity — both spellings may be mixed in the same dictionary.

Warning
An engine-name key can be written as a bare string literal, but a (base, slice) key needs an explicit std::pair{...}: std::variant's converting constructor takes one value convertible to an alternative, not a nested brace list, so a bare {"X", 0} cannot implicitly construct the pair alternative the way it would a std::pair or std::tuple element.

As in the (base, slice) overload, a parent's value is either a modality index or a modality label — see gum::KTBNModality.

m.fillCPT("X[1]", {{"X[0]", 1}, {"C", 0}}, {0.6, 0.4});
m.fillCPT("X[1]", {{std::pair{"X", 0}, 1}, {std::pair{"C", KTBN::ATEMPORAL}, 0}},
{0.6, 0.4});
m.fillCPT("X[1]", {{"X[0]", 1}, {std::pair{"C", KTBN::ATEMPORAL}, 0}},
{0.6, 0.4}); // mixed key styles
m.fillCPT("X[1]", {{"X[0]", "1"}, {"C", 0}}, {0.6, 0.4}); // mixed value styles
Parameters
node_nameInternal engine name of the target node.
parentsOne engine_name-or-(base,slice) -> value entry per parent, any order.
distributionProbabilities over the node's modalities; its size must equal the node's domain size.
Exceptions
NotFoundif node_name or a parent does not exist.
SizeError/ InvalidArgument / OutOfBounds — same conditions as the (base, slice) overload.

Definition at line 637 of file KTBN_tpl.h.

641 {
642 const NodeId id = _bn_.idFromName(std::string{node_name});
643 const Tensor< GUM_SCALAR >& cpt = _bn_.cpt(id);
644 const DiscreteVariable& self = _bn_.variable(id);
645
646 if (distribution.size() != self.domainSize())
648 "fillCPT: distribution has " << distribution.size() << " value(s) but '"
649 << node_name << "' has " << self.domainSize()
650 << " modalities.")
651
652 const Size nbParents = cpt.nbrDim() - 1;
653 if (parents.size() != nbParents)
655 "fillCPT: " << parents.size() << " parent value(s) given but the node has "
656 << nbParents << " parent(s); every parent must be specified.")
657
658 // Unlike the (base, slice)-keyed overload above, duplicates ARE possible here
659 // despite unique map keys: "X[0]" and (base="X", slice=0) compare unequal as
660 // std::variant values yet name the same node. Both are resolved to an engine
661 // name first, so the seen-check below can catch the alias.
665 const std::string parName
669 std::get< std::pair< std::string, int > >(parKey).second);
670 const NodeId parId = _bn_.idFromName(parName);
672 if (parId == id || !cpt.contains(parVar))
674 "fillCPT: '" << parName << "' is not a parent of the target node.")
675 if (seen.contains(parId))
676 GUM_ERROR(InvalidArgument, "fillCPT: parent '" << parName << "' is listed more than once.")
677 seen.insert(parId);
678 inst.chgVal(parVar, parVal.toIndex(parVar));
679 }
680
681 for (Idx m = 0; m < self.domainSize(); ++m) {
682 inst.chgVal(self, m);
683 cpt.set(inst, distribution[m]);
684 }
685 }

References _bn_, _encode_(), gum::Instantiation::chgVal(), gum::Set< Key >::contains(), cpt(), gum::DiscreteVariable::domainSize(), GUM_ERROR, gum::Set< Key >::insert(), and parents().

Here is the call graph for this function:

◆ fromBN()

template<GUM_Numeric GUM_SCALAR>
KTBN< GUM_SCALAR > gum::KTBN< GUM_SCALAR >::fromBN ( const BayesNet< GUM_SCALAR > & bn,
const std::unordered_set< std::string > & atemporalNodes = {},
std::vector< std::string > * warnings = nullptr )
static

Builds a k-DBN from an existing gum::BayesNet, reading its node names under one of two mutually exclusive conventions.

If any node name already carries the engine's own bracket notation (base[t]), the whole network is read that way: exactly the legacy behaviour, temporal node names assumed canonical (a plain decimal slice, no leading zeros – the form produced by add() / unroll() / toBN()). Otherwise – no node name carries a bracket at all – every node is read under the bracket-free convention: a name ending in a run of digits denotes a temporal variable at the timeslice given by that integer (base = everything before the digits, e.g. "X0" and "X12" both belong to process "X", at slices 0 and 12); any other name is atemporal.

The order \(k\) is inferred as one plus the largest slice index found. A group of same-base nodes that does not cover every slice \(0..k-1\) is not an error: each of its nodes becomes an atemporal variable, original name kept, and a message is appended to warnings. When no temporal process survives, \(k\) falls back to 1.

Parameters
bnThe source Bayesian network (copied).
atemporalNodesNode names – exactly as they appear in bn – to classify atemporal outright. Only needed to lift an ambiguity: a name the active convention already reads as atemporal changes nothing by being listed, while listing a temporal-shaped name says "I meant this atemporally" where the reclassification above would only guess (and warn).
warningsIf non-null, receives one message per reclassified group.
Exceptions
NotFoundif a name in atemporalNodes is not a node of bn.
OperationNotAllowedif the temporal structure is inconsistent: two variables mapping to the same (process, slice), a base name used both as an atemporal variable and as a temporal process, an arc from a temporal node into an atemporal one, or an arc from the future to the past.
Warning
A lone temporal-shaped node with no sibling sharing its base (e.g. "X0" alone under the bracket-free convention, or "X[0]" alone under the bracket one) yields \(k=1\), where a single slice is a complete process: it is kept temporal, silently. List it in atemporalNodes to say otherwise.

Definition at line 880 of file KTBN_tpl.h.

882 {
883 KTBN< GUM_SCALAR > res(1); // _determineNodesFromBN_ below will modify this k=1
884 res._bn_ = bn;
886 return res;
887 }
void _determineNodesFromBN_(const std::unordered_set< std::string > &atemporalNodes, std::vector< std::string > *warnings)
Rebuilds the cached name sets from the storage engine content (used by fromBN()/load(); decodes names...
Definition KTBN_tpl.h:890

Referenced by gum::learning::KTBNLearner< GUM_SCALAR >::_assemble_(), and load().

Here is the caller graph for this function:

◆ generateCPT() [1/2]

template<GUM_Numeric GUM_SCALAR>
INLINE void gum::KTBN< GUM_SCALAR >::generateCPT ( std::string_view base,
int slice ) const

Randomly generates the CPT of a single node.

Definition at line 583 of file KTBN_tpl.h.

583 {
584 _bn_.generateCPT(_validateVariable_(base, slice));
585 }

References _bn_, and _validateVariable_().

Referenced by generateCPT().

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

◆ generateCPT() [2/2]

template<GUM_Numeric GUM_SCALAR>
INLINE void gum::KTBN< GUM_SCALAR >::generateCPT ( std::string_view node_name) const

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

Definition at line 588 of file KTBN_tpl.h.

588 {
589 const auto [b, s] = _determineNode_(std::string{node_name});
590 generateCPT(b, s);
591 }
void generateCPT(std::string_view base, int slice) const
Randomly generates the CPT of a single node.
Definition KTBN_tpl.h:583

References _determineNode_(), and generateCPT().

Here is the call graph for this function:

◆ generateCPTs()

template<GUM_Numeric GUM_SCALAR>
INLINE void gum::KTBN< GUM_SCALAR >::generateCPTs ( ) const

Randomly generates every CPT of the template.

Definition at line 578 of file KTBN_tpl.h.

578 {
579 _bn_.generateCPTs();
580 }

References _bn_.

◆ k()

template<GUM_Numeric GUM_SCALAR>
INLINE Size gum::KTBN< GUM_SCALAR >::k ( ) const
Returns
The order \(k\) of the k-DBN.

Definition at line 184 of file KTBN_tpl.h.

184 {
185 return _k_;
186 }

References _k_.

Referenced by KTBN().

Here is the caller graph for this function:

◆ load()

template<GUM_Numeric GUM_SCALAR>
KTBN< GUM_SCALAR > gum::KTBN< GUM_SCALAR >::load ( std::string_view filename)
static

Loads a k-DBN from a GUM file produced by save().

Text iff the name ends with .jgum, otherwise binary (gaining a .bgum extension if missing). If the file carries the KTBN classification properties it is restored exactly; otherwise it is re-derived from node names via fromBN().

Parameters
filenameThe GUM file.
Exceptions
IOErrorif the file cannot be read or is not valid.
OperationNotAllowedif the classification properties are absent and the fallback fromBN() finds an inconsistent temporal structure.

Definition at line 824 of file KTBN_tpl.h.

824 {
826
829 const Size nbErr = reader.proceed();
830 if (nbErr > 0) {
832 reader.showElegantErrorsAndWarnings(stream);
833 reader.showErrorCounts(stream);
834 GUM_ERROR(IOError, "KTBN::load: " << stream.str())
835 }
836
837 // A file written by save() carries the classification as properties: restore it
838 // directly. Otherwise fall back to fromBN(), which re-derives it from node names.
839 if (!(bn.existsProperty("KTBN.k") && bn.existsProperty("KTBN.temporal")
840 && bn.existsProperty("KTBN.atemporal")))
841 return fromBN(bn);
842
845 bool escaped = false;
846 for (const char c: csv) {
847 if (escaped) {
848 current += c;
849 escaped = false;
850 } else if (c == '\\') {
851 escaped = true;
852 } else if (c == ',') {
853 if (!current.empty()) out.insert(current);
854 current.clear();
855 } else {
856 current += c;
857 }
858 }
859 if (!current.empty()) out.insert(current);
860 };
861
862 Size k_val{};
863 try {
864 k_val = static_cast< Size >(std::stoul(bn.property("KTBN.k")));
865 } catch (const std::exception& e) {
867 "KTBN::load: malformed KTBN.k property ('" << bn.property("KTBN.k")
868 << "'): " << e.what())
869 }
871 res._bn_ = bn;
872 split(bn.property("KTBN.temporal"), res._temporal_);
873 split(bn.property("KTBN.atemporal"), res._atemporal_);
874
875 return res;
876 }
void clear()
Removes all variables and arcs, keeping the order .
Definition KTBN_tpl.h:204
static std::pair< std::string, bool > _resolveGumFormat_(std::string_view filename)
Resolves a user filename to (filepath, binary): ensures a .jgum/.bgum extension (....
Definition KTBN_tpl.h:786
static KTBN< GUM_SCALAR > fromBN(const BayesNet< GUM_SCALAR > &bn, const std::unordered_set< std::string > &atemporalNodes={}, std::vector< std::string > *warnings=nullptr)
Builds a k-DBN from an existing gum::BayesNet, reading its node names under one of two mutually exclu...
Definition KTBN_tpl.h:880

References _resolveGumFormat_(), fromBN(), GUM_ERROR, gum::GumBNReader< GUM_SCALAR >::proceed(), gum::GumBNReader< GUM_SCALAR >::showElegantErrorsAndWarnings(), gum::GumBNReader< GUM_SCALAR >::showErrorCounts(), and gum::split().

Here is the call graph for this function:

◆ nbAtemporalVars()

template<GUM_Numeric GUM_SCALAR>
INLINE Size gum::KTBN< GUM_SCALAR >::nbAtemporalVars ( ) const
Returns
The number of atemporal variables.

Definition at line 339 of file KTBN_tpl.h.

339 {
340 return _atemporal_.size();
341 }

References _atemporal_.

◆ nbTemporalVars()

template<GUM_Numeric GUM_SCALAR>
INLINE Size gum::KTBN< GUM_SCALAR >::nbTemporalVars ( ) const
Returns
The number of temporal processes.

Definition at line 334 of file KTBN_tpl.h.

334 {
335 return _temporal_.size();
336 }

References _temporal_.

◆ nodes()

template<GUM_Numeric GUM_SCALAR>
std::vector< std::pair< std::string, int > > gum::KTBN< GUM_SCALAR >::nodes ( ) const
Returns
All nodes as (base, slice) pairs (atemporal nodes use KTBN::ATEMPORAL).

Definition at line 344 of file KTBN_tpl.h.

344 {
346 result.reserve(size());
347 for (const auto& a: _atemporal_)
348 result.emplace_back(a, ATEMPORAL);
349 for (const auto& p: _temporal_)
350 for (Size t = 0; t < _k_; ++t)
351 result.emplace_back(p, static_cast< int >(t));
352 return result;
353 }

References _atemporal_, _k_, _temporal_, ATEMPORAL, and size().

Referenced by _timeSlicesToDot_().

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

◆ operator=() [1/2]

template<GUM_Numeric GUM_SCALAR>
KTBN< GUM_SCALAR > & gum::KTBN< GUM_SCALAR >::operator= ( const KTBN< GUM_SCALAR > & source)

Copy assignment operator.

Definition at line 156 of file KTBN_tpl.h.

156 {
157 if (this != &source) {
159 _k_ = source._k_;
160 _bn_ = source._bn_;
163 }
164 return *this;
165 }

References KTBN(), _atemporal_, _bn_, _k_, and _temporal_.

Here is the call graph for this function:

◆ operator=() [2/2]

template<GUM_Numeric GUM_SCALAR>
KTBN< GUM_SCALAR > & gum::KTBN< GUM_SCALAR >::operator= ( KTBN< GUM_SCALAR > && source)
noexcept

Move assignment operator.

Definition at line 168 of file KTBN_tpl.h.

168 {
169 if (this != &source) {
171 _k_ = source._k_;
175 }
176 return *this;
177 }

References KTBN(), _atemporal_, _bn_, _k_, and _temporal_.

Here is the call graph for this function:

◆ parents() [1/2]

template<GUM_Numeric GUM_SCALAR>
std::vector< std::pair< std::string, int > > gum::KTBN< GUM_SCALAR >::parents ( std::string_view base,
int slice ) const

Parents of a node as (base, slice) pairs (ATEMPORAL if atemporal).

Definition at line 356 of file KTBN_tpl.h.

357 {
359 }

References _bn_, _determineNodeSet_(), and _validateVariable_().

Referenced by fillCPT(), fillCPT(), parents(), and unroll().

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

◆ parents() [2/2]

template<GUM_Numeric GUM_SCALAR>
std::vector< std::pair< std::string, int > > gum::KTBN< GUM_SCALAR >::parents ( std::string_view node_name) const

Returns the parents using an engine name ("X[1]", "C", …).

Definition at line 363 of file KTBN_tpl.h.

363 {
364 const auto [b, s] = _determineNode_(std::string{node_name});
365 return parents(b, s);
366 }

References _determineNode_(), and parents().

Here is the call graph for this function:

◆ save()

template<GUM_Numeric GUM_SCALAR>
void gum::KTBN< GUM_SCALAR >::save ( std::string_view filename) const

Saves the template in the GUM format (text .jgum or binary .bgum).

The extension selects the format: .jgum writes the text (JSON) variant, anything else the binary (msgpack) one, gaining a .bgum extension if missing. The temporal/atemporal classification and the order \(k\) are stored as properties, so load() reconstructs the k-DBN exactly — including k=1 processes and bracket-named atemporal variables, which fromBN() cannot disambiguate from node names alone.

Parameters
filenameThe destination file.

Definition at line 796 of file KTBN_tpl.h.

796 {
798
799 // Persist the temporal/atemporal classification (and k) as BN properties so that
800 // load() restores the k-DBN exactly, instead of re-deriving it heuristically from
801 // node names (ambiguous for k=1 processes and bracket-named atemporal variables).
802 // Separator is ','; '\' and ',' inside names are backslash-escaped so any name is safe.
805 for (const auto& n: names) {
806 if (!out.empty()) out += ',';
807 for (const char c: n) {
808 if (c == '\\' || c == ',') out += '\\';
809 out += c;
810 }
811 }
812 return out;
813 };
814
816 annotated.setProperty("KTBN.k", std::to_string(_k_));
817 annotated.setProperty("KTBN.temporal", join(_temporal_));
818 annotated.setProperty("KTBN.atemporal", join(_atemporal_));
820 writer.write(filepath, annotated);
821 }

References _atemporal_, _bn_, _k_, _resolveGumFormat_(), _temporal_, and gum::BNWriter< GUM_SCALAR >::write().

Here is the call graph for this function:

◆ size()

template<GUM_Numeric GUM_SCALAR>
INLINE Size gum::KTBN< GUM_SCALAR >::size ( ) const
Returns
The number of nodes in the template (all slices).

Definition at line 189 of file KTBN_tpl.h.

189 {
190 return _bn_.size();
191 }

References _bn_.

Referenced by nodes().

Here is the caller graph for this function:

◆ sizeArcs()

template<GUM_Numeric GUM_SCALAR>
INLINE Size gum::KTBN< GUM_SCALAR >::sizeArcs ( ) const
Returns
The number of arcs in the template.

Definition at line 194 of file KTBN_tpl.h.

194 {
195 return _bn_.sizeArcs();
196 }

References _bn_.

◆ summaryGraph()

template<GUM_Numeric GUM_SCALAR>
std::string gum::KTBN< GUM_SCALAR >::summaryGraph ( ) const

Returns the Graphviz DOT string of the summary graph: the projection of the transition kernel alone (the pattern that actually repeats through time when unrolling), not the whole template.

Every process (temporal or atemporal) becomes a single node. Only arcs whose head lies in the last time slice ( \(k-1\)) are kept – an arc between two earlier slices belongs to the initial-condition structure (slices \(0, \ldots, k-2\)), not to the repeated pattern, and is dropped. Several arcs may connect the same pair of nodes when the kernel depends on more than one lag (e.g. both X[k-2] and X[k-3] pointing to X[k-1]): each is kept and labelled with its lag (head slice minus tail slice). An arc from an atemporal variable has no lag and is left unlabelled.

Definition at line 1219 of file KTBN_tpl.h.

1219 {
1221 baseNames.insert(_atemporal_.begin(), _atemporal_.end());
1222
1223 const int lastSlice = static_cast< int >(_k_) - 1;
1225 for (const auto& arc: _bn_.arcs()) {
1226 const auto [tailBase, tailSlice] = _decodeName_(_bn_.variable(arc.tail()).name());
1227 const auto [headBase, headSlice] = _decodeName_(_bn_.variable(arc.head()).name());
1228 if (headSlice != lastSlice) continue; // not part of the repeated transition kernel
1229
1230 const int lag = (tailSlice == ATEMPORAL) ? ATEMPORAL : headSlice - tailSlice;
1231 edges.emplace(tailBase, headBase, lag);
1232 }
1233
1235 dot << "digraph KTBN {\n";
1236 dot << " rankdir=LR;\n";
1237 dot << " node [color=\"#000000\", fillcolor=white, style=filled];\n\n";
1238
1239 for (const auto& base: baseNames)
1240 dot << " \"" << _escapeDot_(base) << "\";\n";
1241 dot << "\n";
1242
1243 for (const auto& [tailBase, headBase, lag]: edges) {
1244 dot << " \"" << _escapeDot_(tailBase) << "\" -> \"" << _escapeDot_(headBase) << "\"";
1245 if (lag != ATEMPORAL) dot << " [label=\"" << lag << "\"]";
1246 dot << ";\n";
1247 }
1248
1249 dot << "}\n";
1250 return dot.str();
1251 }

References _atemporal_, _bn_, _decodeName_(), _escapeDot_(), _k_, _temporal_, and ATEMPORAL.

Here is the call graph for this function:

◆ temporalVarNames()

template<GUM_Numeric GUM_SCALAR>
INLINE const std::unordered_set< std::string > & gum::KTBN< GUM_SCALAR >::temporalVarNames ( ) const
Returns
The set of temporal process base names.

Definition at line 324 of file KTBN_tpl.h.

324 {
325 return _temporal_;
326 }

References _temporal_.

◆ timeSlice()

template<GUM_Numeric GUM_SCALAR>
INLINE int gum::KTBN< GUM_SCALAR >::timeSlice ( const DiscreteVariable & var) const

The time slice of var, or ATEMPORAL if it is atemporal.

Exceptions
NotFoundif var is not a node of this k-DBN.

Definition at line 474 of file KTBN_tpl.h.

474 {
475 _bn_.idFromName(var.name()); // throws NotFound if var is not in this k-DBN
476 return _determineNode_(var.name()).second;
477 }

References _bn_, _determineNode_(), and gum::Variable::name().

Here is the call graph for this function:

◆ toBN()

template<GUM_Numeric GUM_SCALAR>
BayesNet< GUM_SCALAR > gum::KTBN< GUM_SCALAR >::toBN ( ) const
Returns
A deep copy of the underlying template as a gum::BayesNet.

Definition at line 692 of file KTBN_tpl.h.

692 {
694 }

References _bn_.

◆ toDot()

template<GUM_Numeric GUM_SCALAR>
std::string gum::KTBN< GUM_SCALAR >::toDot ( ) const

Returns a Graphviz DOT string with one cluster per time slice.

Definition at line 1119 of file KTBN_tpl.h.

1119 {
1120 return _timeSlicesToDot_(_bn_, false);
1121 }
std::string _timeSlicesToDot_(const BayesNet< GUM_SCALAR > &bn, bool highlightReplicated) const
Renders bn as time-slice-clustered DOT. Shared engine behind toDot() (on _bn_) and toUnrolledDot() (o...
Definition KTBN_tpl.h:1146

References _bn_, and _timeSlicesToDot_().

Here is the call graph for this function:

◆ toString()

template<GUM_Numeric GUM_SCALAR>
std::string gum::KTBN< GUM_SCALAR >::toString ( ) const
Returns
A human-readable description of the k-DBN.

Definition at line 1084 of file KTBN_tpl.h.

1084 {
1085 const auto join = [](const std::vector< std::string >& v) {
1086 std::string s;
1087 for (const auto& n: v) {
1088 if (!s.empty()) s += ", ";
1089 s += n;
1090 }
1091 return s;
1092 };
1093
1096
1098 for (const auto& arc: _bn_.arcs())
1099 arcs += std::format(" {} -> {}\n",
1100 _bn_.variable(arc.tail()).name(),
1101 _bn_.variable(arc.head()).name());
1102
1103 return std::format("k-TBN (k={}, {} nodes, {} arcs)\n"
1104 " temporal processes ({}): {}\n"
1105 " atemporal variables ({}): {}\n"
1106 "\n arcs ({}):\n{}",
1107 _k_,
1108 _bn_.size(),
1109 _bn_.sizeArcs(),
1110 _temporal_.size(),
1112 _atemporal_.size(),
1114 _bn_.sizeArcs(),
1115 arcs);
1116 }
Size sizeArcs() const
Definition KTBN_tpl.h:194

References _atemporal_, _bn_, _k_, _temporal_, and arcs().

Here is the call graph for this function:

◆ toUnrolledDot()

template<GUM_Numeric GUM_SCALAR>
std::string gum::KTBN< GUM_SCALAR >::toUnrolledDot ( Size T,
bool highlightReplicated = false ) const

Returns a Graphviz DOT string of the k-DBN unrolled over T time slices.

Parameters
TTotal number of time slices to display. Must be \(\geq k\).
highlightReplicatedIf true, shade slices \(\geq k\) (lightcyan) differently from the template slices \(0, \ldots, k-1\) (gray).
Exceptions
OperationNotAllowedif T < k.

Definition at line 1124 of file KTBN_tpl.h.

1124 {
1125 if (T < _k_)
1126 GUM_ERROR(OperationNotAllowed, "toUnrolledDot: T=" << T << " must be >= k=" << _k_ << ".")
1128 }
BayesNet< GUM_SCALAR > unroll(Size nbTimeSlices) const
Unrolls the k-DBN into a standard gum::BayesNet.
Definition KTBN_tpl.h:697

References _k_, _timeSlicesToDot_(), GUM_ERROR, and unroll().

Here is the call graph for this function:

◆ unroll()

template<GUM_Numeric GUM_SCALAR>
BayesNet< GUM_SCALAR > gum::KTBN< GUM_SCALAR >::unroll ( Size nbTimeSlices) const

Unrolls the k-DBN into a standard gum::BayesNet.

The result has exactly nbTimeSlices time slices. Slices \(0, \ldots, k-1\) are copied verbatim from the template; every additional slice \(t \geq k\) replicates the incoming pattern of slice \(k-1\) (the transition kernel), shifting each temporal parent's slice accordingly so that lags are preserved. Variables are named with the same base[slice] bracket-notation convention (atemporal variables keep their name).

Parameters
nbTimeSlicesTotal number of time slices of the unrolled network.
Exceptions
OperationNotAllowedif nbTimeSlices is smaller than \(k\).

Definition at line 697 of file KTBN_tpl.h.

697 {
698 if (nbTimeSlices < _k_)
700 "Cannot unroll over " << nbTimeSlices << " slices: fewer than the order k=" << _k_
701 << ".")
702
704 const int kernelSlice = static_cast< int >(_k_ - 1);
705
706 // 1. atemporal variables (kept as-is)
708 unrolled.add(_bn_.variable(_bn_.idFromName(a)));
709 }
710
711 // 2. temporal variables, instantiated for every slice 0..nbTimeSlices-1
712 for (const auto& p: _temporal_) {
714 = _bn_.variable(_bn_.idFromName(_encode_(p, kernelSlice)));
715 for (Size t = 0; t < nbTimeSlices; ++t) {
716 // clone() to rename; BayesNet::add() clones again internally (unavoidable via public API).
718 clone->setName(_encode_(p, static_cast< int >(t)));
720 }
721 }
722
723 // 3. template arcs (slices 0..k-1) are copied verbatim: the engine names of
724 // the template already match the unrolled names for those slices.
725 for (const auto& arc: _bn_.arcs()) {
726 unrolled.addArc(_bn_.variable(arc.tail()).name(), _bn_.variable(arc.head()).name());
727 }
728
729 // 4. CPTs of the template nodes (slices 0..k-1): copied by name.
730 for (const NodeId n: _bn_.nodes()) {
731 unrolled.cpt(_bn_.variable(n).name()).fillWith(_bn_.cpt(n));
732 }
733
734 // 5. transition kernel: for each extra slice t = k..nbTimeSlices-1,
735 // add arcs and fill the CPT in one pass using lags computed once per process.
736 for (const auto& p: _temporal_) {
737 const NodeId lastSliceNodeId = _bn_.idFromName(_encode_(p, kernelSlice));
739
740 // (parBase, lag): lag == ATEMPORAL for static parents, otherwise lag = (k-1) - parSlice.
742 for (const auto& [parBase, parSlice]: parents(p, kernelSlice)) {
743 const int lag = (parSlice == ATEMPORAL) ? ATEMPORAL : kernelSlice - parSlice;
744 lags.emplace_back(parBase, lag);
745 }
746
749
750 for (Size t = _k_; t < nbTimeSlices; ++t) {
751 const std::string child = _encode_(p, static_cast< int >(t));
752
753 // Add arcs and build the unrolled->template name mapping simultaneously.
756 for (const auto& [parBase, lag]: lags) {
757 if (lag == ATEMPORAL) {
760 } else {
761 const std::string parName = _encode_(parBase, static_cast< int >(t) - lag);
764 }
765 }
766
767 // Fill the CPT using the mapping built above.
770 templateVarNames.reserve(unrolledCpt.nbrDim());
771 for (Idx i = 0; i < unrolledCpt.nbrDim(); ++i) {
773 }
775 }
776 }
777
778 return unrolled;
779 }

References _atemporal_, _bn_, _encode_(), _k_, _temporal_, ATEMPORAL, gum::HashTable< Key, Val >::clear(), gum::DiscreteVariable::clone(), GUM_ERROR, gum::HashTable< Key, Val >::insert(), and parents().

Referenced by toUnrolledDot().

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

◆ variable() [1/2]

template<GUM_Numeric GUM_SCALAR>
INLINE const DiscreteVariable & gum::KTBN< GUM_SCALAR >::variable ( std::string_view base,
int slice ) const

Returns the gum::DiscreteVariable of a (process, slice) couple.

Use KTBN::ATEMPORAL as slice for atemporal variables.

Exceptions
NotFoundif no such variable exists.
OutOfBoundsif slice is out of \([0,k-1]\) for a temporal variable.
OperationNotAllowedif the temporal/atemporal kind does not match slice.

Definition at line 462 of file KTBN_tpl.h.

463 {
464 return _bn_.variable(_validateVariable_(base, slice));
465 }

References _bn_, and _validateVariable_().

Referenced by variable().

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

◆ variable() [2/2]

template<GUM_Numeric GUM_SCALAR>
INLINE const DiscreteVariable & gum::KTBN< GUM_SCALAR >::variable ( std::string_view node_name) const

Same, using an engine name: resolved via _determineNode_, so "X[1]" and bare "C" are both accepted.

Definition at line 468 of file KTBN_tpl.h.

468 {
469 const auto [b, s] = _determineNode_(std::string{node_name});
470 return variable(b, s);
471 }

References _determineNode_(), and variable().

Here is the call graph for this function:

Member Data Documentation

◆ _atemporal_

template<GUM_Numeric GUM_SCALAR>
std::unordered_set< std::string > gum::KTBN< GUM_SCALAR >::_atemporal_
private

◆ _bn_

template<GUM_Numeric GUM_SCALAR>
BayesNet< GUM_SCALAR > gum::KTBN< GUM_SCALAR >::_bn_
private

◆ _k_

template<GUM_Numeric GUM_SCALAR>
Size gum::KTBN< GUM_SCALAR >::_k_
private

◆ _temporal_

template<GUM_Numeric GUM_SCALAR>
std::unordered_set< std::string > gum::KTBN< GUM_SCALAR >::_temporal_
private

◆ ATEMPORAL

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

Conventional time-slice value denoting an atemporal (static) variable.

Definition at line 200 of file KTBN.h.

Referenced by gum::learning::KTBNDatabaseGenerator< GUM_SCALAR >::_build_(), gum::learning::KTBNLearner< GUM_SCALAR >::_build_(), gum::learning::IKTBNLearner< GUM_SCALAR >::_checkArcTemporallyFeasible_(), gum::learning::KTBNDatabaseGenerator< GUM_SCALAR >::_decode_(), _decodeName_(), _determineNode_(), gum::learning::IKTBNLearner< GUM_SCALAR >::_determineNode_(), _determineNodesFromBN_(), gum::learning::KTBNDatabaseGenerator< GUM_SCALAR >::_drawSamples_(), _encode_(), gum::learning::IKTBNLearner< GUM_SCALAR >::_encode_(), gum::learning::KTBNLearner< GUM_SCALAR >::_forEachAllSlicesPair_(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_forEachScoredNode_(), gum::learning::KTBNLearner< GUM_SCALAR >::_forOwningLearner_(), gum::KTBNGenerator< GUM_SCALAR >::_legalArcs_(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_raiseKMinForSlice_(), _timeSlicesToDot_(), _validateAdd_(), _validateVariable_(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_verifyBase_(), addArc(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::addForbiddenArcAllSlices(), gum::learning::KTBNLearner< GUM_SCALAR >::addNoChildrenNode(), gum::learning::KTBNLearner< GUM_SCALAR >::addNoParentNode(), gum::learning::KTBNLearner< GUM_SCALAR >::addPossibleEdge(), changeVariableName(), gum::learning::KTBNLearner< GUM_SCALAR >::domainSize(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::eraseForbiddenArcAllSlices(), gum::learning::KTBNLearner< GUM_SCALAR >::eraseNoChildrenNode(), gum::learning::KTBNLearner< GUM_SCALAR >::eraseNoParentNode(), gum::learning::KTBNLearner< GUM_SCALAR >::erasePossibleEdge(), gum::learning::KTBNLearner< GUM_SCALAR >::learnParameters(), nodes(), gum::learning::KTBNDatabaseGenerator< GUM_SCALAR >::setVarOrderTopological(), gum::learning::KTBNLearner< GUM_SCALAR >::state(), summaryGraph(), and unroll().


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