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

Learns a k-TBN (structure and/or parameters) from trajectory CSVs. More...

#include <agrum/KTBN/learning/KTBNLearner.h>

Inheritance diagram for gum::learning::KTBNLearner< GUM_SCALAR >:
[legend]
Collaboration diagram for gum::learning::KTBNLearner< GUM_SCALAR >:
[legend]

Public Member Functions

Constructors / Destructors
 KTBNLearner (std::string_view dirPath, std::string_view csvBaseName, Size nbSamples, Size k, const std::unordered_set< std::string > &atemporalVars, const std::vector< std::string > &missingSymbols={"?"}, bool induceTypes=true, bool ignoreMissingSymbols=false)
 Structure-learning constructor — variable roles supplied explicitly.
 KTBNLearner (std::string_view dirPath, std::string_view csvBaseName, Size nbSamples, Size k, const std::vector< std::string > &missingSymbols={"?"}, bool induceTypes=true, bool ignoreMissingSymbols=false)
 Structure-learning constructor — atemporal variables inferred from the CSVs.
 KTBNLearner (std::string_view dirPath, std::string_view csvBaseName, Size nbSamples, Size k, const BayesNet< GUM_SCALAR > &bn, const std::unordered_set< std::string > &atemporalVars={}, const std::vector< std::string > &missingSymbols={"?"}, bool ignoreMissingSymbols=false)
 Variable-schema constructor — types and domains supplied via a BN.
 ~KTBNLearner ()
 destructor
Main learning methods
KTBN< GUM_SCALAR > learnKTBN () override
 Full learning (structure + CPTs). Mirrors BNLearner::learnBN().
KTBN< GUM_SCALAR > learnParameters (const KTBN< GUM_SCALAR > &structure, bool takeIntoAccountScore=true)
 CPTs only, using the arc structure of structure. structure must have the same base variables (names and domains) as those used to construct this learner; mismatches throw at learn time.
Score selection (applied to all internal learners)
KTBNLearner< GUM_SCALAR > & useScoreAIC () override
 Returns a warning string if the current score and prior are incompatible, empty string otherwise.
KTBNLearner< GUM_SCALAR > & useScoreBD () override
 Returns a warning string if the current score and prior are incompatible, empty string otherwise.
KTBNLearner< GUM_SCALAR > & useScoreBDeu () override
 Returns a warning string if the current score and prior are incompatible, empty string otherwise.
KTBNLearner< GUM_SCALAR > & useScoreBIC () override
 Returns a warning string if the current score and prior are incompatible, empty string otherwise.
KTBNLearner< GUM_SCALAR > & useScoreLog2Likelihood () override
 Returns a warning string if the current score and prior are incompatible, empty string otherwise.
KTBNLearner< GUM_SCALAR > & useScoreMDL () override
 Returns a warning string if the current score and prior are incompatible, empty string otherwise.
void useScorefNML () override
 Returns a warning string if the current score and prior are incompatible, empty string otherwise.
std::string checkScorePriorCompatibility () const
 Returns a warning string if the current score and prior are incompatible, empty string otherwise.
Algorithm selection (applied to all internal learners)
KTBNLearner< GUM_SCALAR > & useGreedyHillClimbing () override
KTBNLearner< GUM_SCALAR > & useExtendedGreedyHillClimbing () override
KTBNLearner< GUM_SCALAR > & useLocalSearchWithTabuList (Size tabu_size=100, Size nb_decrease=2) override
KTBNLearner< GUM_SCALAR > & useMIIC () override
MIIC correction (applied to all internal learners)
KTBNLearner< GUM_SCALAR > & useNMLCorrection () override
 Engine-name pairs (tail, head) of arcs flagged as hiding a latent variable by MIIC (merged from all internal learners). Returned as names rather than Arc/NodeId pairs because the internal learners live in independent NodeId spaces, so a bare NodeId would be ambiguous.
KTBNLearner< GUM_SCALAR > & useMDLCorrection () override
 Engine-name pairs (tail, head) of arcs flagged as hiding a latent variable by MIIC (merged from all internal learners). Returned as names rather than Arc/NodeId pairs because the internal learners live in independent NodeId spaces, so a bare NodeId would be ambiguous.
KTBNLearner< GUM_SCALAR > & useNoCorrection () override
 Engine-name pairs (tail, head) of arcs flagged as hiding a latent variable by MIIC (merged from all internal learners). Returned as names rather than Arc/NodeId pairs because the internal learners live in independent NodeId spaces, so a bare NodeId would be ambiguous.
std::vector< std::pair< std::string, std::string > > latentVariables () const
 Engine-name pairs (tail, head) of arcs flagged as hiding a latent variable by MIIC (merged from all internal learners). Returned as names rather than Arc/NodeId pairs because the internal learners live in independent NodeId spaces, so a bare NodeId would be ambiguous.
Prior selection (applied to all internal learners)
KTBNLearner< GUM_SCALAR > & useSmoothingPrior (double weight=1.0) override
Structural constraints (base names, translated to the relevant internal learner(s))
KTBNLearner< GUM_SCALAR > & addForbiddenArc (std::string_view tailNode, std::string_view headNode) override
 Forbid tailNode from ever parenting headNode (engine names, e.g. "X[1]", "C").
KTBNLearner< GUM_SCALAR > & addForbiddenArc (std::string_view tailBase, int tailSlice, std::string_view headBase, int headSlice) override
 Forbid one (base, slice) -> (base, slice) arc (KTBN::ATEMPORAL for static). A backward arc (tailSlice > headSlice) is accepted but has no effect: such an arc is already impossible, so forbidding it is a harmless no-op.
KTBNLearner< GUM_SCALAR > & eraseForbiddenArc (std::string_view tailNode, std::string_view headNode) override
 Undo a previous addForbiddenArc (engine names, e.g. "X[1]", "C").
KTBNLearner< GUM_SCALAR > & eraseForbiddenArc (std::string_view tailBase, int tailSlice, std::string_view headBase, int headSlice) override
 Undo a previous addForbiddenArc for a specific (base, slice) pair.
KTBNLearner< GUM_SCALAR > & addMandatoryArc (std::string_view tailNode, std::string_view headNode) override
 Force tailNode to be a parent of headNode (engine names, e.g. "X[1]", "C").
KTBNLearner< GUM_SCALAR > & addMandatoryArc (std::string_view tailBase, int tailSlice, std::string_view headBase, int headSlice) override
 Force one arc, lag stated explicitly via slices (KTBN::ATEMPORAL for static).
KTBNLearner< GUM_SCALAR > & eraseMandatoryArc (std::string_view tailNode, std::string_view headNode) override
 Undo a previous addMandatoryArc (engine names, e.g. "X[1]", "C").
KTBNLearner< GUM_SCALAR > & eraseMandatoryArc (std::string_view tailBase, int tailSlice, std::string_view headBase, int headSlice) override
 Undo a previous addMandatoryArc.
KTBNLearner< GUM_SCALAR > & addForbiddenIntraSliceArc (std::string_view tailBase, std::string_view headBase) override
 Forbid tailBase -> headBase at every intra-slice position (i.e. tailBase[t] -> headBase[t] for all t in [0, k-1]).
KTBNLearner< GUM_SCALAR > & eraseForbiddenIntraSliceArc (std::string_view tailBase, std::string_view headBase) override
 Undo a previous addForbiddenIntraSliceArc.
KTBNLearner< GUM_SCALAR > & addForbiddenArcAllSlices (std::string_view tailBase, std::string_view headBase) override
 Forbid tailBase -> headBase at every causally-possible slice pair (every lag, not just matching slices): tailBase can never be an ancestor of headBase in the learned k-TBN. A temporal->atemporal pair is a no-op (already structurally impossible).
KTBNLearner< GUM_SCALAR > & eraseForbiddenArcAllSlices (std::string_view tailBase, std::string_view headBase) override
 Undo a previous addForbiddenArcAllSlices.
KTBNLearner< GUM_SCALAR > & addNoParentNode (std::string_view base, int slice) override
 Declare a single (base, slice) node as a root (no parents).
KTBNLearner< GUM_SCALAR > & addNoParentNode (std::string_view name) override
 Declare a single node (bracket notation, e.g. "X[2]" or "C") as a root.
KTBNLearner< GUM_SCALAR > & eraseNoParentNode (std::string_view base, int slice) override
 Undo a previous addNoParentNode for a single (base, slice) node.
KTBNLearner< GUM_SCALAR > & eraseNoParentNode (std::string_view name) override
 Undo addNoParentNode for a node given by bracket notation.
KTBNLearner< GUM_SCALAR > & addNoChildrenNode (std::string_view base, int slice) override
 Declare a single (base, slice) node as a leaf (no children).
KTBNLearner< GUM_SCALAR > & addNoChildrenNode (std::string_view name) override
 Declare a single node (bracket notation, e.g. "X[2]" or "C") as a leaf.
KTBNLearner< GUM_SCALAR > & eraseNoChildrenNode (std::string_view base, int slice) override
 Undo a previous addNoChildrenNode for a single (base, slice) node.
KTBNLearner< GUM_SCALAR > & eraseNoChildrenNode (std::string_view name) override
 Undo addNoChildrenNode for a node given by bracket notation.
KTBNLearner< GUM_SCALAR > & addPossibleEdge (std::string_view tailBase, int tailSlice, std::string_view headBase, int headSlice) override
 Add a candidate edge for MIIC (only edges explicitly listed are explored).
KTBNLearner< GUM_SCALAR > & addPossibleEdge (std::string_view tail, std::string_view head) override
 Add a candidate edge for MIIC using engine names (e.g. "X[1]", "C").
KTBNLearner< GUM_SCALAR > & erasePossibleEdge (std::string_view tailBase, int tailSlice, std::string_view headBase, int headSlice) override
 Undo a previous addPossibleEdge.
KTBNLearner< GUM_SCALAR > & erasePossibleEdge (std::string_view tail, std::string_view head) override
 Undo a previous addPossibleEdge using engine names (e.g. "X[1]", "C").
KTBNLearner< GUM_SCALAR > & allowArcAdditions (bool allow=true) override
 Allow or forbid arc additions during structure search.
KTBNLearner< GUM_SCALAR > & allowArcDeletions (bool allow=true) override
 Allow or forbid arc deletions during structure search.
KTBNLearner< GUM_SCALAR > & allowArcReversals (bool allow=true) override
 Allow or forbid arc reversals during structure search.
KTBNLearner< GUM_SCALAR > & setMaxIndegree (Size max_indegree) override
 Cap the number of parents of any single node.
Diagnostics
Size k () const
 Order \(k\) of the k-TBN being learned.
Size nbCols () const
 Number of columns in each CSV, i.e. of base variables (temporal + atemporal).
std::vector< Size > nbRows () const
 Number of time steps in each trajectory CSV (one entry per sample, in load order). This is the raw trajectory length; the transition table's sliding-window row count for trajectory i is nbRows()[i] - k + 1.
bool isConstraintBased () const
 True if the current structure-learning algorithm is constraint-based (e.g. MIIC).
bool isScoreBased () const
 True if the current structure-learning algorithm is score-based (e.g. BIC, AIC).
std::string toString () const
 Human-readable summary of the learner's current configuration.
std::vector< std::tuple< std::string, std::string, std::string > > state () const
 Settings as a vector of (key, value, comment) tuples (mirrors BNLearner::state()).
void copyState (const KTBNLearner< GUM_SCALAR > &learner)
 Copy all score/algorithm/prior/constraint settings from another KTBNLearner (does not copy the database).
