56 template < GUM_Numeric GUM_SCALAR >
59 return std::string{base} +
'[' + std::to_string(slice) +
']';
62 template < GUM_Numeric GUM_SCALAR >
63 std::pair< std::string, int >
70 const std::size_t bracketPos = name.rfind(
'[');
73 const std::string_view bracketContent{name.c_str() + bracketPos + 1,
74 name.size() - bracketPos - 1};
75 if (bracketContent.empty() || bracketContent.back() !=
']')
78 const std::string_view digits = bracketContent.substr(0, bracketContent.size() - 1);
80 for (
const char c: digits)
81 if (std::isdigit(
static_cast< unsigned char >(c)) == 0)
86 slice = std::stoi(std::string{digits});
87 }
catch (
const std::out_of_range&) {
89 "Node name '" << name <<
"' has a slice index too large to represent as int.")
91 return {name.substr(0, bracketPos), slice};
94 template < GUM_Numeric GUM_SCALAR >
96 std::string_view head,
97 std::string_view action)
const {
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")
105 if (headSlice < tailSlice)
107 "cannot " << action <<
" " << tail <<
" -> " << head
108 <<
": its head is at an earlier time slice than its tail, which "
109 "violates temporal causality")
112 template < GUM_Numeric GUM_SCALAR >
114 std::string_view dirPath,
115 std::string_view csvBaseName,
117 const std::vector< std::string >& missingSymbols) {
118 namespace fs = std::filesystem;
119 const fs::path dir{dirPath};
120 const std::string stem{csvBaseName};
121 const std::unordered_set< std::string > missing(missingSymbols.begin(), missingSymbols.end());
123 std::vector< std::string > header;
124 std::vector< std::size_t > candidates;
125 std::vector< std::string > firstSeen;
129 for (
Size i = 0; i < nbSamples; ++i) {
134 if (i > 0 && candidates.empty())
break;
136 const fs::path file = dir / (stem + std::to_string(i + 1) +
".csv");
137 std::ifstream is(file, std::ifstream::in);
142 const auto& raw = parser.
current();
145 header.assign(raw.begin(), raw.end());
146 candidates.resize(header.size());
147 std::iota(candidates.begin(), candidates.end(), std::size_t{0});
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]);
154 "Header of " << file.string() <<
" differs from trajectory 1");
157 haveFirstSeen.assign(header.size(),
false);
158 firstSeen.assign(header.size(), {});
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());
168 for (std::size_t idx = 0; idx < candidates.size();) {
169 const std::size_t c = candidates[idx];
170 if (missing.contains(tokens[c])) {
174 if (!haveFirstSeen[c]) {
175 firstSeen[c] = tokens[c];
176 haveFirstSeen[c] =
true;
178 }
else if (tokens[c] != firstSeen[c]) {
179 candidates[idx] = candidates.back();
180 candidates.pop_back();
185 if (candidates.empty())
break;
189 std::unordered_set< std::string > atemporalVars;
190 for (
const std::size_t c: candidates)
191 atemporalVars.insert(header[c]);
192 return atemporalVars;
195 template < GUM_Numeric GUM_SCALAR >
197 std::string_view context)
const {
198 const std::string b{base};
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")
209 template < GUM_Numeric GUM_SCALAR >
213 "a k-TBN learner requires "
214 << label <<
" >= 2: k=1 is a static Bayesian network, use BNLearner instead")
Common configuration interface for k-TBN learners.
Exception : input/output problem.
Exception: at least one argument passed to a function is not what was expected.
static constexpr int ATEMPORAL
Conventional time-slice value denoting an atemporal (static) variable.
Class for fast parsing of CSV file (never more than one line in application memory).
bool next()
gets the next line of the csv stream and parses it
std::size_t nbLine() const
returns the current line number within the stream
const std::vector< std::string > & current() const
returns the current parsed line
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 ...
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...
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...
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...
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...
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...
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 t...
#define GUM_ERROR(type, msg)
std::size_t Size
In aGrUM, hashed values are unsigned long int.
GUM_SHARED_PUBLIC bool contains(std::string_view s, std::string_view needle)
true if needle in s
include the inlined functions if necessary
gum is the global namespace for all aGrUM entities