Database accessors
Size nbSamples () const
 Number of trajectory CSV files loaded (the constructor's nbSamples).
bool hasMissingValues () const
 True if any internal database contains missing values.
Size nbDroppedRows () const
 Number of rows dropped from the internal databases because they carried a missing symbol.
bool isIgnoringMissingSymbols () const
 Whether build() drops the rows carrying a missing symbol.
std::vector< std::string > names () const
 Base names (no slice suffix), one entry per base variable (temporal or atemporal), in the original CSV header order.
std::vector< std::size_t > domainSizes () const
 Domain sizes of the base variables, in the same column order as names().
Size domainSize (std::string_view base) const
 Domain size of the base variable base (e.g. "X", "C"). Engine names (e.g. "X[1]") are also accepted.

Private Member Functions

template<class F>
void _forEachLearner_ (F &&f)
 Apply f to each present internal learner (the atemporal one only when it exists). Factors out the fan-out shared by every score / algorithm / prior / correction / graph-change setter.
template<class F>
void _forOwningLearner_ (std::string_view tail, std::string_view head, F &&f)
 Apply f to the ONE internal learner that can learn the arc tail -> head, chosen by its head: a slice-(k-1) head belongs to the transition learner, an atemporal->atemporal arc to the atemporal learner, any other head (a past temporal slice) to the initial learner. The three cases are exclusive because build() forces every other node root in the learners that do not own it, so no arc is ever learnable in two of them.
template<class F>
void _forEachAllSlicesPair_ (std::string_view tailBase, std::string_view headBase, F &&f) const
 Apply f(tailSlice, headSlice) to every causally-possible slice pair of an all-slices constraint between tailBase and headBase. Four shapes: atemporal->atemporal is one pair; an atemporal tail reaches every slice of the head; a temporal tail into an atemporal head is structurally impossible and yields none; two temporal bases give every pair with tailSlice <= headSlice, i.e. every lag.
void _build_ (std::string_view dirPath, std::string_view csvBaseName, Size nbSamples, const std::vector< std::string > &missingSymbols)
 reads every trajectory, builds the three DatabaseTables (sliding window, initial-slice flattening, atemporal columns), constructs the BNLearners from them and applies the automatic temporal constraints (slice order, root past slices, temporal->atemporal forbids). Relies solely on prior_ktbn (already fully populated by the time it runs): the column order is read from the first trajectory's header and the atemporal columns are derived from prior_ktbn.atemporalVarNames(). Shared by both constructors.
const std::unordered_set< std::string > & _atemporalVarNames_ () const override
 atemporal base names for IKTBNLearner's shared encode/_determineNode_; read straight from the prior k-TBN (the single source of truth).
bool _isKnownBase_ (std::string_view base) const override
 whether base is one of this learner's variables; read straight from the prior k-TBN, like atemporalVarNames() above.
KTBN< GUM_SCALAR > _assemble_ (const BayesNet< GUM_SCALAR > &transitionBN, const BayesNet< GUM_SCALAR > &initialBN, const BayesNet< GUM_SCALAR > &atemporalBN) const
 glues the three parameter-learned BNs into a single k-TBN
 KTBNLearner (const KTBNLearner< GUM_SCALAR > &)=delete
 KTBNLearner (KTBNLearner< GUM_SCALAR > &&)=delete
KTBNLearner< GUM_SCALAR > & operator= (const KTBNLearner< GUM_SCALAR > &)=delete
KTBNLearner< GUM_SCALAR > & operator= (KTBNLearner< GUM_SCALAR > &&)=delete

Static Private Member Functions

static std::unordered_set< std::string > _inferAtemporalVars_ (std::string_view dirPath, std::string_view csvBaseName, Size nbSamples, Size k, const std::vector< std::string > &missingSymbols)
 checks k >= 2, then delegates the actual scan to the shared IKTBNLearner::scanConstantColumns() (also used by KTBNAdaptiveLearner, hence not duplicated here). The check happens first and here, not inside the shared scan: called once, in the member-initialiser list of the atemporal-inferring constructor, before prior_ktbn exists — before that constructor's body runs the equivalent check in buildPriorFromCSV — so without it here too, a bad k would only be caught after a wasted scan of every trajectory.
static KTBN< GUM_SCALAR > _buildPriorFromCSV_ (std::string_view dirPath, std::string_view csvBaseName, Size k, const std::unordered_set< std::string > &atemporalVars, const std::vector< std::string > &missingSymbols, bool induceTypes)
 called in the member-initialiser list of the k-CSV constructor: opens the first trajectory CSV, uses a temporary BNLearner to type the base variables (induceTypes promotes numeric columns to range/integer), builds and returns a KTBN with the right temporal/atemporal classification. Must be static because it is called before the object exists.
static KTBN< GUM_SCALAR > _buildPriorFromBN_ (Size k, const BayesNet< GUM_SCALAR > &bn, const std::unordered_set< std::string > &atemporalVars)
 called in the member-initialiser list of the BN constructor: builds and returns a KTBN whose variables are copied from bn (one node per base variable, right types/domains) and classified as temporal/atemporal according to atemporalVars. Must be static because it is called before the object exists.

Private Attributes

std::unique_ptr< BNLearner< GUM_SCALAR > > _transitionLearner_
 learns the transition kernel (arcs arriving at slice k-1)
std::unique_ptr< BNLearner< GUM_SCALAR > > _initialLearner_
 learns the initial slices 0..k-2
std::unique_ptr< BNLearner< GUM_SCALAR > > _atemporalLearner_
 learns the atemporal variables (arcs atemporal -> atemporal)
bool _ignoreMissingSymbols_ {false}
 prior k-TBN: the single source of truth for k, variable domains, temporal/atemporal classification and engine names. Everything else (base lists, column names) is derived from it on demand. Analogous to the prior BayesNet stored in BNLearner when a reference BN is given. whether build() drops incomplete rows rather than refusing the data
KTBN< GUM_SCALAR > _prior_ktbn_
Size _nbDroppedRows_ {0}
 number of time steps (rows) in each trajectory CSV, in load order. rows build() dropped because they carried a missing symbol
std::vector< Size > _nbTimeSlices_
 Captured once by build() and exposed by nbRows(). This is the raw trajectory length, not the sliding-window row count of the transition table (which is nbTimeSlices[i] - k + 1 summed over trajectories).
Size _nbTemporalPossibleEdges_ = 0
 counts of currently-active possible edges, split by kind: edges with at least one temporal endpoint, and edges between two atemporal variables. Maintained by add/erasePossibleEdge(). When a whitelist is active (temporal count > 0) but no atemporal->atemporal edge is whitelisted, the atemporal learner would otherwise stay unrestricted; learnKTBN() then suppresses it so the whitelist is honoured (no atemporal arc is produced).
Size _nbAtemporalPossibleEdges_ = 0

Name encoding: (base, slice) <-> engine name

std::string _encode_ (std::string_view base, int slice) const
 (base, slice) -> engine name ("A[1]" / atemporal engine name). Pure function, shared by every learner.
std::pair< std::string, int > _determineNode_ (const std::string &name) const
 engine name -> (base, slice); atemporal names map to KTBN::ATEMPORAL. Shared by every learner; only the atemporal test varies (via atemporalVarNames()).
void _checkArcTemporallyFeasible_ (std::string_view tail, std::string_view head, std::string_view action) const
 Reject an arc the k-TBN definition can never contain, so eraseForbiddenArc and addMandatoryArc both fail early (before any internal learner is touched) with a uniform message. action is the verb phrase completing "cannot <action> <tail> -> <head>: ..." (e.g. "force the mandatory arc", "un-forbid the arc"). Two arcs are refused: a temporal -> atemporal arc (a time-varying variable can never parent a static one) and a backward-in-time arc (head strictly before tail).
void _checkBaseIsTemporal_ (std::string_view base, std::string_view context) const
 Throw InvalidArgument unless base is a known temporal base. context completes "cannot appear in <context>" (e.g. "an intra-slice constraint", "a kernel-relative arc"): every caller rejects an atemporal base for the same underlying reason – it has no per-slice instance – so they share the sentence and vary the setting.
static void _checkMinimalOrder_ (Size order, std::string_view label)
 Throw InvalidArgument unless order is at least 2, label naming the offending parameter ("k" for the fixed-k learner, "kMax" for the adaptive one).
static std::unordered_set< std::string > _scanConstantColumns_ (std::string_view dirPath, std::string_view csvBaseName, Size nbSamples, const std::vector< std::string > &missingSymbols)
 Scans every trajectory and returns the base names classified atemporal: those whose value never changes across the rows of a single trajectory (checked independently per trajectory, so the value may still differ between trajectories). Shared by every atemporal-inferring CSV constructor (KTBNLearner, KTBNAdaptiveLearner): identical scanning logic regardless of which subclass calls it, so duplicating it per subclass would only invite the two copies to drift. Callers are responsible for validating their own arguments (e.g. k/kMax >= 2) before calling this — it does no such check itself, only opens files, so a bad argument would otherwise only be caught after a wasted scan.

Detailed Description

template<GUM_Numeric GUM_SCALAR>
class gum::learning::KTBNLearner< GUM_SCALAR >

Learns a k-TBN (structure and/or parameters) from trajectory CSVs.

See also
gum::learning::BNLearner, the static Bayesian-network counterpart whose API this class mirrors. KTBNLearner delegates score / algorithm / prior / constraint settings to three internal BNLearner instances (one per table).

Implements gum::learning::IKTBNLearner, the configuration interface shared with gum::learning::KTBNAdaptiveLearner (which learns k as well): here the setters apply each setting to the internal learners immediately, since k is fixed at construction.

Definition at line 115 of file KTBNLearner.h.

Constructor & Destructor Documentation

◆ KTBNLearner() [1/5]

template<GUM_Numeric GUM_SCALAR>
gum::learning::KTBNLearner< GUM_SCALAR >::KTBNLearner ( std::string_view dirPath,
std::string_view csvBaseName,
Size nbSamples,
Size k,
const std::unordered_set< std::string > & atemporalVars,
const std::vector< std::string > & missingSymbols = {"?"},
bool induceTypes = true,
bool ignoreMissingSymbols = false )

Structure-learning constructor — variable roles supplied explicitly.

Use this constructor when you want to learn the k-TBN structure from the data. The order \(k\) and the atemporal variable list must be given because they cannot be reliably inferred from the CSVs alone. Variable domains (modality counts) are read from the CSV data.

Internally calls _buildPriorFromCSV_() then _build_().

Parameters
dirPathDirectory holding the trajectory CSV files.
csvBaseNameStem of each file name (1-based index and .csv appended, e.g. "traj" -> "traj1.csv", "traj2.csv", ...).
nbSamplesNumber of CSV files to read.
kOrder of the k-TBN. Must be \(\geq 2\).
atemporalVarsBase names of the atemporal (static) variables. All other variables found in the CSV header are treated as temporal. Pass an empty set for "every variable is temporal"; it has no default, since omitting it selects the inferring overload below.
missingSymbolsSymbols in the CSVs to interpret as missing values.
induceTypesWhen true (default), columns whose values are all numeric are retyped (integer/range/continuous) instead of being treated as plain labels — same semantics as gum::learning::BNLearner.
ignoreMissingSymbolsWhen true, a row carrying a missing symbol is dropped rather than handed to the internal learners, which cannot cope with one (see nbDroppedRows() and its warning on the distortion this introduces). When false (the default), such data is refused outright.
Warning
A braced literal is ambiguous between this overload and the inferring one, since it could equally initialize either's fifth parameter. Name the type — std::unordered_set<std::string>{"C","D"} — or pass a named variable.

Definition at line 131 of file KTBNLearner_tpl.h.

138 :
141 k,
144 induceTypes)) {
145 // k >= 2 is already enforced by _buildPriorFromCSV_ during member initialisation
146 if (nbSamples == 0) GUM_ERROR(InvalidArgument, "KTBNLearner needs at least one sample")
148 try {
150 } catch (const gum::UnknownLabelInDatabase&) {
151 // Domains are inferred from trajectory 1 alone, so a variable whose full
152 // domain is absent there fails once a later trajectory shows a new label.
153 // Atemporal variables are the usual culprits: constant within a trajectory,
154 // they reveal only one value per file. Re-throw with a KTBN-specific hint.
157 "KTBNLearner CSV constructor: an unknown label was encountered while "
158 "reading the trajectory CSVs. The variable domains are inferred from "
159 "the first CSV alone, so any variable whose modalities are not all "
160 "present in trajectory 1 will trigger this error (atemporal variables "
161 "are especially prone: each trajectory holds a single constant value "
162 "for them, so at most one label appears in trajectory 1). "
163 "Use the BN-schema constructor "
164 "KTBNLearner(dir, base, n, k, bn, atemporals) to supply the full "
165 "variable domains explicitly.")
166 } catch (...) {
168 throw;
169 }
170 }
Learns a k-TBN (structure and/or parameters) from trajectory CSVs.
static KTBN< GUM_SCALAR > _buildPriorFromCSV_(std::string_view dirPath, std::string_view csvBaseName, Size k, const std::unordered_set< std::string > &atemporalVars, const std::vector< std::string > &missingSymbols, bool induceTypes)
called in the member-initialiser list of the k-CSV constructor: opens the first trajectory CSV,...
void _build_(std::string_view dirPath, std::string_view csvBaseName, Size nbSamples, const std::vector< std::string > &missingSymbols)
reads every trajectory, builds the three DatabaseTables (sliding window, initial-slice flattening,...
bool _ignoreMissingSymbols_
prior k-TBN: the single source of truth for k, variable domains, temporal/atemporal classification an...
Size nbSamples() const
Number of trajectory CSV files loaded (the constructor's nbSamples).
KTBN< GUM_SCALAR > _prior_ktbn_
KTBNLearner(std::string_view dirPath, std::string_view csvBaseName, Size nbSamples, Size k, const std::unordered_set< std::string > &atemporalVars, const std::vector< std::string > &missingSymbols={"?"}, bool induceTypes=true, bool ignoreMissingSymbols=false)
Structure-learning constructor — variable roles supplied explicitly.
Size k() const
Order of the k-TBN being learned.

References KTBNLearner(), _build_(), _buildPriorFromCSV_(), _ignoreMissingSymbols_, _prior_ktbn_, GUM_ERROR, k(), and nbSamples().

Referenced by KTBNLearner(), KTBNLearner(), KTBNLearner(), KTBNLearner(), KTBNLearner(), ~KTBNLearner(), addForbiddenArc(), addForbiddenArc(), addMandatoryArc(), addMandatoryArc(), addNoChildrenNode(), addNoChildrenNode(), addNoParentNode(), addNoParentNode(), addPossibleEdge(), addPossibleEdge(), allowArcAdditions(), allowArcDeletions(), allowArcReversals(), copyState(), eraseForbiddenArc(), eraseMandatoryArc(), eraseNoChildrenNode(), eraseNoChildrenNode(), eraseNoParentNode(), eraseNoParentNode(), erasePossibleEdge(), erasePossibleEdge(), operator=(), operator=(), setMaxIndegree(), useExtendedGreedyHillClimbing(), useGreedyHillClimbing(), useMDLCorrection(), useMIIC(), useNMLCorrection(), useNoCorrection(), useScoreAIC(), useScoreBD(), useScoreBDeu(), useScoreBIC(), useScoreLog2Likelihood(), useScoreMDL(), and useSmoothingPrior().

Here is the call graph for this function:

◆ KTBNLearner() [2/5]

template<GUM_Numeric GUM_SCALAR>
gum::learning::KTBNLearner< GUM_SCALAR >::KTBNLearner ( std::string_view dirPath,
std::string_view csvBaseName,
Size nbSamples,
Size k,
const std::vector< std::string > & missingSymbols = {"?"},
bool induceTypes = true,
bool ignoreMissingSymbols = false )

Structure-learning constructor — atemporal variables inferred from the CSVs.

Same as the explicit-atemporalVars constructor, except the temporal/atemporal classification is inferred instead of supplied: a base variable is classified atemporal iff its value never changes across the rows of any single trajectory. It may still differ between trajectories — this matches the model's actual semantics (constant through time, not constant across samples) rather than requiring one global value, e.g. a per-individual static covariate such as an age or a site identifier. A column with too few non-missing values to ever witness a change is optimistically classified atemporal.

This is a heuristic, not a guarantee: a genuinely temporal variable that happens to hold one value throughout every sampled trajectory (a very short horizon, or a near-deterministic process) will be misclassified as atemporal. Prefer the explicit-atemporalVars constructor whenever the classification is already known.

Warning
Variable domains are still discovered from trajectory 1 alone (see the explicit-atemporalVars constructor). An inferred atemporal variable is, by construction, one whose value may vary between trajectories — exactly the case most likely to reveal only one of several modalities in trajectory 1 and later throw UnknownLabelInDatabase. Use the BN-schema constructor if the full domain cannot be guaranteed present in the first trajectory.
Parameters
dirPathDirectory holding the trajectory CSV files.
csvBaseNameStem of each file name (1-based index and .csv appended, e.g. "traj" -> "traj1.csv", "traj2.csv", ...).
nbSamplesNumber of CSV files to read.
kOrder of the k-TBN. Must be \(\geq 2\).
missingSymbolsSymbols in the CSVs to interpret as missing values.
induceTypesWhen true (default), columns whose values are all numeric are retyped (integer/range/continuous) instead of being treated as plain labels.
ignoreMissingSymbolsWhen true, a row carrying a missing symbol is dropped rather than handed to the internal learners, which cannot cope with one (see nbDroppedRows() and its warning on the distortion this introduces). When false (the default), such data is refused outright.

Definition at line 173 of file KTBNLearner_tpl.h.

179 :
180 // _inferAtemporalVars_ checks k>=2 itself, before opening anything (see
181 // its declaration) — this initialiser-list call runs before the
182 // delegated-to constructor's own body, so that check cannot be left to
183 // _buildPriorFromCSV_ the way the explicit constructor leaves it.
184 // Delegates to the explicit-atemporalVars constructor for the rest
185 // (including the UnknownLabelInDatabase hint); _inferAtemporalVars_
186 // needs nbSamples, unlike _buildPriorFromCSV_, since one trajectory
187 // alone cannot show that a value stays constant.
190 nbSamples,
191 k,
static std::unordered_set< std::string > _inferAtemporalVars_(std::string_view dirPath, std::string_view csvBaseName, Size nbSamples, Size k, const std::vector< std::string > &missingSymbols)
checks k >= 2, then delegates the actual scan to the shared IKTBNLearner::scanConstantColumns() (also...

References KTBNLearner(), _inferAtemporalVars_(), k(), and nbSamples().

Here is the call graph for this function:

◆ KTBNLearner() [3/5]

template<GUM_Numeric GUM_SCALAR>
gum::learning::KTBNLearner< GUM_SCALAR >::KTBNLearner ( std::string_view dirPath,
std::string_view csvBaseName,
Size nbSamples,
Size k,
const BayesNet< GUM_SCALAR > & bn,
const std::unordered_set< std::string > & atemporalVars = {},
const std::vector< std::string > & missingSymbols = {"?"},
bool ignoreMissingSymbols = false )

Variable-schema constructor — types and domains supplied via a BN.

Use this constructor when the variable types and domains are already known (e.g. from a reference BN). The BN must contain exactly one node per base variable (temporal or atemporal), with the correct DiscreteVariable type and domain. Arcs in bn are ignored.

Internally calls _buildPriorFromBN_() then _build_().

Parameters
dirPathDirectory holding the trajectory CSV files.
csvBaseNameStem of each file name.
nbSamplesNumber of CSV files to read.
kOrder of the k-TBN. Must be \(\geq 2\).
bnA BayesNet with one node per base variable providing variable types and domains. Atemporal variables are identified by atemporalVars.
atemporalVarsBase names of the atemporal (static) variables present in bn.
missingSymbolsSymbols in the CSVs to interpret as missing values.
ignoreMissingSymbolsWhen true, a row carrying a missing symbol is dropped rather than handed to the internal learners, which cannot cope with one (see nbDroppedRows() and its warning on the distortion this introduces). When false (the default), such data is refused outright.

Definition at line 198 of file KTBNLearner_tpl.h.

205 :
208 // k >= 2 is already enforced by _buildPriorFromBN_ during member initialisation
209 if (nbSamples == 0) GUM_ERROR(InvalidArgument, "KTBNLearner needs at least one sample")
211 try {
213 } catch (...) {
215 throw;
216 }
217 }
static KTBN< GUM_SCALAR > _buildPriorFromBN_(Size k, const BayesNet< GUM_SCALAR > &bn, const std::unordered_set< std::string > &atemporalVars)
called in the member-initialiser list of the BN constructor: builds and returns a KTBN whose variable...

References KTBNLearner(), _build_(), _buildPriorFromBN_(), _ignoreMissingSymbols_, _prior_ktbn_, GUM_ERROR, k(), and nbSamples().

Here is the call graph for this function:

◆ ~KTBNLearner()

template<GUM_Numeric GUM_SCALAR>
gum::learning::KTBNLearner< GUM_SCALAR >::~KTBNLearner ( )

destructor

Definition at line 220 of file KTBNLearner_tpl.h.

220 {
222 }

References KTBNLearner().

Here is the call graph for this function:

◆ KTBNLearner() [4/5]

template<GUM_Numeric GUM_SCALAR>
gum::learning::KTBNLearner< GUM_SCALAR >::KTBNLearner ( const KTBNLearner< GUM_SCALAR > & )
privatedelete

References KTBNLearner().

Here is the call graph for this function:

◆ KTBNLearner() [5/5]

template<GUM_Numeric GUM_SCALAR>
gum::learning::KTBNLearner< GUM_SCALAR >::KTBNLearner ( KTBNLearner< GUM_SCALAR > && )
privatedelete

References KTBNLearner().

Here is the call graph for this function:

Member Function Documentation

◆ _assemble_()

template<GUM_Numeric GUM_SCALAR>
KTBN< GUM_SCALAR > gum::learning::KTBNLearner< GUM_SCALAR >::_assemble_ ( const BayesNet< GUM_SCALAR > & transitionBN,
const BayesNet< GUM_SCALAR > & initialBN,
const BayesNet< GUM_SCALAR > & atemporalBN ) const
private

glues the three parameter-learned BNs into a single k-TBN

Definition at line 1311 of file KTBNLearner_tpl.h.

1313 {
1314 const int k = (int)_prior_ktbn_.k();
1315
1316 // 1. Base: initialBN provides slices 0..k-2 (temporal structure + CPTs, and
1317 // atemporal→temporal arcs). Atemporal variables enter only as forced roots;
1318 // steps 4 and 6 overwrite their mutual structure and CPTs from atemporalBN
1319 // (transitionBN repeats each atemporal value k-fold — not independent samples).
1321
1322 // 2. Add the slice k-1 variables from transitionBN (absent from initialBN).
1323 for (const auto& base: _prior_ktbn_.temporalVarNames()) {
1324 const std::string name = _encode_(base, k - 1);
1325 const NodeId transId = transitionBN.idFromName(name);
1326 bn.add(transitionBN.variable(transId));
1327 }
1328
1329 // 3. Add the arcs arriving at slice k-1 from transitionBN
1330 for (const auto& base: _prior_ktbn_.temporalVarNames()) {
1331 const std::string headName = _encode_(base, k - 1);
1332 const NodeId transHead = transitionBN.idFromName(headName);
1333 for (const NodeId transParent: transitionBN.parents(transHead)) {
1334 const std::string& parentName = transitionBN.variable(transParent).name();
1335 bn.addArc(parentName, headName);
1336 }
1337 }
1338
1339 // 4. Add the atemporal -> atemporal arcs from atemporalBN. Skipped when
1340 // atemporalBN is an empty placeholder: either <= 1 atemporal variable (no
1341 // learner is built, see _build_()) or the learner was suppressed by a
1342 // temporal-only possible-edge whitelist (see learnKTBN()). Either way the
1343 // atemporal variables already sit in bn as roots, via initialBN.
1344 if (atemporalBN.size() != 0) {
1345 for (const auto& atemBase: _prior_ktbn_.atemporalVarNames()) {
1346 const NodeId atemTail = atemporalBN.idFromName(atemBase);
1347 for (const NodeId atemChild: atemporalBN.children(atemTail)) {
1348 const std::string& childName = atemporalBN.variable(atemChild).name();
1349 bn.addArc(atemBase, childName);
1350 }
1351 }
1352 }
1353
1354 // 5. Fill the slice k-1 CPTs from transitionBN
1355 for (const auto& base: _prior_ktbn_.temporalVarNames()) {
1356 const std::string name = _encode_(base, k - 1);
1357 const NodeId bnId = bn.idFromName(name);
1358 const NodeId transId = transitionBN.idFromName(name);
1359 bn.cpt(bnId).fillWith(transitionBN.cpt(transId));
1360 }
1361
1362 // 6. Fill the atemporal CPTs from atemporalBN (same guard as step 4); when
1363 // skipped, the atemporal variables keep their initialBN marginal.
1364 if (atemporalBN.size() != 0) {
1365 for (const auto& atemBase: _prior_ktbn_.atemporalVarNames()) {
1366 const NodeId bnId = bn.idFromName(atemBase);
1367 const NodeId atemId = atemporalBN.idFromName(atemBase);
1368 bn.cpt(bnId).fillWith(atemporalBN.cpt(atemId));
1369 }
1370 }
1371
1372 // 7. Convert the assembled flat BN into a KTBN: fromBN infers k from the
1373 // highest bracket index and validates temporal causality.
1375 }
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
std::string _encode_(std::string_view base, int slice) const
(base, slice) -> engine name ("A[1]" / atemporal engine name). Pure function, shared by every learner...

References gum::learning::IKTBNLearner< GUM_SCALAR >::_encode_(), _prior_ktbn_, gum::KTBN< GUM_SCALAR >::fromBN(), and k().

Referenced by learnKTBN(), and learnParameters().

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

◆ _atemporalVarNames_()

template<GUM_Numeric GUM_SCALAR>
const std::unordered_set< std::string > & gum::learning::KTBNLearner< GUM_SCALAR >::_atemporalVarNames_ ( ) const
overrideprivatevirtual

atemporal base names for IKTBNLearner's shared encode/_determineNode_; read straight from the prior k-TBN (the single source of truth).

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 1298 of file KTBNLearner_tpl.h.

1298 {
1299 return _prior_ktbn_.atemporalVarNames();
1300 }

References _prior_ktbn_.

◆ _build_()

template<GUM_Numeric GUM_SCALAR>
void gum::learning::KTBNLearner< GUM_SCALAR >::_build_ ( std::string_view dirPath,
std::string_view csvBaseName,
Size nbSamples,
const std::vector< std::string > & missingSymbols )
private

reads every trajectory, builds the three DatabaseTables (sliding window, initial-slice flattening, atemporal columns), constructs the BNLearners from them and applies the automatic temporal constraints (slice order, root past slices, temporal->atemporal forbids). Relies solely on prior_ktbn (already fully populated by the time it runs): the column order is read from the first trajectory's header and the atemporal columns are derived from prior_ktbn.atemporalVarNames(). Shared by both constructors.

Definition at line 994 of file KTBNLearner_tpl.h.

997 {
998 Size k = _prior_ktbn_.k();
999 Size nbTempVars = _prior_ktbn_.temporalVarNames().size();
1000 Size nbAtempVars = _prior_ktbn_.atemporalVarNames().size();
1001
1002 // tables are default-constructed here; translators are inserted per-column
1003 // in the i==0 block once the header (column order) is known
1007
1008 // Complete-case selection, at row granularity.
1009 //
1010 // aGrUM's structure learning refuses a database holding any missing value
1011 // outright (IBNLearner::learnDag_), so an incomplete row cannot simply be
1012 // handed over: learnKTBN() would throw on any trajectory with a gap. A row
1013 // that carries one is therefore dropped here, and everything downstream sees
1014 // a database with no missing value at all.
1015 //
1016 // This mirrors what the cross-k score already does per instance
1017 // (KTBNAdaptiveLearner::_forEachScoredNode_ skips any instance whose family
1018 // is not fully observed), so the two layers agree on which data counts.
1019 //
1020 // The unit differs per table: a transition row is a whole width-k window, so
1021 // one gap costs up to k windows; an initial or atemporal row is the whole
1022 // trajectory's contribution to that table.
1024 missingSymbols.end());
1027 for (const auto& cell: r)
1028 if (missingSet.contains(cell)) {
1030 return;
1031 }
1032 }
1033 table.insertRow(r);
1034 };
1035
1038
1042
1043 std::vector< std::string > header; // column order, captured from trajectory 1
1044 std::unordered_set< Size > atemVarsCols; // atemporal column indices
1045
1046 // reused across trajectories (capacity is kept between iterations)
1049 row.reserve(transRowSize);
1050 _nbTimeSlices_.reserve(nbSamples); // one raw length per trajectory (see nbRows())
1051
1052 for (Size i = 0; i < nbSamples; ++i) {
1053 // open the i-th trajectory (each file is opened exactly once)
1054 const std::filesystem::path file = dir / (stem + std::to_string(i + 1) + ".csv");
1056 if (!is.is_open()) GUM_ERROR(gum::IOError, "Cannot open " << file.string());
1057
1058 CSVParser parser(is, file.string());
1059 parser.next();
1060
1061 if (i == 0) {
1062 // first file only: capture the column order, derive the atemporal columns
1063 // from _prior_ktbn_, build the bracket-encoded names and finish setting up the
1064 // tables (must happen before any insertRow)
1065 const auto& rawHeader = parser.current();
1066 header.assign(rawHeader.begin(), rawHeader.end());
1067 for (std::size_t c = 0; c < header.size(); ++c)
1068 if (_prior_ktbn_.atemporalVarNames().contains(header[c])) atemVarsCols.insert(c);
1069
1070 // every schema variable must appear in the data, else _assemble_ (used
1071 // by both learnKTBN and learnParameters) would later fail with an
1072 // opaque NotFound when resolving bracket names
1073 {
1075 auto requirePresent = [&](const std::string& base) {
1076 if (!headerSet.contains(base))
1078 "schema variable '" << base << "' is absent from '" << file.string() << "'")
1079 };
1080 for (const auto& base: _prior_ktbn_.temporalVarNames())
1082 for (const auto& base: _prior_ktbn_.atemporalVarNames())
1084
1085 // conversely, every CSV column must be a known schema variable, else the
1086 // translator-insertion loop below would fail with an opaque NotFound
1087 // when resolving it against _prior_ktbn_
1088 for (const std::string& col: header)
1089 if (!_prior_ktbn_.exists(col))
1091 "CSV column '" << col << "' in '" << file.string()
1092 << "' is not declared as a variable of this KTBNLearner")
1093 }
1094
1095 // insert one translator per column into each table; every schema variable
1096 // has a concrete domain (user-supplied, or discovered by _buildPriorFromCSV_), so
1097 // insertTranslator picks the matching translator type from the variable.
1101 {
1102 auto insertTrans
1103 = [&](DatabaseTable& table, const std::string& base, int slice, std::size_t col) {
1104 table.insertTranslator(_prior_ktbn_.variable(base, slice), col, missingSymbols);
1105 };
1106
1107 // translators and their engine names are built in lockstep, column by
1108 // column, so a translator's table position and its name can never drift
1109 // apart (unlike keeping two separately-indexed passes in sync by hand).
1110 varNamesTran.reserve(transRowSize);
1111 varNamesInit.reserve(initRowSize);
1112 varNamesAtemp.reserve(atempRowSize);
1113
1114 std::size_t tcol = 0, icol = 0, acol = 0;
1115 for (std::size_t c = 0; c < header.size(); ++c) {
1116 const int slice = atemVarsCols.contains(c) ? KTBN< GUM_SCALAR >::ATEMPORAL : 0;
1118 varNamesTran.push_back(_encode_(header[c], slice));
1120 varNamesInit.push_back(_encode_(header[c], slice));
1121 if (atemVarsCols.contains(c)) {
1123 varNamesAtemp.push_back(header[c]);
1124 }
1125 }
1126 for (Size slice = 1; slice < k - 1; ++slice)
1127 for (std::size_t c = 0; c < header.size(); ++c)
1128 if (!atemVarsCols.contains(c)) {
1129 insertTrans(initTable, header[c], (int)slice, icol++);
1130 varNamesInit.push_back(_encode_(header[c], (int)slice));
1131 }
1132 for (Size slice = 1; slice < k; ++slice)
1133 for (std::size_t c = 0; c < header.size(); ++c)
1134 if (!atemVarsCols.contains(c)) {
1136 varNamesTran.push_back(_encode_(header[c], (int)slice));
1137 }
1138 }
1139
1140 transitionTable.setVariableNames(varNamesTran, false);
1141 initTable.setVariableNames(varNamesInit, false);
1142 atemporalTable.setVariableNames(varNamesAtemp, false);
1143 } else {
1144 // later files: validate the header against trajectory 1's without copying it
1145 const auto& raw = parser.current();
1146 bool same = (raw.size() == header.size());
1147 for (std::size_t c = 0; same && c < header.size(); ++c)
1148 same = (raw[c] == header[c]);
1149 if (!same)
1150 GUM_ERROR(InvalidArgument, "Header of " << file.string() << " differs from trajectory 1");
1151 }
1152
1153 buffer.clear();
1154 while (parser.next()) {
1155 const auto& tokens = parser.current();
1156 if (tokens.size() != header.size())
1158 "Trajectory " << (i + 1) << ", row " << parser.nbLine() << ": expected "
1159 << header.size() << " columns, got " << tokens.size());
1160 buffer.push_back({tokens.begin(), tokens.end()});
1161 }
1162
1163 if (buffer.size() < k)
1165 "Trajectory " << (i + 1) << " has " << buffer.size()
1166 << " time steps but at least k=" << k << " are required");
1167
1168 // record this trajectory's raw length (number of time steps), exposed by nbRows()
1169 _nbTimeSlices_.push_back(buffer.size());
1170
1171 // An atemporal column is constant down the trajectory, so any row carries
1172 // its value -- but row 0's may be the missing one, and reading row 0 blindly
1173 // would then drop every row this trajectory feeds. Resolve each once, from
1174 // its first non-missing occurrence. Same rule the score walk applies.
1176 for (const Size col: atemVarsCols)
1177 for (const auto& r: buffer)
1178 if (!missingSet.contains(r[col])) {
1179 atempValue[col] = r[col];
1180 break;
1181 }
1182
1183 // value of column col at time tt: the resolved constant for an atemporal
1184 // column, the row's own cell for a temporal one. A column missing all the
1185 // way down has no resolved value, so row 0's marker stands and the row is
1186 // dropped like any other incomplete one.
1187 const auto cellAt = [&](std::size_t tt, Size col) -> const std::string& {
1188 if (!atemVarsCols.contains(col)) return buffer[tt][col];
1189 const auto it = atempValue.find(col);
1190 return (it == atempValue.end()) ? buffer[0][col] : it->second;
1191 };
1192
1193 // transition table: sliding windows of width k
1194 for (std::size_t t = 0; t + k <= buffer.size(); ++t) {
1195 row.clear();
1196 // slice 0 carries the atemporal columns; later slices skip them so atemporal
1197 // variables aren't repeated k-1 times in each row
1198 for (Size col = 0; col < buffer[0].size(); ++col) {
1199 row.push_back(cellAt(t, col));
1200 }
1201 for (Size slice = 1; slice < k; ++slice) {
1202 for (Size col = 0; col < buffer[0].size(); ++col) {
1203 if (!atemVarsCols.contains(col)) { row.push_back(buffer[t + slice][col]); }
1204 }
1205 }
1207 }
1208
1209 // initial table: first k-1 slices, one row per trajectory
1210 row.clear();
1211 for (Size col = 0; col < buffer[0].size(); ++col) {
1212 row.push_back(cellAt(0, col));
1213 }
1214 for (Size slice = 1; slice < k - 1; ++slice) {
1215 for (Size col = 0; col < buffer[0].size(); ++col) {
1216 if (!atemVarsCols.contains(col)) { row.push_back(buffer[slice][col]); }
1217 }
1218 }
1220
1221 // atemporal table: one row per trajectory, atemporal columns only. Their
1222 // value is constant across the trajectory, so any time step works — take 0.
1223 // Skipped when fewer than 2 atemporal variables exist: a single atemporal
1224 // variable has no possible atemporal->atemporal arcs, so no learner is built.
1225 if (nbAtempVars > 1) {
1226 row.clear();
1227 for (Size col = 0; col < buffer[0].size(); ++col)
1228 if (atemVarsCols.contains(col)) row.push_back(cellAt(0, col));
1230 }
1231 }
1232
1233 // Dropping incomplete rows can empty a table outright -- every transition
1234 // window straddling a gap, or every trajectory's initial block incomplete.
1235 // The internal learner would then fail obscurely on an empty database, so
1236 // say what actually happened.
1239 "every row was dropped as incomplete ("
1241 << " in total): no fully observed transition window (or initial block) is left "
1242 "to learn from. The trajectories are too sparsely observed for k="
1243 << k << ".")
1244
1245 // Variables are already typed upstream (template / first-trajectory learner),
1246 // so no induceTypes pass is needed here — just canonicalize the value codes.
1249
1252
1253 if (nbAtempVars > 1) {
1254 atemporalTable.reorder();
1256 }
1257
1258
1259 // Impose k-TBN temporal constraints on structure learning:
1260 // - transition learner: past slices (0..k-2) are forced roots so only the
1261 // present slice (k-1) receives new arcs.
1262 // - atemporal variables are forced roots in transition/initial learners so
1263 // their mutual structure is learned exclusively by the atemporal learner.
1264 // No-parent in both learners covers every algorithm and also bans
1265 // temporal→atemporal arcs.
1266 // - initial learner: backward temporal arcs among past slices forbidden
1267 // explicitly (honoured by MIIC and score-based algorithms alike).
1268 // These constraints shape structure search in learnKTBN(); they are inert
1269 // for learnParameters.
1270 const int ki = (int)k;
1271 const auto& temporalVars = _prior_ktbn_.temporalVarNames();
1272 const auto& atemporalVars = _prior_ktbn_.atemporalVarNames();
1273
1274 for (const auto& base: temporalVars)
1275 for (int slice = 0; slice < ki - 1; ++slice)
1277
1278 for (const auto& atemBase: atemporalVars) {
1279 _transitionLearner_->addNoParentNode(atemBase);
1280 _initialLearner_->addNoParentNode(atemBase);
1281 }
1282
1283 if (k > 2) {
1284 // backward-in-time arcs among the past slices 0..k-2 are forbidden
1285 // explicitly: this is the only form MIIC honours (it ignores slice order),
1286 // and score-based algorithms respect it too, so it fully covers the
1287 // constraint. A setSliceOrder() mirror was dropped here as redundant.
1288 for (const auto& tailBase: temporalVars)
1289 for (const auto& headBase: temporalVars)
1290 for (int tailSlice = 1; tailSlice < ki - 1; ++tailSlice)
1291 for (int headSlice = 0; headSlice < tailSlice; ++headSlice)
1292 _initialLearner_->addForbiddenArc(_encode_(tailBase, tailSlice),
1294 }
1295 }
std::unique_ptr< BNLearner< GUM_SCALAR > > _initialLearner_
learns the initial slices 0..k-2
std::vector< Size > nbRows() const
Number of time steps in each trajectory CSV (one entry per sample, in load order)....
std::unique_ptr< BNLearner< GUM_SCALAR > > _transitionLearner_
learns the transition kernel (arcs arriving at slice k-1)
Size _nbDroppedRows_
number of time steps (rows) in each trajectory CSV, in load order. rows build() dropped because they ...
std::vector< Size > _nbTimeSlices_
Captured once by build() and exposed by nbRows(). This is the raw trajectory length,...
KTBNLearner< GUM_SCALAR > & addNoParentNode(std::string_view base, int slice) override
Declare a single (base, slice) node as a root (no parents).
std::unique_ptr< BNLearner< GUM_SCALAR > > _atemporalLearner_
learns the atemporal variables (arcs atemporal -> atemporal)

References _atemporalLearner_, gum::learning::IKTBNLearner< GUM_SCALAR >::_encode_(), _ignoreMissingSymbols_, _initialLearner_, _nbDroppedRows_, _nbTimeSlices_, _prior_ktbn_, _transitionLearner_, gum::KTBN< GUM_SCALAR >::ATEMPORAL, gum::learning::CSVParser::current(), GUM_ERROR, gum::learning::DatabaseTable::insertRow(), gum::learning::DatabaseTable::insertTranslator(), k(), gum::learning::CSVParser::nbLine(), gum::learning::IDatabaseTable< T_DATA >::nbRows(), nbSamples(), gum::learning::CSVParser::next(), gum::learning::DatabaseTable::reorder(), and gum::learning::DatabaseTable::setVariableNames().

Referenced by KTBNLearner(), and KTBNLearner().

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

◆ _buildPriorFromBN_()

template<GUM_Numeric GUM_SCALAR>
KTBN< GUM_SCALAR > gum::learning::KTBNLearner< GUM_SCALAR >::_buildPriorFromBN_ ( Size k,
const BayesNet< GUM_SCALAR > & bn,
const std::unordered_set< std::string > & atemporalVars )
staticprivate

called in the member-initialiser list of the BN constructor: builds and returns a KTBN whose variables are copied from bn (one node per base variable, right types/domains) and classified as temporal/atemporal according to atemporalVars. Must be static because it is called before the object exists.

Definition at line 974 of file KTBNLearner_tpl.h.

977 {
979
981 // bn.nodes() iterates in hash order (unspecified). This is harmless because
982 // KTBN looks up variables by name, not by insertion index.
983 for (const NodeId node: bn.nodes()) {
984 const DiscreteVariable& var = bn.variable(node);
985 prior.add(var, !atemporalVars.contains(var.name()));
986 }
987 for (const std::string& aname: atemporalVars)
988 if (!prior.exists(aname))
989 GUM_ERROR(InvalidArgument, "atemporal variable '" << aname << "' not found in the BN")
991 }
static void _checkMinimalOrder_(Size order, std::string_view label)
Throw InvalidArgument unless order is at least 2, label naming the offending parameter ("k" for the f...

References gum::learning::IKTBNLearner< GUM_SCALAR >::_checkMinimalOrder_(), GUM_ERROR, k(), and gum::Variable::name().

Referenced by KTBNLearner().

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

◆ _buildPriorFromCSV_()

template<GUM_Numeric GUM_SCALAR>
KTBN< GUM_SCALAR > gum::learning::KTBNLearner< GUM_SCALAR >::_buildPriorFromCSV_ ( std::string_view dirPath,
std::string_view csvBaseName,
Size k,
const std::unordered_set< std::string > & atemporalVars,
const std::vector< std::string > & missingSymbols,
bool induceTypes )
staticprivate

called in the member-initialiser list of the k-CSV constructor: opens the first trajectory CSV, uses a temporary BNLearner to type the base variables (induceTypes promotes numeric columns to range/integer), builds and returns a KTBN with the right temporal/atemporal classification. Must be static because it is called before the object exists.

Definition at line 933 of file KTBNLearner_tpl.h.

939 {
941
942 namespace fs = std::filesystem;
944 = (fs::path{dirPath} / (std::string{csvBaseName} + "1.csv")).string();
945 // _build_() also reads trajectory 1 (its i=0 pass). The double-read is
946 // unavoidable: this function runs in the member-initialiser list, before
947 // the object (and hence _build_()) exists, and _build_() must read every
948 // trajectory.
949
950 // BNLearner construction runs induceTypes on the CSV and populates all
951 // column translators with properly-typed DiscreteVariables.
953
954 // Pull typed variables from the translator set. translatorSafe(i) bounds-checks,
955 // guarding against any names()/translator-set mismatch.
956 const DBTranslatorSet& translators = tmpLearner.database().translatorSet();
958
960 for (std::size_t i = 0; i < names.size(); ++i) {
961 prior.add(static_cast< const DiscreteVariable& >(*translators.translatorSafe(i).variable()),
962 !atemporalVars.contains(names[i]));
963 }
964
965 // Every declared atemporal name must appear in the CSV header.
966 for (const std::string& aname: atemporalVars)
967 if (!prior.exists(aname))
969 "atemporal variable '" << aname << "' not found in the CSV header")
971 }
std::vector< std::string > names() const
Base names (no slice suffix), one entry per base variable (temporal or atemporal),...

References gum::learning::IKTBNLearner< GUM_SCALAR >::_checkMinimalOrder_(), GUM_ERROR, k(), names(), gum::learning::DBTranslatorSet::translatorSafe(), and gum::learning::DBTranslator::variable().

Referenced by KTBNLearner().

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

◆ _checkArcTemporallyFeasible_()

template<GUM_Numeric GUM_SCALAR>
void gum::learning::IKTBNLearner< GUM_SCALAR >::_checkArcTemporallyFeasible_ ( std::string_view tail,
std::string_view head,
std::string_view action ) const
protectedinherited

Reject an arc the k-TBN definition can never contain, so eraseForbiddenArc and addMandatoryArc both fail early (before any internal learner is touched) with a uniform message. action is the verb phrase completing "cannot <action> <tail> -> <head>: ..." (e.g. "force the mandatory arc", "un-forbid the arc"). Two arcs are refused: a temporal -> atemporal arc (a time-varying variable can never parent a static one) and a backward-in-time arc (head strictly before tail).

Definition at line 95 of file IKTBNLearner_tpl.h.

97 {
102 "cannot " << action << " " << tail << " -> " << head
103 << ": a temporal variable can never be a parent of an atemporal one; "
104 "this constraint is part of the k-TBN definition")
107 "cannot " << action << " " << tail << " -> " << head
108 << ": its head is at an earlier time slice than its tail, which "
109 "violates temporal causality")
110 }
Pure-virtual configuration interface shared by all k-TBN learners.
std::pair< std::string, int > _determineNode_(const std::string &name) const
engine name -> (base, slice); atemporal names map to KTBN::ATEMPORAL. Shared by every learner; only t...

References _determineNode_(), gum::KTBN< GUM_SCALAR >::ATEMPORAL, and GUM_ERROR.

Referenced by _isKnownBase_(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::addMandatoryArc(), gum::learning::KTBNLearner< GUM_SCALAR >::addMandatoryArc(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::eraseForbiddenArc(), and gum::learning::KTBNLearner< GUM_SCALAR >::eraseForbiddenArc().

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

◆ _checkBaseIsTemporal_()

template<GUM_Numeric GUM_SCALAR>
void gum::learning::IKTBNLearner< GUM_SCALAR >::_checkBaseIsTemporal_ ( std::string_view base,
std::string_view context ) const
protectedinherited

Throw InvalidArgument unless base is a known temporal base. context completes "cannot appear in <context>" (e.g. "an intra-slice constraint", "a kernel-relative arc"): every caller rejects an atemporal base for the same underlying reason – it has no per-slice instance – so they share the sentence and vary the setting.

Definition at line 196 of file IKTBNLearner_tpl.h.

197 {
198 const std::string b{base};
199 if (!_isKnownBase_(b))
201 "unknown base variable '" << base
202 << "': it is not one of this learner's variables")
205 "atemporal variable '" << base << "' cannot appear in " << context
206 << ": it has no per-slice instance")
207 }
virtual bool _isKnownBase_(std::string_view base) const =0
Whether base is one of this learner's variables, temporal or atemporal. The second subclass-specific ...
virtual const std::unordered_set< std::string > & _atemporalVarNames_() const =0
The base names of the atemporal (static) variables. The only subclass-specific input to determineNode...

References _atemporalVarNames_(), _isKnownBase_(), gum::contains(), and GUM_ERROR.

Referenced by _isKnownBase_(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_verifyKernelArc_(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::addForbiddenIntraSliceArc(), gum::learning::KTBNLearner< GUM_SCALAR >::addForbiddenIntraSliceArc(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::eraseForbiddenIntraSliceArc(), and gum::learning::KTBNLearner< GUM_SCALAR >::eraseForbiddenIntraSliceArc().

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

◆ _checkMinimalOrder_()

template<GUM_Numeric GUM_SCALAR>
void gum::learning::IKTBNLearner< GUM_SCALAR >::_checkMinimalOrder_ ( Size order,
std::string_view label )
staticprotectedinherited

Throw InvalidArgument unless order is at least 2, label naming the offending parameter ("k" for the fixed-k learner, "kMax" for the adaptive one).

This is a learner constraint, not a model one – gum::KTBN, gum::KTBNGenerator and gum::KTBNInference all accept k = 1 – which is why it lives here rather than on the model. Static because four of its call sites are themselves static helpers invoked from member-initialiser lists, before any object exists.

Definition at line 210 of file IKTBNLearner_tpl.h.

210 {
211 if (order < 2)
213 "a k-TBN learner requires "
214 << label << " >= 2: k=1 is a static Bayesian network, use BNLearner instead")
215 }

References GUM_ERROR.

Referenced by gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::KTBNAdaptiveLearner(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::KTBNAdaptiveLearner(), gum::learning::KTBNLearner< GUM_SCALAR >::_buildPriorFromBN_(), gum::learning::KTBNLearner< GUM_SCALAR >::_buildPriorFromCSV_(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_inferAtemporalVars_(), gum::learning::KTBNLearner< GUM_SCALAR >::_inferAtemporalVars_(), and _isKnownBase_().

Here is the caller graph for this function:

◆ _determineNode_()

template<GUM_Numeric GUM_SCALAR>
std::pair< std::string, int > gum::learning::IKTBNLearner< GUM_SCALAR >::_determineNode_ ( const std::string & name) const
protectedinherited

engine name -> (base, slice); atemporal names map to KTBN::ATEMPORAL. Shared by every learner; only the atemporal test varies (via atemporalVarNames()).

Definition at line 64 of file IKTBNLearner_tpl.h.

64 {
65 // check atemporal set first: a name registered as atemporal always maps
66 // to ATEMPORAL, even if it syntactically looks like "base[t]"
68
69 // syntactic parse: look for a trailing "[digits]" suffix
70 const std::size_t bracketPos = name.rfind('[');
72
74 name.size() - bracketPos - 1};
75 if (bracketContent.empty() || bracketContent.back() != ']')
77
78 const std::string_view digits = bracketContent.substr(0, bracketContent.size() - 1);
79 if (digits.empty()) return {name, KTBN< GUM_SCALAR >::ATEMPORAL};
80 for (const char c: digits)
81 if (std::isdigit(static_cast< unsigned char >(c)) == 0)
83
84 int slice{};
85 try {
87 } catch (const std::out_of_range&) {
89 "Node name '" << name << "' has a slice index too large to represent as int.")
90 }
91 return {name.substr(0, bracketPos), slice};
92 }

References _atemporalVarNames_(), gum::KTBN< GUM_SCALAR >::ATEMPORAL, gum::contains(), and GUM_ERROR.

Referenced by gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_applyConstraints_(), _checkArcTemporallyFeasible_(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_forEachScoredNode_(), gum::learning::KTBNLearner< GUM_SCALAR >::_forOwningLearner_(), _isKnownBase_(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_recomputeKMin_(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::addForbiddenArc(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::addMandatoryArc(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::addNoChildrenNode(), gum::learning::KTBNLearner< GUM_SCALAR >::addNoChildrenNode(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::addNoParentNode(), gum::learning::KTBNLearner< GUM_SCALAR >::addNoParentNode(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::addPossibleEdge(), gum::learning::KTBNLearner< GUM_SCALAR >::addPossibleEdge(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::eraseForbiddenArc(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::eraseMandatoryArc(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::eraseNoChildrenNode(), gum::learning::KTBNLearner< GUM_SCALAR >::eraseNoChildrenNode(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::eraseNoParentNode(), gum::learning::KTBNLearner< GUM_SCALAR >::eraseNoParentNode(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::erasePossibleEdge(), gum::learning::KTBNLearner< GUM_SCALAR >::erasePossibleEdge(), and gum::learning::KTBNLearner< GUM_SCALAR >::names().

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

◆ _encode_()

template<GUM_Numeric GUM_SCALAR>
std::string gum::learning::IKTBNLearner< GUM_SCALAR >::_encode_ ( std::string_view base,
int slice ) const
protectedinherited

(base, slice) -> engine name ("A[1]" / atemporal engine name). Pure function, shared by every learner.

Definition at line 57 of file IKTBNLearner_tpl.h.

57 {
59 return std::string{base} + '[' + std::to_string(slice) + ']';
60 }

References gum::KTBN< GUM_SCALAR >::ATEMPORAL.

Referenced by gum::learning::KTBNLearner< GUM_SCALAR >::_assemble_(), gum::learning::KTBNLearner< GUM_SCALAR >::_build_(), _isKnownBase_(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::addForbiddenArc(), gum::learning::KTBNLearner< GUM_SCALAR >::addForbiddenArc(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::addMandatoryArc(), gum::learning::KTBNLearner< GUM_SCALAR >::addMandatoryArc(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::addNoChildrenNode(), gum::learning::KTBNLearner< GUM_SCALAR >::addNoChildrenNode(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::addNoParentNode(), gum::learning::KTBNLearner< GUM_SCALAR >::addNoParentNode(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::addPossibleEdge(), gum::learning::KTBNLearner< GUM_SCALAR >::addPossibleEdge(), gum::learning::KTBNLearner< GUM_SCALAR >::domainSize(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::eraseForbiddenArc(), gum::learning::KTBNLearner< GUM_SCALAR >::eraseForbiddenArc(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::eraseMandatoryArc(), gum::learning::KTBNLearner< GUM_SCALAR >::eraseMandatoryArc(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::eraseNoChildrenNode(), gum::learning::KTBNLearner< GUM_SCALAR >::eraseNoChildrenNode(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::eraseNoParentNode(), gum::learning::KTBNLearner< GUM_SCALAR >::eraseNoParentNode(), gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::erasePossibleEdge(), gum::learning::KTBNLearner< GUM_SCALAR >::erasePossibleEdge(), and gum::learning::KTBNLearner< GUM_SCALAR >::learnParameters().

Here is the caller graph for this function:

◆ _forEachAllSlicesPair_()

template<GUM_Numeric GUM_SCALAR>
template<class F>
void gum::learning::KTBNLearner< GUM_SCALAR >::_forEachAllSlicesPair_ ( std::string_view tailBase,
std::string_view headBase,
F && f ) const
private

Apply f(tailSlice, headSlice) to every causally-possible slice pair of an all-slices constraint between tailBase and headBase. Four shapes: atemporal->atemporal is one pair; an atemporal tail reaches every slice of the head; a temporal tail into an atemporal head is structurally impossible and yields none; two temporal bases give every pair with tailSlice <= headSlice, i.e. every lag.

Validates that both bases exist – which the two callers used not to do, letting an unknown name reach a BNLearner as an opaque MissingVariableInDatabase – but deliberately NOT that they are temporal: an atemporal tail is one of the four shapes above.

Definition at line 95 of file KTBNLearner_tpl.h.

97 {
100 "unknown base variable '" << tailBase
101 << "': it is not one of this learner's variables")
104 "unknown base variable '" << headBase
105 << "': it is not one of this learner's variables")
106
108 const int k = static_cast< int >(_prior_ktbn_.k());
110 const bool headAtemp = _prior_ktbn_.atemporalVarNames().contains(std::string{headBase});
111
112 if (tailAtemp && headAtemp) {
113 f(AT, AT);
114 } else if (tailAtemp) {
115 for (int hs = 0; hs < k; ++hs)
116 f(AT, hs);
117 } else if (headAtemp) {
118 // temporal tail -> atemporal head is already structurally impossible
119 } else {
120 for (int ts = 0; ts < k; ++ts)
121 for (int hs = ts; hs < k; ++hs)
122 f(ts, hs);
123 }
124 }
bool _isKnownBase_(std::string_view base) const override
whether base is one of this learner's variables; read straight from the prior k-TBN,...

References _isKnownBase_(), _prior_ktbn_, gum::KTBN< GUM_SCALAR >::ATEMPORAL, GUM_ERROR, and k().

Referenced by addForbiddenArcAllSlices(), and eraseForbiddenArcAllSlices().

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

◆ _forEachLearner_()

template<GUM_Numeric GUM_SCALAR>
template<class F>
void gum::learning::KTBNLearner< GUM_SCALAR >::_forEachLearner_ ( F && f)
private

Apply f to each present internal learner (the atemporal one only when it exists). Factors out the fan-out shared by every score / algorithm / prior / correction / graph-change setter.

Definition at line 67 of file KTBNLearner_tpl.h.

References _atemporalLearner_, _initialLearner_, and _transitionLearner_.

Referenced by allowArcAdditions(), allowArcDeletions(), allowArcReversals(), setMaxIndegree(), useExtendedGreedyHillClimbing(), useGreedyHillClimbing(), useLocalSearchWithTabuList(), useMDLCorrection(), useMIIC(), useNMLCorrection(), useNoCorrection(), useScoreAIC(), useScoreBD(), useScoreBDeu(), useScoreBIC(), useScorefNML(), useScoreLog2Likelihood(), useScoreMDL(), and useSmoothingPrior().

Here is the caller graph for this function:

◆ _forOwningLearner_()

template<GUM_Numeric GUM_SCALAR>
template<class F>
void gum::learning::KTBNLearner< GUM_SCALAR >::_forOwningLearner_ ( std::string_view tail,
std::string_view head,
F && f )
private

Apply f to the ONE internal learner that can learn the arc tail -> head, chosen by its head: a slice-(k-1) head belongs to the transition learner, an atemporal->atemporal arc to the atemporal learner, any other head (a past temporal slice) to the initial learner. The three cases are exclusive because build() forces every other node root in the learners that do not own it, so no arc is ever learnable in two of them.

The bothAtemp guard matters: without it an "X[0] -> C" arc would be routed to the atemporal learner, whose table has no X[0] column, and surface as MissingVariableInDatabase. It falls to the initial learner instead, where both endpoints exist and the arc is a harmless no-op.

Callers keep their own checkArcTemporallyFeasible call, which is deliberately asymmetric – forbidding an impossible arc is harmless while UN-forbidding it lifts an invariant, and forcing one must be refused while erasing a never-forced one is harmless – so it cannot be folded in here without flattening that asymmetry.

Definition at line 75 of file KTBNLearner_tpl.h.

77 {
78 const int last = static_cast< int >(_prior_ktbn_.k()) - 1;
85 else if (tailSlice != last) f(*_initialLearner_);
86 // else: the head is at a past slice but the tail sits on the kernel one,
87 // i.e. a backward arc. No table can hold it -- the initial one has no
88 // slice k-1 column -- and the k-TBN forbids it anyway, so there is
89 // nothing to constrain. Silently nothing, as the old broadcast did
90 // through its "tailSlice != last && headSlice != last" guard.
91 }

References _atemporalLearner_, gum::learning::IKTBNLearner< GUM_SCALAR >::_determineNode_(), _initialLearner_, _prior_ktbn_, _transitionLearner_, and gum::KTBN< GUM_SCALAR >::ATEMPORAL.

Referenced by addForbiddenArc(), addMandatoryArc(), eraseForbiddenArc(), and eraseMandatoryArc().

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

◆ _inferAtemporalVars_()

template<GUM_Numeric GUM_SCALAR>
std::unordered_set< std::string > gum::learning::KTBNLearner< GUM_SCALAR >::_inferAtemporalVars_ ( std::string_view dirPath,
std::string_view csvBaseName,
Size nbSamples,
Size k,
const std::vector< std::string > & missingSymbols )
staticprivate

checks k >= 2, then delegates the actual scan to the shared IKTBNLearner::scanConstantColumns() (also used by KTBNAdaptiveLearner, hence not duplicated here). The check happens first and here, not inside the shared scan: called once, in the member-initialiser list of the atemporal-inferring constructor, before prior_ktbn exists — before that constructor's body runs the equivalent check in buildPriorFromCSV — so without it here too, a bad k would only be caught after a wasted scan of every trajectory.

Definition at line 919 of file KTBNLearner_tpl.h.

924 {
928 nbSamples,
930 }
static std::unordered_set< std::string > _scanConstantColumns_(std::string_view dirPath, std::string_view csvBaseName, Size nbSamples, const std::vector< std::string > &missingSymbols)
Scans every trajectory and returns the base names classified atemporal: those whose value never chang...

References gum::learning::IKTBNLearner< GUM_SCALAR >::_checkMinimalOrder_(), gum::learning::IKTBNLearner< GUM_SCALAR >::_scanConstantColumns_(), k(), and nbSamples().

Referenced by KTBNLearner().

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

◆ _isKnownBase_()

template<GUM_Numeric GUM_SCALAR>
bool gum::learning::KTBNLearner< GUM_SCALAR >::_isKnownBase_ ( std::string_view base) const
overrideprivatevirtual

whether base is one of this learner's variables; read straight from the prior k-TBN, like atemporalVarNames() above.

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 1303 of file KTBNLearner_tpl.h.

1303 {
1304 const std::string b{base};
1305 return _prior_ktbn_.temporalVarNames().contains(b)
1306 || _prior_ktbn_.atemporalVarNames().contains(b);
1307 }

References _prior_ktbn_.

Referenced by _forEachAllSlicesPair_().

Here is the caller graph for this function:

◆ _scanConstantColumns_()

template<GUM_Numeric GUM_SCALAR>
std::unordered_set< std::string > gum::learning::IKTBNLearner< GUM_SCALAR >::_scanConstantColumns_ ( std::string_view dirPath,
std::string_view csvBaseName,
Size nbSamples,
const std::vector< std::string > & missingSymbols )
staticprotectedinherited

Scans every trajectory and returns the base names classified atemporal: those whose value never changes across the rows of a single trajectory (checked independently per trajectory, so the value may still differ between trajectories). Shared by every atemporal-inferring CSV constructor (KTBNLearner, KTBNAdaptiveLearner): identical scanning logic regardless of which subclass calls it, so duplicating it per subclass would only invite the two copies to drift. Callers are responsible for validating their own arguments (e.g. k/kMax >= 2) before calling this — it does no such check itself, only opens files, so a bad argument would otherwise only be caught after a wasted scan.

Column indices still "live" (not yet proven non-constant) are tracked as a shrinking list rather than rescanned from scratch, and scanning stops opening further trajectories once that list is empty — falsification is one-way, so nothing left to test can ever become atemporal again.

Definition at line 113 of file IKTBNLearner_tpl.h.

117 {
118 namespace fs = std::filesystem;
119 const fs::path dir{dirPath};
122
123 std::vector< std::string > header; // captured from trajectory 1
124 std::vector< std::size_t > candidates; // column indices not yet falsified; shrinks only
125 std::vector< std::string > firstSeen; // per column: first non-missing value THIS trajectory
127 haveFirstSeen; // per column: whether firstSeen[c] is set THIS trajectory
128
129 for (Size i = 0; i < nbSamples; ++i) {
130 // once every column has been falsified, nothing left to test can
131 // ever become atemporal again, so remaining trajectories are never
132 // even opened. Not part of the for-condition: `candidates` does not
133 // exist yet before trajectory 1 populates it.
134 if (i > 0 && candidates.empty()) break;
135
136 const fs::path file = dir / (stem + std::to_string(i + 1) + ".csv");
138 if (!is.is_open()) GUM_ERROR(IOError, "Cannot open " << file.string());
139
140 CSVParser parser(is, file.string());
141 parser.next();
142 const auto& raw = parser.current();
143
144 if (i == 0) {
145 header.assign(raw.begin(), raw.end());
146 candidates.resize(header.size());
147 std::iota(candidates.begin(), candidates.end(), std::size_t{0});
148 } else {
149 bool same = (raw.size() == header.size());
150 for (std::size_t c = 0; same && c < header.size(); ++c)
151 same = (raw[c] == header[c]);
152 if (!same)
154 "Header of " << file.string() << " differs from trajectory 1");
155 }
156
157 haveFirstSeen.assign(header.size(), false);
158 firstSeen.assign(header.size(), {});
159
160 while (parser.next()) {
161 const auto& tokens = parser.current();
162 if (tokens.size() != header.size())
164 "Trajectory " << (i + 1) << ", row " << parser.nbLine() << ": expected "
165 << header.size() << " columns, got " << tokens.size());
166 // iterate only the still-live candidates, swap-erasing any just
167 // falsified so later rows (and later trajectories) never revisit it
168 for (std::size_t idx = 0; idx < candidates.size();) {
169 const std::size_t c = candidates[idx];
170 if (missing.contains(tokens[c])) {
171 ++idx; // uninformative row for this column, still a candidate
172 continue;
173 }
174 if (!haveFirstSeen[c]) {
175 firstSeen[c] = tokens[c];
176 haveFirstSeen[c] = true;
177 ++idx;
178 } else if (tokens[c] != firstSeen[c]) {
179 candidates[idx] = candidates.back(); // falsified: drop, O(1)
180 candidates.pop_back();
181 } else {
182 ++idx;
183 }
184 }
185 if (candidates.empty()) break; // nothing left to test in this file either
186 }
187 }
188
190 for (const std::size_t c: candidates)
191 atemporalVars.insert(header[c]);
192 return atemporalVars;
193 }

References gum::learning::CSVParser::current(), GUM_ERROR, gum::learning::CSVParser::nbLine(), and gum::learning::CSVParser::next().

Referenced by gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_inferAtemporalVars_(), gum::learning::KTBNLearner< GUM_SCALAR >::_inferAtemporalVars_(), and _isKnownBase_().

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

◆ addForbiddenArc() [1/2]

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::addForbiddenArc ( std::string_view tailBase,
int tailSlice,
std::string_view headBase,
int headSlice )
overridevirtual

Forbid one (base, slice) -> (base, slice) arc (KTBN::ATEMPORAL for static). A backward arc (tailSlice > headSlice) is accepted but has no effect: such an arc is already impossible, so forbidding it is a harmless no-op.

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 465 of file KTBNLearner_tpl.h.

468 {
469 // wrapper: encode (base, slice) -> engine name and delegate to the string overload
471 }
KTBNLearner< GUM_SCALAR > & addForbiddenArc(std::string_view tailNode, std::string_view headNode) override
Forbid tailNode from ever parenting headNode (engine names, e.g. "X[1]", "C").

References KTBNLearner(), gum::learning::IKTBNLearner< GUM_SCALAR >::_encode_(), and addForbiddenArc().

Here is the call graph for this function:

◆ addForbiddenArc() [2/2]

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::addForbiddenArc ( std::string_view tailNode,
std::string_view headNode )
overridevirtual

Forbid tailNode from ever parenting headNode (engine names, e.g. "X[1]", "C").

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 456 of file KTBNLearner_tpl.h.

457 {
460 });
461 return *this;
462 }
void _forOwningLearner_(std::string_view tail, std::string_view head, F &&f)
Apply f to the ONE internal learner that can learn the arc tail -> head, chosen by its head: a slice-...

References KTBNLearner(), and _forOwningLearner_().

Referenced by gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_applyConstraints_(), addForbiddenArc(), addForbiddenArcAllSlices(), and addForbiddenIntraSliceArc().

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

◆ addForbiddenArcAllSlices()

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::addForbiddenArcAllSlices ( std::string_view tailBase,
std::string_view headBase )
overridevirtual

Forbid tailBase -> headBase at every causally-possible slice pair (every lag, not just matching slices): tailBase can never be an ancestor of headBase in the learned k-TBN. A temporal->atemporal pair is a no-op (already structurally impossible).

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 567 of file KTBNLearner_tpl.h.

568 {
571 });
572 return *this;
573 }
void _forEachAllSlicesPair_(std::string_view tailBase, std::string_view headBase, F &&f) const
Apply f(tailSlice, headSlice) to every causally-possible slice pair of an all-slices constraint betwe...

References _forEachAllSlicesPair_(), and addForbiddenArc().

Referenced by gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_applyConstraints_().

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

◆ addForbiddenIntraSliceArc()

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::addForbiddenIntraSliceArc ( std::string_view tailBase,
std::string_view headBase )
overridevirtual

Forbid tailBase -> headBase at every intra-slice position (i.e. tailBase[t] -> headBase[t] for all t in [0, k-1]).

Exceptions
InvalidArgumentif either endpoint is unknown or atemporal (an atemporal variable has no intra-slice position).

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 541 of file KTBNLearner_tpl.h.

542 {
543 // validate both endpoints BEFORE touching any learner, so a rejected call
544 // leaves no partially-applied constraint behind
545 _checkBaseIsTemporal_(tailBase, "an intra-slice constraint");
546 _checkBaseIsTemporal_(headBase, "an intra-slice constraint");
547 const int k = static_cast< int >(_prior_ktbn_.k());
548 for (int t = 0; t < k; ++t)
550 return *this;
551 }
void _checkBaseIsTemporal_(std::string_view base, std::string_view context) const
Throw InvalidArgument unless base is a known temporal base. context completes "cannot appear in <cont...

References gum::learning::IKTBNLearner< GUM_SCALAR >::_checkBaseIsTemporal_(), _prior_ktbn_, addForbiddenArc(), and k().

Referenced by gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_applyConstraints_().

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

◆ addMandatoryArc() [1/2]

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::addMandatoryArc ( std::string_view tailBase,
int tailSlice,
std::string_view headBase,
int headSlice )
overridevirtual

Force one arc, lag stated explicitly via slices (KTBN::ATEMPORAL for static).

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 511 of file KTBNLearner_tpl.h.

514 {
515 // wrapper: encode (base, slice) -> engine name and delegate to the string overload
517 }
KTBNLearner< GUM_SCALAR > & addMandatoryArc(std::string_view tailNode, std::string_view headNode) override
Force tailNode to be a parent of headNode (engine names, e.g. "X[1]", "C").

References KTBNLearner(), gum::learning::IKTBNLearner< GUM_SCALAR >::_encode_(), and addMandatoryArc().

Here is the call graph for this function:

◆ addMandatoryArc() [2/2]

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::addMandatoryArc ( std::string_view tailNode,
std::string_view headNode )
overridevirtual

Force tailNode to be a parent of headNode (engine names, e.g. "X[1]", "C").

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 495 of file KTBNLearner_tpl.h.

496 {
497 // Unlike forbidding, a mandatory arc must be feasible: reject up front the arcs
498 // the k-TBN can never contain (shared with eraseForbiddenArc) rather than
499 // letting them crash later in the wrong learner. Kept here, not in
500 // _forOwningLearner_: the check is deliberately asymmetric across the four
501 // setters (see its declaration).
502 _checkArcTemporallyFeasible_(tailNode, headNode, "force the mandatory arc");
503
506 });
507 return *this;
508 }
void _checkArcTemporallyFeasible_(std::string_view tail, std::string_view head, std::string_view action) const
Reject an arc the k-TBN definition can never contain, so eraseForbiddenArc and addMandatoryArc both f...

References KTBNLearner(), gum::learning::IKTBNLearner< GUM_SCALAR >::_checkArcTemporallyFeasible_(), and _forOwningLearner_().

Referenced by gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_applyConstraints_(), and addMandatoryArc().

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

◆ addNoChildrenNode() [1/2]

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::addNoChildrenNode ( std::string_view base,
int slice )
overridevirtual

Declare a single (base, slice) node as a leaf (no children).

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 627 of file KTBNLearner_tpl.h.

628 {
630 }
KTBNLearner< GUM_SCALAR > & addNoChildrenNode(std::string_view base, int slice) override
Declare a single (base, slice) node as a leaf (no children).

References KTBNLearner(), gum::learning::IKTBNLearner< GUM_SCALAR >::_encode_(), and addNoChildrenNode().

Referenced by gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_applyConstraints_(), and addNoChildrenNode().

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

◆ addNoChildrenNode() [2/2]

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::addNoChildrenNode ( std::string_view name)
overridevirtual

Declare a single node (bracket notation, e.g. "X[2]" or "C") as a leaf.

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 633 of file KTBNLearner_tpl.h.

633 {
637 if (slice < (int)_prior_ktbn_.k() - 1) _initialLearner_->addNoChildrenNode(name);
638 if (isAtemp && _atemporalLearner_) _atemporalLearner_->addNoChildrenNode(name);
639 return *this;
640 }

References KTBNLearner(), _atemporalLearner_, gum::learning::IKTBNLearner< GUM_SCALAR >::_determineNode_(), _initialLearner_, _prior_ktbn_, _transitionLearner_, and gum::KTBN< GUM_SCALAR >::ATEMPORAL.

Here is the call graph for this function:

◆ addNoParentNode() [1/2]

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::addNoParentNode ( std::string_view base,
int slice )
overridevirtual

Declare a single (base, slice) node as a root (no parents).

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 586 of file KTBNLearner_tpl.h.

587 {
589 }

References KTBNLearner(), gum::learning::IKTBNLearner< GUM_SCALAR >::_encode_(), and addNoParentNode().

Referenced by gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_applyConstraints_(), and addNoParentNode().

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

◆ addNoParentNode() [2/2]

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::addNoParentNode ( std::string_view name)
overridevirtual

Declare a single node (bracket notation, e.g. "X[2]" or "C") as a root.

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 592 of file KTBNLearner_tpl.h.

592 {
596 // Atemporals are already forced roots in transition/initial learners by _build_.
597 // The user constraint is only meaningful in the atemporal learner.
598 _atemporalLearner_->addNoParentNode(name);
599 } else if (!isAtemp) {
600 _transitionLearner_->addNoParentNode(name);
601 if (slice < (int)_prior_ktbn_.k() - 1) _initialLearner_->addNoParentNode(name);
602 }
603 return *this;
604 }

References KTBNLearner(), _atemporalLearner_, gum::learning::IKTBNLearner< GUM_SCALAR >::_determineNode_(), _initialLearner_, _prior_ktbn_, _transitionLearner_, and gum::KTBN< GUM_SCALAR >::ATEMPORAL.

Here is the call graph for this function:

◆ addPossibleEdge() [1/2]

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::addPossibleEdge ( std::string_view tail,
std::string_view head )
overridevirtual

Add a candidate edge for MIIC using engine names (e.g. "X[1]", "C").

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 677 of file KTBNLearner_tpl.h.

678 {
679 const int last = (int)_prior_ktbn_.k() - 1;
682
683 // Atemporal names exist as forced roots in every table, so whitelisting them
684 // here too keeps the whole-model promise ("only listed edges are explored")
685 // even when the edge can fire in just one learner: the no-parent constraint
686 // on atemporal heads still vetoes it there (constraints compound, never
687 // override), so this can only shrink the candidate set, never widen it.
689 // forward to the initial learner only when both endpoints exist there, i.e. both
690 // are at a past slice (< k-1); atemporals (slice -1) satisfy this automatically.
691 if (tailSlice < last && headSlice < last) _initialLearner_->addPossibleEdge(tail, head);
692
695 if (_atemporalLearner_) _atemporalLearner_->addPossibleEdge(tail, head);
696 } else {
698 }
699 return *this;
700 }
Size _nbTemporalPossibleEdges_
counts of currently-active possible edges, split by kind: edges with at least one temporal endpoint,...
KTBNLearner< GUM_SCALAR > & addPossibleEdge(std::string_view tailBase, int tailSlice, std::string_view headBase, int headSlice) override
Add a candidate edge for MIIC (only edges explicitly listed are explored).

References KTBNLearner(), _atemporalLearner_, gum::learning::IKTBNLearner< GUM_SCALAR >::_determineNode_(), _initialLearner_, _nbAtemporalPossibleEdges_, _nbTemporalPossibleEdges_, _prior_ktbn_, _transitionLearner_, and gum::KTBN< GUM_SCALAR >::ATEMPORAL.

Here is the call graph for this function:

◆ addPossibleEdge() [2/2]

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::addPossibleEdge ( std::string_view tailBase,
int tailSlice,
std::string_view headBase,
int headSlice )
overridevirtual

Add a candidate edge for MIIC (only edges explicitly listed are explored).

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 659 of file KTBNLearner_tpl.h.

662 {
663 // encode (base, slice) -> engine name and delegate to the string overload, which
664 // owns the full routing (atemporal->atemporal to the atemporal learner, slice-k-1 guard)
666 }

References KTBNLearner(), gum::learning::IKTBNLearner< GUM_SCALAR >::_encode_(), and addPossibleEdge().

Referenced by gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_applyConstraints_(), and addPossibleEdge().

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

◆ allowArcAdditions()

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::allowArcAdditions ( bool allow = true)
overridevirtual

Allow or forbid arc additions during structure search.

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 722 of file KTBNLearner_tpl.h.

722 {
724 return *this;
725 }
void _forEachLearner_(F &&f)
Apply f to each present internal learner (the atemporal one only when it exists). Factors out the fan...
KTBNLearner< GUM_SCALAR > & allowArcAdditions(bool allow=true) override
Allow or forbid arc additions during structure search.

References KTBNLearner(), and _forEachLearner_().

Referenced by gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_applyConstraints_().

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

◆ allowArcDeletions()

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::allowArcDeletions ( bool allow = true)
overridevirtual

Allow or forbid arc deletions during structure search.

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 728 of file KTBNLearner_tpl.h.

728 {
730 return *this;
731 }
KTBNLearner< GUM_SCALAR > & allowArcDeletions(bool allow=true) override
Allow or forbid arc deletions during structure search.

References KTBNLearner(), and _forEachLearner_().

Referenced by gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_applyConstraints_().

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

◆ allowArcReversals()

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::allowArcReversals ( bool allow = true)
overridevirtual

Allow or forbid arc reversals during structure search.

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 734 of file KTBNLearner_tpl.h.

734 {
736 return *this;
737 }
KTBNLearner< GUM_SCALAR > & allowArcReversals(bool allow=true) override
Allow or forbid arc reversals during structure search.

References KTBNLearner(), and _forEachLearner_().

Referenced by gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_applyConstraints_().

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

◆ checkScorePriorCompatibility()

template<GUM_Numeric GUM_SCALAR>
std::string gum::learning::KTBNLearner< GUM_SCALAR >::checkScorePriorCompatibility ( ) const

Returns a warning string if the current score and prior are incompatible, empty string otherwise.

Definition at line 356 of file KTBNLearner_tpl.h.

356 {
357 // Every score/prior setter is applied uniformly to all internal learners, so
358 // their configurations are identical: a single check on the transition learner
359 // is representative of the whole KTBNLearner.
360 return _transitionLearner_->checkScorePriorCompatibility();
361 }

References _transitionLearner_.

◆ copyState()

template<GUM_Numeric GUM_SCALAR>
void gum::learning::KTBNLearner< GUM_SCALAR >::copyState ( const KTBNLearner< GUM_SCALAR > & learner)

Copy all score/algorithm/prior/constraint settings from another KTBNLearner (does not copy the database).

Definition at line 834 of file KTBNLearner_tpl.h.

834 {
835 // As in BNLearner::copyState, only score/algorithm/prior/constraint settings
836 // are copied, never the database. Both sides must be structurally compatible
837 // (same k, same temporal/atemporal partition) for the name-based constraints
838 // to mean the same thing. The atemporal learner is copied only when both
839 // sides have one (absent when there are <= 1 atemporal vars).
844
845 // KTBNLearner-only state (BNLearner::copyState can't carry it): learnKTBN reads
846 // these to suppress the atemporal learner under a temporal-only whitelist
849 }

References KTBNLearner(), _atemporalLearner_, _initialLearner_, _nbAtemporalPossibleEdges_, _nbTemporalPossibleEdges_, and _transitionLearner_.

Here is the call graph for this function:

◆ domainSize()

template<GUM_Numeric GUM_SCALAR>
Size gum::learning::KTBNLearner< GUM_SCALAR >::domainSize ( std::string_view base) const

Domain size of the base variable base (e.g. "X", "C"). Engine names (e.g. "X[1]") are also accepted.

Note
KTBNLearner is deliberately name-only: it exposes no NodeId-based accessor (no domainSize(NodeId), idFromName or nameFromId). The three internal learners live in independent NodeId spaces, so a bare NodeId would be ambiguous — always address variables by name here.

Definition at line 902 of file KTBNLearner_tpl.h.

902 {
903 // Resolve the base name to an engine name in the transition table: a temporal
904 // variable is addressed at slice 0, everything else keeps its bare name (which
905 // also lets a raw engine name pass through). An unknown name reaches
906 // domainSize() unchanged and throws MissingVariableInDatabase.
907 const std::string b{base};
908 const int slice
909 = _prior_ktbn_.temporalVarNames().contains(b) ? 0 : KTBN< GUM_SCALAR >::ATEMPORAL;
911 }
Size domainSize(std::string_view base) const
Domain size of the base variable base (e.g. "X", "C"). Engine names (e.g. "X[1]") are also accepted.

References gum::learning::IKTBNLearner< GUM_SCALAR >::_encode_(), _prior_ktbn_, _transitionLearner_, and gum::KTBN< GUM_SCALAR >::ATEMPORAL.

Here is the call graph for this function:

◆ domainSizes()

template<GUM_Numeric GUM_SCALAR>
std::vector< Size > gum::learning::KTBNLearner< GUM_SCALAR >::domainSizes ( ) const

Domain sizes of the base variables, in the same column order as names().

Definition at line 894 of file KTBNLearner_tpl.h.

894 {
895 // Mirror names(): the first nbCols() columns are the base variables (a temporal
896 // variable's domain size is the same across all its slices), so keep only those.
897 const auto& all = _transitionLearner_->domainSizes();
898 return std::vector< Size >(all.begin(), all.begin() + nbCols());
899 }
Size nbCols() const
Number of columns in each CSV, i.e. of base variables (temporal + atemporal).
std::vector< std::size_t > domainSizes() const
Domain sizes of the base variables, in the same column order as names().

References _transitionLearner_, and nbCols().

Here is the call graph for this function:

◆ eraseForbiddenArc() [1/2]

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::eraseForbiddenArc ( std::string_view tailBase,
int tailSlice,
std::string_view headBase,
int headSlice )
overridevirtual

Undo a previous addForbiddenArc for a specific (base, slice) pair.

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 487 of file KTBNLearner_tpl.h.

490 {
492 }
KTBNLearner< GUM_SCALAR > & eraseForbiddenArc(std::string_view tailNode, std::string_view headNode) override
Undo a previous addForbiddenArc (engine names, e.g. "X[1]", "C").

References KTBNLearner(), gum::learning::IKTBNLearner< GUM_SCALAR >::_encode_(), and eraseForbiddenArc().

Here is the call graph for this function:

◆ eraseForbiddenArc() [2/2]

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::eraseForbiddenArc ( std::string_view tailNode,
std::string_view headNode )
overridevirtual

Undo a previous addForbiddenArc (engine names, e.g. "X[1]", "C").

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 475 of file KTBNLearner_tpl.h.

476 {
477 // refuse to lift an invariant the k-TBN definition enforces (shared with addMandatoryArc)
478 _checkArcTemporallyFeasible_(tailNode, headNode, "un-forbid the arc");
479
482 });
483 return *this;
484 }

References gum::learning::IKTBNLearner< GUM_SCALAR >::_checkArcTemporallyFeasible_(), and _forOwningLearner_().

Referenced by eraseForbiddenArc(), eraseForbiddenArcAllSlices(), and eraseForbiddenIntraSliceArc().

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

◆ eraseForbiddenArcAllSlices()

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::eraseForbiddenArcAllSlices ( std::string_view tailBase,
std::string_view headBase )
overridevirtual

Undo a previous addForbiddenArcAllSlices.

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 577 of file KTBNLearner_tpl.h.

578 {
581 });
582 return *this;
583 }

References _forEachAllSlicesPair_(), and eraseForbiddenArc().

Here is the call graph for this function:

◆ eraseForbiddenIntraSliceArc()

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::eraseForbiddenIntraSliceArc ( std::string_view tailBase,
std::string_view headBase )
overridevirtual

Undo a previous addForbiddenIntraSliceArc.

Exceptions
InvalidArgumentunder the same conditions as addForbiddenIntraSliceArc.

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 555 of file KTBNLearner_tpl.h.

556 {
557 _checkBaseIsTemporal_(tailBase, "an intra-slice constraint");
558 _checkBaseIsTemporal_(headBase, "an intra-slice constraint");
559 const int k = static_cast< int >(_prior_ktbn_.k());
560 for (int t = 0; t < k; ++t)
562 return *this;
563 }

References gum::learning::IKTBNLearner< GUM_SCALAR >::_checkBaseIsTemporal_(), _prior_ktbn_, eraseForbiddenArc(), and k().

Here is the call graph for this function:

◆ eraseMandatoryArc() [1/2]

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::eraseMandatoryArc ( std::string_view tailBase,
int tailSlice,
std::string_view headBase,
int headSlice )
overridevirtual

Undo a previous addMandatoryArc.

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 532 of file KTBNLearner_tpl.h.

535 {
537 }
KTBNLearner< GUM_SCALAR > & eraseMandatoryArc(std::string_view tailNode, std::string_view headNode) override
Undo a previous addMandatoryArc (engine names, e.g. "X[1]", "C").

References KTBNLearner(), gum::learning::IKTBNLearner< GUM_SCALAR >::_encode_(), and eraseMandatoryArc().

Here is the call graph for this function:

◆ eraseMandatoryArc() [2/2]

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::eraseMandatoryArc ( std::string_view tailNode,
std::string_view headNode )
overridevirtual

Undo a previous addMandatoryArc (engine names, e.g. "X[1]", "C").

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 521 of file KTBNLearner_tpl.h.

522 {
523 // no feasibility check: a backward-in-time arc can never have been added, so
524 // erasing it is harmless -- it resolves to a no-op on the owning learner.
527 });
528 return *this;
529 }

References _forOwningLearner_().

Referenced by eraseMandatoryArc().

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

◆ eraseNoChildrenNode() [1/2]

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::eraseNoChildrenNode ( std::string_view base,
int slice )
overridevirtual

Undo a previous addNoChildrenNode for a single (base, slice) node.

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 643 of file KTBNLearner_tpl.h.

644 {
646 }
KTBNLearner< GUM_SCALAR > & eraseNoChildrenNode(std::string_view base, int slice) override
Undo a previous addNoChildrenNode for a single (base, slice) node.

References KTBNLearner(), gum::learning::IKTBNLearner< GUM_SCALAR >::_encode_(), and eraseNoChildrenNode().

Referenced by eraseNoChildrenNode().

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

◆ eraseNoChildrenNode() [2/2]

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::eraseNoChildrenNode ( std::string_view name)
overridevirtual

Undo addNoChildrenNode for a node given by bracket notation.

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 649 of file KTBNLearner_tpl.h.

649 {
653 if (slice < (int)_prior_ktbn_.k() - 1) _initialLearner_->eraseNoChildrenNode(name);
654 if (isAtemp && _atemporalLearner_) _atemporalLearner_->eraseNoChildrenNode(name);
655 return *this;
656 }

References KTBNLearner(), _atemporalLearner_, gum::learning::IKTBNLearner< GUM_SCALAR >::_determineNode_(), _initialLearner_, _prior_ktbn_, _transitionLearner_, and gum::KTBN< GUM_SCALAR >::ATEMPORAL.

Here is the call graph for this function:

◆ eraseNoParentNode() [1/2]

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::eraseNoParentNode ( std::string_view base,
int slice )
overridevirtual

Undo a previous addNoParentNode for a single (base, slice) node.

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 607 of file KTBNLearner_tpl.h.

608 {
610 }
KTBNLearner< GUM_SCALAR > & eraseNoParentNode(std::string_view base, int slice) override
Undo a previous addNoParentNode for a single (base, slice) node.

References KTBNLearner(), gum::learning::IKTBNLearner< GUM_SCALAR >::_encode_(), and eraseNoParentNode().

Referenced by eraseNoParentNode().

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

◆ eraseNoParentNode() [2/2]

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::eraseNoParentNode ( std::string_view name)
overridevirtual

Undo addNoParentNode for a node given by bracket notation.

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 613 of file KTBNLearner_tpl.h.

613 {
615 const int last = (int)_prior_ktbn_.k() - 1;
618 else if (slice == last) _transitionLearner_->eraseNoParentNode(name);
619 else if (!isAtemp)
620 // Past slices keep their transition-learner root constraint (confines learning
621 // to the kernel); only lift the constraint in the initial learner.
622 _initialLearner_->eraseNoParentNode(name);
623 return *this;
624 }

References KTBNLearner(), _atemporalLearner_, gum::learning::IKTBNLearner< GUM_SCALAR >::_determineNode_(), _initialLearner_, _prior_ktbn_, _transitionLearner_, and gum::KTBN< GUM_SCALAR >::ATEMPORAL.

Here is the call graph for this function:

◆ erasePossibleEdge() [1/2]

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::erasePossibleEdge ( std::string_view tail,
std::string_view head )
overridevirtual

Undo a previous addPossibleEdge using engine names (e.g. "X[1]", "C").

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 703 of file KTBNLearner_tpl.h.

704 {
705 const int last = (int)_prior_ktbn_.k() - 1;
708
710 if (tailSlice < last && headSlice < last) _initialLearner_->erasePossibleEdge(tail, head);
711
714 if (_atemporalLearner_) _atemporalLearner_->erasePossibleEdge(tail, head);
715 } else {
717 }
718 return *this;
719 }
KTBNLearner< GUM_SCALAR > & erasePossibleEdge(std::string_view tailBase, int tailSlice, std::string_view headBase, int headSlice) override
Undo a previous addPossibleEdge.

References KTBNLearner(), _atemporalLearner_, gum::learning::IKTBNLearner< GUM_SCALAR >::_determineNode_(), _initialLearner_, _nbAtemporalPossibleEdges_, _nbTemporalPossibleEdges_, _prior_ktbn_, _transitionLearner_, and gum::KTBN< GUM_SCALAR >::ATEMPORAL.

Here is the call graph for this function:

◆ erasePossibleEdge() [2/2]

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::erasePossibleEdge ( std::string_view tailBase,
int tailSlice,
std::string_view headBase,
int headSlice )
overridevirtual

Undo a previous addPossibleEdge.

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 669 of file KTBNLearner_tpl.h.

References KTBNLearner(), gum::learning::IKTBNLearner< GUM_SCALAR >::_encode_(), and erasePossibleEdge().

Referenced by erasePossibleEdge().

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

◆ hasMissingValues()

template<GUM_Numeric GUM_SCALAR>
bool gum::learning::KTBNLearner< GUM_SCALAR >::hasMissingValues ( ) const

True if any internal database contains missing values.

Warning
Always false since build() performs complete-case selection: a row carrying a missing symbol is never inserted, so the databases hold no gap by construction. To learn whether the CSVs actually had any, read nbDroppedRows() instead.

Definition at line 873 of file KTBNLearner_tpl.h.

873 {
874 return _transitionLearner_->hasMissingValues() || _initialLearner_->hasMissingValues()
875 || (_atemporalLearner_ && _atemporalLearner_->hasMissingValues());
876 }

References _atemporalLearner_, _initialLearner_, and _transitionLearner_.

◆ isConstraintBased()

template<GUM_Numeric GUM_SCALAR>
bool gum::learning::KTBNLearner< GUM_SCALAR >::isConstraintBased ( ) const

True if the current structure-learning algorithm is constraint-based (e.g. MIIC).

Definition at line 768 of file KTBNLearner_tpl.h.

768 {
769 return _transitionLearner_->isConstraintBased();
770 }

References _transitionLearner_.

◆ isIgnoringMissingSymbols()

template<GUM_Numeric GUM_SCALAR>
INLINE bool gum::learning::KTBNLearner< GUM_SCALAR >::isIgnoringMissingSymbols ( ) const

Whether build() drops the rows carrying a missing symbol.

See also
nbDroppedRows() for how much was dropped, and for the warning on the likelihood distortion this causes.

Definition at line 863 of file KTBNLearner_tpl.h.

863 {
865 }

References _ignoreMissingSymbols_.

◆ isScoreBased()

template<GUM_Numeric GUM_SCALAR>
bool gum::learning::KTBNLearner< GUM_SCALAR >::isScoreBased ( ) const

True if the current structure-learning algorithm is score-based (e.g. BIC, AIC).

Definition at line 773 of file KTBNLearner_tpl.h.

773 {
774 return _transitionLearner_->isScoreBased();
775 }

References _transitionLearner_.

◆ k()

template<GUM_Numeric GUM_SCALAR>
Size gum::learning::KTBNLearner< GUM_SCALAR >::k ( ) const

Order \(k\) of the k-TBN being learned.

Definition at line 750 of file KTBNLearner_tpl.h.

750 {
751 return _prior_ktbn_.k();
752 }

References _prior_ktbn_.

Referenced by KTBNLearner(), KTBNLearner(), KTBNLearner(), _assemble_(), _build_(), _buildPriorFromBN_(), _buildPriorFromCSV_(), _forEachAllSlicesPair_(), _inferAtemporalVars_(), addForbiddenIntraSliceArc(), eraseForbiddenIntraSliceArc(), learnParameters(), and toString().

Here is the caller graph for this function:

◆ latentVariables()

template<GUM_Numeric GUM_SCALAR>
std::vector< std::pair< std::string, std::string > > gum::learning::KTBNLearner< GUM_SCALAR >::latentVariables ( ) const

Engine-name pairs (tail, head) of arcs flagged as hiding a latent variable by MIIC (merged from all internal learners). Returned as names rather than Arc/NodeId pairs because the internal learners live in independent NodeId spaces, so a bare NodeId would be ambiguous.

Definition at line 417 of file KTBNLearner_tpl.h.

417 {
418 // Each learner reports latent arcs in its own NodeId space, so translate each
419 // Arc to an (engine-name, engine-name) pair before merging. A pair among
420 // slices 0..k-2 can be flagged by both the transition and the initial learner,
421 // so deduplicate on a tab-joined key (engine names never contain a tab).
422 // Propagates BNLearner's OperationNotAllowed if MIIC is not selected.
425
427 if (!learner) return;
428 for (const auto& arc: learner->latentVariables()) {
429 std::string tail = learner->nameFromId(arc.tail());
430 std::string head = learner->nameFromId(arc.head());
431 if (seen.insert(tail + '\t' + head).second)
432 result.emplace_back(std::move(tail), std::move(head));
433 }
434 };
438 return result;
439 }
std::vector< std::pair< std::string, std::string > > latentVariables() const
Engine-name pairs (tail, head) of arcs flagged as hiding a latent variable by MIIC (merged from all i...

References _atemporalLearner_, _initialLearner_, and _transitionLearner_.

◆ learnKTBN()

template<GUM_Numeric GUM_SCALAR>
KTBN< GUM_SCALAR > gum::learning::KTBNLearner< GUM_SCALAR >::learnKTBN ( )
overridevirtual

Full learning (structure + CPTs). Mirrors BNLearner::learnBN().

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 229 of file KTBNLearner_tpl.h.

229 {
230 // Honour the possible-edge whitelist for atemporal->atemporal arcs: when a
231 // whitelist is active (>=1 possible edge with a temporal endpoint) but no
232 // atemporal->atemporal edge was whitelisted, the atemporal learner has an
233 // empty (hence unrestricted) list. Skip it entirely so no atemporal arc is
234 // produced; _assemble_ then leaves the atemporal variables as roots.
235 const bool suppressAtemporal
240 ? _atemporalLearner_->learnBN()
243 }
KTBN< GUM_SCALAR > _assemble_(const BayesNet< GUM_SCALAR > &transitionBN, const BayesNet< GUM_SCALAR > &initialBN, const BayesNet< GUM_SCALAR > &atemporalBN) const
glues the three parameter-learned BNs into a single k-TBN

References _assemble_(), _atemporalLearner_, _initialLearner_, _nbAtemporalPossibleEdges_, _nbTemporalPossibleEdges_, and _transitionLearner_.

Here is the call graph for this function:

◆ learnParameters()

template<GUM_Numeric GUM_SCALAR>
KTBN< GUM_SCALAR > gum::learning::KTBNLearner< GUM_SCALAR >::learnParameters ( const KTBN< GUM_SCALAR > & structure,
bool takeIntoAccountScore = true )

CPTs only, using the arc structure of structure. structure must have the same base variables (names and domains) as those used to construct this learner; mismatches throw at learn time.

Definition at line 246 of file KTBNLearner_tpl.h.

247 {
248 if (structure.k() != _prior_ktbn_.k())
250 "learnParameters: structure has k="
251 << structure.k() << " but this learner was built with k=" << _prior_ktbn_.k())
252 const int k = (int)_prior_ktbn_.k();
253
254 // Build one DAG per internal table, in that table's own NodeId space, by
255 // routing each of structure's (base, slice) arcs to the table that owns its
256 // head: slice k-1 goes to the transition table, atemporal heads go to the
257 // atemporal table (guaranteed atemporal-tailed too — a temporal variable can
258 // never be a parent of an atemporal one), everything else (slices 0..k-2,
259 // whether the tail is temporal or atemporal) goes to the initial table.
263
265 for (std::size_t id = 0; id < nbTransNodes; ++id)
267
269 for (std::size_t id = 0; id < nbInitNodes; ++id)
271
274 for (std::size_t id = 0; id < nbAtemNodes; ++id)
275 atemporalDAG.addNodeWithId(NodeId(id));
276 }
277
278 for (const auto& [tail, head]: structure.arcs()) {
279 const auto& [tailBase, tailSlice] = tail;
280 const auto& [headBase, headSlice] = head;
283
284 if (headSlice == k - 1) {
285 transitionDAG.addArc(_transitionLearner_->idFromName(tailName),
286 _transitionLearner_->idFromName(headName));
289 atemporalDAG.addArc(_atemporalLearner_->idFromName(tailName),
290 _atemporalLearner_->idFromName(headName));
291 } else {
292 initialDAG.addArc(_initialLearner_->idFromName(tailName),
293 _initialLearner_->idFromName(headName));
294 }
295 }
296
305
307 }
KTBN< GUM_SCALAR > learnParameters(const KTBN< GUM_SCALAR > &structure, bool takeIntoAccountScore=true)
CPTs only, using the arc structure of structure. structure must have the same base variables (names a...

References _assemble_(), _atemporalLearner_, gum::learning::IKTBNLearner< GUM_SCALAR >::_encode_(), _initialLearner_, _prior_ktbn_, _transitionLearner_, gum::DAG::addArc(), gum::NodeGraphPart::addNodeWithId(), gum::KTBN< GUM_SCALAR >::ATEMPORAL, GUM_ERROR, and k().

Here is the call graph for this function:

◆ names()

template<GUM_Numeric GUM_SCALAR>
std::vector< std::string > gum::learning::KTBNLearner< GUM_SCALAR >::names ( ) const

Base names (no slice suffix), one entry per base variable (temporal or atemporal), in the original CSV header order.

Definition at line 879 of file KTBNLearner_tpl.h.

879 {
880 // The first nbCols() columns of the transition table are the base variables
881 // (slice-0 temporals then atemporals); the remaining columns are just later
882 // slices of those same temporals. So strip the slice suffix off the first
883 // nbCols() engine names to get each base variable exactly once.
884 const auto& engine = _transitionLearner_->names();
885 const Size n = nbCols();
887 result.reserve(n);
888 for (Size i = 0; i < n; ++i)
889 result.push_back(_determineNode_(engine[i]).first);
890 return result;
891 }

References gum::learning::IKTBNLearner< GUM_SCALAR >::_determineNode_(), _transitionLearner_, and nbCols().

Referenced by _buildPriorFromCSV_().

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

◆ nbCols()

template<GUM_Numeric GUM_SCALAR>
Size gum::learning::KTBNLearner< GUM_SCALAR >::nbCols ( ) const

Number of columns in each CSV, i.e. of base variables (temporal + atemporal).

Definition at line 755 of file KTBNLearner_tpl.h.

755 {
756 return _prior_ktbn_.nbTemporalVars() + _prior_ktbn_.nbAtemporalVars();
757 }

References _prior_ktbn_.

Referenced by domainSizes(), names(), and toString().

Here is the caller graph for this function:

◆ nbDroppedRows()

template<GUM_Numeric GUM_SCALAR>
INLINE Size gum::learning::KTBNLearner< GUM_SCALAR >::nbDroppedRows ( ) const

Number of rows dropped from the internal databases because they carried a missing symbol.

aGrUM's structure learning refuses a database holding any missing value (gum::learning::IBNLearner::learnDag_), so build() performs complete-case selection: a row is inserted only if every cell it needs is observed. This mirrors the cross-k score, which likewise skips any instance whose family is incomplete, so both layers agree on which data counts. hasMissingValues() is consequently always false.

The unit differs per table: a transition row is a whole width-k window, so one gap costs up to \(k\) of them; an initial or atemporal row is a whole trajectory's contribution.

Warning
Complete-case selection distorts any likelihood computed on the result, and it does so unevenly across \(k\).

A row is dropped as soon as one cell of the window it feeds is missing, so a single gap costs up to \(k\) transition rows. A larger \(k\) spans more rows per window and therefore loses proportionally more of them.

That matters because \(\log_2 L\) is a sum of per-instance terms, every one of which is \(\leq 0\). Scoring fewer instances removes negative terms, so it raises the total — a model does not fit better, it is simply charged for less. Two consequences:

  • a \(\log_2 L\) obtained with dropping is not comparable to one obtained on complete data, nor to one obtained at a different missing-value rate;
  • comparing orders this way (gum::learning::KTBNAdaptiveLearner) skews towards larger \(k\), since the larger candidate both drops more instances and gains more from having dropped them. The BIC penalty does not offset it: \(\log_2 N\) is taken once from the raw trajectory length and held fixed across candidates, so the penalty describes a sample size the likelihood no longer uses.

The effect grows with the missing-value rate. Read bestK() and scorePerCandidateK() with that in mind, and prefer complete trajectories whenever the order itself is the question being asked.

Note
Independently of the above, complete-case selection is only unbiased when values are missing completely at random (MCAR). If missingness depends on the data, the retained rows are a biased sample and the learned CPTs inherit that bias.

Definition at line 868 of file KTBNLearner_tpl.h.

868 {
869 return _nbDroppedRows_;
870 }

References _nbDroppedRows_.

◆ nbRows()

template<GUM_Numeric GUM_SCALAR>
std::vector< Size > gum::learning::KTBNLearner< GUM_SCALAR >::nbRows ( ) const

Number of time steps in each trajectory CSV (one entry per sample, in load order). This is the raw trajectory length; the transition table's sliding-window row count for trajectory i is nbRows()[i] - k + 1.

Definition at line 760 of file KTBNLearner_tpl.h.

760 {
761 // Raw trajectory lengths captured by _build_(), one entry per sample. Unlike
762 // BNLearner::nbRows() (a single flat-table row count), a trajectory learner
763 // has one length per sequence, so the per-sample vector is the natural analog.
764 return _nbTimeSlices_;
765 }

References _nbTimeSlices_.

◆ nbSamples()

template<GUM_Numeric GUM_SCALAR>
Size gum::learning::KTBNLearner< GUM_SCALAR >::nbSamples ( ) const

Number of trajectory CSV files loaded (the constructor's nbSamples).

Definition at line 856 of file KTBNLearner_tpl.h.

856 {
857 // the initial table holds exactly one row per trajectory, so its row count
858 // is the number of trajectory files given to the constructor.
859 return _initialLearner_->nbRows();
860 }

References _initialLearner_.

Referenced by KTBNLearner(), KTBNLearner(), KTBNLearner(), _build_(), and _inferAtemporalVars_().

Here is the caller graph for this function:

◆ operator=() [1/2]

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::operator= ( const KTBNLearner< GUM_SCALAR > & )
privatedelete

References KTBNLearner().

Here is the call graph for this function:

◆ operator=() [2/2]

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::operator= ( KTBNLearner< GUM_SCALAR > && )
privatedelete

References KTBNLearner().

Here is the call graph for this function:

◆ setMaxIndegree()

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::setMaxIndegree ( Size max_indegree)
overridevirtual

Cap the number of parents of any single node.

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 740 of file KTBNLearner_tpl.h.

740 {
742 return *this;
743 }
KTBNLearner< GUM_SCALAR > & setMaxIndegree(Size max_indegree) override
Cap the number of parents of any single node.

References KTBNLearner(), and _forEachLearner_().

Referenced by gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_applyConstraints_().

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

◆ state()

template<GUM_Numeric GUM_SCALAR>
std::vector< std::tuple< std::string, std::string, std::string > > gum::learning::KTBNLearner< GUM_SCALAR >::state ( ) const

Settings as a vector of (key, value, comment) tuples (mirrors BNLearner::state()).

Definition at line 803 of file KTBNLearner_tpl.h.

803 {
804 // settings are applied identically to every internal learner, so the
805 // transition learner's state represents the whole KTBNLearner.
807
808 // The transition learner lists every slice of every temporal variable
809 // (e.g. "W[0][2], W[1][2], W[2][2], W[3][2]"). Replace that row with one
810 // entry per base variable (e.g. "W[2]") built directly from the KTBN's own
811 // variable sets — no string parsing, robust to any base name.
812 for (auto& [key, val, comment]: result) {
813 if (key != "Variables") continue;
814 // Build base -> domainSize from the prior KTBN (slice 0 for temporal, AT for atemporal).
816 bool first = true;
817 auto emit = [&](const std::string& base, int slice) {
818 const auto& var = _prior_ktbn_.variable(base, slice);
819 if (!first) collapsed += ", ";
820 collapsed += base + "[" + std::to_string(var.domainSize()) + "]";
821 first = false;
822 };
823 for (const auto& base: _prior_ktbn_.temporalVarNames())
824 emit(base, 0);
825 for (const auto& base: _prior_ktbn_.atemporalVarNames())
827 val = collapsed;
828 break;
829 }
830 return result;
831 }
std::vector< std::tuple< std::string, std::string, std::string > > state() const
Settings as a vector of (key, value, comment) tuples (mirrors BNLearner::state()).

References _prior_ktbn_, _transitionLearner_, and gum::KTBN< GUM_SCALAR >::ATEMPORAL.

◆ toString()

template<GUM_Numeric GUM_SCALAR>
std::string gum::learning::KTBNLearner< GUM_SCALAR >::toString ( ) const

Human-readable summary of the learner's current configuration.

Definition at line 778 of file KTBNLearner_tpl.h.

778 {
779 // Emit a k-TBN-specific header, then delegate score/algo/prior/constraint
780 // details to each internal learner's toString().
782 s << "k : " << k() << '\n';
783 s << "Variables : " << nbCols() << " (" << _prior_ktbn_.nbTemporalVars()
784 << " temporal, " << _prior_ktbn_.nbAtemporalVars() << " atemporal)" << '\n';
785 s << "Transition rows : " << _transitionLearner_->nbRows() << '\n';
786 s << "Initial rows : " << _initialLearner_->nbRows() << '\n';
787 s << '\n';
788 s << "=== Transition learner (arcs into slice " << (k() - 1) << ") ===" << '\n';
790 s << '\n';
791 s << "=== Initial learner (slices 0.." << (k() - 2) << ") ===" << '\n';
793 if (_atemporalLearner_) {
794 s << '\n';
795 s << "=== Atemporal learner (atemporal->atemporal arcs) ===" << '\n';
797 }
798 return s.str();
799 }
std::string toString() const
Human-readable summary of the learner's current configuration.

References _atemporalLearner_, _initialLearner_, _prior_ktbn_, _transitionLearner_, k(), and nbCols().

Here is the call graph for this function:

◆ useExtendedGreedyHillClimbing()

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::useExtendedGreedyHillClimbing ( )
overridevirtual

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 374 of file KTBNLearner_tpl.h.

374 {
376 return *this;
377 }
KTBNLearner< GUM_SCALAR > & useExtendedGreedyHillClimbing() override

References KTBNLearner(), and _forEachLearner_().

Referenced by gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_applyConfig_().

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

◆ useGreedyHillClimbing()

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::useGreedyHillClimbing ( )
overridevirtual

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 368 of file KTBNLearner_tpl.h.

368 {
370 return *this;
371 }
KTBNLearner< GUM_SCALAR > & useGreedyHillClimbing() override

References KTBNLearner(), and _forEachLearner_().

Referenced by gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_applyConfig_().

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

◆ useLocalSearchWithTabuList()

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::useLocalSearchWithTabuList ( Size tabu_size = 100,
Size nb_decrease = 2 )
overridevirtual

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 381 of file KTBNLearner_tpl.h.

381 {
384 return *this;
385 }
KTBNLearner< GUM_SCALAR > & useLocalSearchWithTabuList(Size tabu_size=100, Size nb_decrease=2) override

References _forEachLearner_().

Referenced by gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_applyConfig_().

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

◆ useMDLCorrection()

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::useMDLCorrection ( )
overridevirtual

Engine-name pairs (tail, head) of arcs flagged as hiding a latent variable by MIIC (merged from all internal learners). Returned as names rather than Arc/NodeId pairs because the internal learners live in independent NodeId spaces, so a bare NodeId would be ambiguous.

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 404 of file KTBNLearner_tpl.h.

404 {
406 return *this;
407 }
KTBNLearner< GUM_SCALAR > & useMDLCorrection() override
Engine-name pairs (tail, head) of arcs flagged as hiding a latent variable by MIIC (merged from all i...

References KTBNLearner(), and _forEachLearner_().

Here is the call graph for this function:

◆ useMIIC()

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::useMIIC ( )
overridevirtual

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 388 of file KTBNLearner_tpl.h.

388 {
390 return *this;
391 }
KTBNLearner< GUM_SCALAR > & useMIIC() override

References KTBNLearner(), and _forEachLearner_().

Here is the call graph for this function:

◆ useNMLCorrection()

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::useNMLCorrection ( )
overridevirtual

Engine-name pairs (tail, head) of arcs flagged as hiding a latent variable by MIIC (merged from all internal learners). Returned as names rather than Arc/NodeId pairs because the internal learners live in independent NodeId spaces, so a bare NodeId would be ambiguous.

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 398 of file KTBNLearner_tpl.h.

398 {
400 return *this;
401 }
KTBNLearner< GUM_SCALAR > & useNMLCorrection() override
Engine-name pairs (tail, head) of arcs flagged as hiding a latent variable by MIIC (merged from all i...

References KTBNLearner(), and _forEachLearner_().

Referenced by gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_applyConfig_().

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

◆ useNoCorrection()

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::useNoCorrection ( )
overridevirtual

Engine-name pairs (tail, head) of arcs flagged as hiding a latent variable by MIIC (merged from all internal learners). Returned as names rather than Arc/NodeId pairs because the internal learners live in independent NodeId spaces, so a bare NodeId would be ambiguous.

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 410 of file KTBNLearner_tpl.h.

410 {
412 return *this;
413 }
KTBNLearner< GUM_SCALAR > & useNoCorrection() override
Engine-name pairs (tail, head) of arcs flagged as hiding a latent variable by MIIC (merged from all i...

References KTBNLearner(), and _forEachLearner_().

Referenced by gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_applyConfig_().

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

◆ useScoreAIC()

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::useScoreAIC ( )
overridevirtual

Returns a warning string if the current score and prior are incompatible, empty string otherwise.

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 315 of file KTBNLearner_tpl.h.

315 {
317 return *this;
318 }
KTBNLearner< GUM_SCALAR > & useScoreAIC() override
Returns a warning string if the current score and prior are incompatible, empty string otherwise.

References KTBNLearner(), and _forEachLearner_().

Referenced by gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_applyConfig_().

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

◆ useScoreBD()

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::useScoreBD ( )
overridevirtual

Returns a warning string if the current score and prior are incompatible, empty string otherwise.

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 321 of file KTBNLearner_tpl.h.

321 {
323 return *this;
324 }
KTBNLearner< GUM_SCALAR > & useScoreBD() override
Returns a warning string if the current score and prior are incompatible, empty string otherwise.

References KTBNLearner(), and _forEachLearner_().

Referenced by gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_applyConfig_().

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

◆ useScoreBDeu()

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::useScoreBDeu ( )
overridevirtual

Returns a warning string if the current score and prior are incompatible, empty string otherwise.

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 327 of file KTBNLearner_tpl.h.

327 {
329 return *this;
330 }
KTBNLearner< GUM_SCALAR > & useScoreBDeu() override
Returns a warning string if the current score and prior are incompatible, empty string otherwise.

References KTBNLearner(), and _forEachLearner_().

Here is the call graph for this function:

◆ useScoreBIC()

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::useScoreBIC ( )
overridevirtual

Returns a warning string if the current score and prior are incompatible, empty string otherwise.

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 333 of file KTBNLearner_tpl.h.

333 {
335 return *this;
336 }
KTBNLearner< GUM_SCALAR > & useScoreBIC() override
Returns a warning string if the current score and prior are incompatible, empty string otherwise.

References KTBNLearner(), and _forEachLearner_().

Referenced by gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_applyConfig_().

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

◆ useScorefNML()

template<GUM_Numeric GUM_SCALAR>
void gum::learning::KTBNLearner< GUM_SCALAR >::useScorefNML ( )
overridevirtual

Returns a warning string if the current score and prior are incompatible, empty string otherwise.

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 351 of file KTBNLearner_tpl.h.

351 {
353 }
void useScorefNML() override
Returns a warning string if the current score and prior are incompatible, empty string otherwise.

References _forEachLearner_().

Referenced by gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_applyConfig_().

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

◆ useScoreLog2Likelihood()

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::useScoreLog2Likelihood ( )
overridevirtual

Returns a warning string if the current score and prior are incompatible, empty string otherwise.

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 339 of file KTBNLearner_tpl.h.

339 {
341 return *this;
342 }
KTBNLearner< GUM_SCALAR > & useScoreLog2Likelihood() override
Returns a warning string if the current score and prior are incompatible, empty string otherwise.

References KTBNLearner(), and _forEachLearner_().

Referenced by gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_applyConfig_().

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

◆ useScoreMDL()

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::useScoreMDL ( )
overridevirtual

Returns a warning string if the current score and prior are incompatible, empty string otherwise.

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 345 of file KTBNLearner_tpl.h.

345 {
347 return *this;
348 }
KTBNLearner< GUM_SCALAR > & useScoreMDL() override
Returns a warning string if the current score and prior are incompatible, empty string otherwise.

References KTBNLearner(), and _forEachLearner_().

Referenced by gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_applyConfig_().

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

◆ useSmoothingPrior()

template<GUM_Numeric GUM_SCALAR>
KTBNLearner< GUM_SCALAR > & gum::learning::KTBNLearner< GUM_SCALAR >::useSmoothingPrior ( double weight = 1.0)
overridevirtual

Implements gum::learning::IKTBNLearner< GUM_SCALAR >.

Definition at line 446 of file KTBNLearner_tpl.h.

446 {
448 return *this;
449 }
KTBNLearner< GUM_SCALAR > & useSmoothingPrior(double weight=1.0) override

References KTBNLearner(), and _forEachLearner_().

Referenced by gum::learning::KTBNAdaptiveLearner< GUM_SCALAR >::_applyConfig_().

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

Member Data Documentation

◆ _atemporalLearner_

template<GUM_Numeric GUM_SCALAR>
std::unique_ptr< BNLearner< GUM_SCALAR > > gum::learning::KTBNLearner< GUM_SCALAR >::_atemporalLearner_
private

◆ _ignoreMissingSymbols_

template<GUM_Numeric GUM_SCALAR>
bool gum::learning::KTBNLearner< GUM_SCALAR >::_ignoreMissingSymbols_ {false}
private

prior k-TBN: the single source of truth for k, variable domains, temporal/atemporal classification and engine names. Everything else (base lists, column names) is derived from it on demand. Analogous to the prior BayesNet stored in BNLearner when a reference BN is given. whether build() drops incomplete rows rather than refusing the data

Definition at line 587 of file KTBNLearner.h.

Referenced by KTBNLearner(), KTBNLearner(), _build_(), and isIgnoringMissingSymbols().

◆ _initialLearner_

template<GUM_Numeric GUM_SCALAR>
std::unique_ptr< BNLearner< GUM_SCALAR > > gum::learning::KTBNLearner< GUM_SCALAR >::_initialLearner_
private

◆ _nbAtemporalPossibleEdges_

template<GUM_Numeric GUM_SCALAR>
Size gum::learning::KTBNLearner< GUM_SCALAR >::_nbAtemporalPossibleEdges_ = 0
private

Definition at line 607 of file KTBNLearner.h.

Referenced by addPossibleEdge(), copyState(), erasePossibleEdge(), and learnKTBN().

◆ _nbDroppedRows_

template<GUM_Numeric GUM_SCALAR>
Size gum::learning::KTBNLearner< GUM_SCALAR >::_nbDroppedRows_ {0}
private

number of time steps (rows) in each trajectory CSV, in load order. rows build() dropped because they carried a missing symbol

Definition at line 593 of file KTBNLearner.h.

Referenced by _build_(), and nbDroppedRows().

◆ _nbTemporalPossibleEdges_

template<GUM_Numeric GUM_SCALAR>
Size gum::learning::KTBNLearner< GUM_SCALAR >::_nbTemporalPossibleEdges_ = 0
private

counts of currently-active possible edges, split by kind: edges with at least one temporal endpoint, and edges between two atemporal variables. Maintained by add/erasePossibleEdge(). When a whitelist is active (temporal count > 0) but no atemporal->atemporal edge is whitelisted, the atemporal learner would otherwise stay unrestricted; learnKTBN() then suppresses it so the whitelist is honoured (no atemporal arc is produced).

Definition at line 606 of file KTBNLearner.h.

Referenced by addPossibleEdge(), copyState(), erasePossibleEdge(), and learnKTBN().

◆ _nbTimeSlices_

template<GUM_Numeric GUM_SCALAR>
std::vector< Size > gum::learning::KTBNLearner< GUM_SCALAR >::_nbTimeSlices_
private

Captured once by build() and exposed by nbRows(). This is the raw trajectory length, not the sliding-window row count of the transition table (which is nbTimeSlices[i] - k + 1 summed over trajectories).

Definition at line 598 of file KTBNLearner.h.

Referenced by _build_(), and nbRows().

◆ _prior_ktbn_

◆ _transitionLearner_


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