aGrUM 3.1.1
a C++ library for (probabilistic) graphical models
BNDatabaseGenerator_tpl.h
Go to the documentation of this file.
1/****************************************************************************
2 * This file is part of the aGrUM/pyAgrum library. *
3 * *
4 * Copyright (c) 2005-2026 by *
5 * - Pierre-Henri WUILLEMIN(_at_LIP6) *
6 * - Christophe GONZALES(_at_AMU) *
7 * *
8 * The aGrUM/pyAgrum library is free software; you can redistribute it *
9 * and/or modify it under the terms of either : *
10 * *
11 * - the GNU Lesser General Public License as published by *
12 * the Free Software Foundation, either version 3 of the License, *
13 * or (at your option) any later version, *
14 * - the MIT license (MIT), *
15 * - or both in dual license, as here. *
16 * *
17 * (see https://agrum.gitlab.io/articles/dual-licenses-lgplv3mit.html) *
18 * *
19 * This aGrUM/pyAgrum library is distributed in the hope that it will be *
20 * useful, but WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
21 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES MERCHANTABILITY or FITNESS *
22 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
25 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
26 * OTHER DEALINGS IN THE SOFTWARE. *
27 * *
28 * See LICENCES for more details. *
29 * *
30 * SPDX-FileCopyrightText: Copyright 2005-2026 *
31 * - Pierre-Henri WUILLEMIN(_at_LIP6) *
32 * - Christophe GONZALES(_at_AMU) *
33 * SPDX-License-Identifier: LGPL-3.0-or-later OR MIT *
34 * *
35 * Contact : info_at_agrum_dot_org *
36 * homepage : http://agrum.gitlab.io *
37 * gitlab : https://gitlab.com/agrumery/agrum *
38 * *
39 ****************************************************************************/
40
41#pragma once
42
43
49
52
53namespace gum::learning {
54
55
57 template < GUM_Numeric GUM_SCALAR >
59 _bn_(bn) {
60 GUM_CONSTRUCTOR(BNDatabaseGenerator)
61
62 // get the node names => they will serve as ids
63 NodeId id = 0;
64 for (const auto& var: _bn_.internalDag()) {
65 auto name = _bn_.variable(var).name();
66 _names2ids_.insert(name, var);
67 ++id;
68 }
69 _nbVars_ = id;
70 _varOrder_.resize(_nbVars_);
72 std::iota(_varOrder_.begin(), _varOrder_.end(), (Idx)0);
73 }
74
76 template < GUM_Numeric GUM_SCALAR >
80
82 template < GUM_Numeric GUM_SCALAR >
84 const Instantiation inst;
85 return drawSamples(nbSamples, inst);
86 }
87
89 template < GUM_Numeric GUM_SCALAR >
91 const Instantiation& evs,
92 int timeout) {
93 int progress = 0;
94
95 if (onProgress.hasListener()) { GUM_EMIT2(onProgress, progress, 0.0); }
96
97 _database_.clear();
98 _database_.resize(nbSamples);
99 for (auto& row: _database_) {
100 row.resize(_nbVars_);
101 }
102 // get the order in which the nodes will be sampled
103 const auto topOrder = _bn_.topologicalOrder();
104 gum::Instantiation particule;
105
106 // create instantiations in advance
107 for (NodeId node = 0; node < _nbVars_; ++node)
108 particule.add(_bn_.variable(node));
109
110 gum::Timer timer;
111 timer.reset();
112
113 // perform the sampling
115 Idx idSample = 0;
116 while (idSample < nbSamples) {
117 if (onProgress.hasListener()) {
118 auto p = int((idSample * 100) / nbSamples);
119 if (p != progress) {
120 progress = p;
121 GUM_EMIT2(onProgress, progress, timer.step());
122 }
123 }
124 std::vector< Idx >& sample = _database_.at(idSample);
125 bool reject = false;
126 for (Idx rank = 0; rank < _nbVars_; ++rank) {
127 const NodeId node = topOrder[rank];
128 const auto& var = _bn_.variable(node);
129 const auto& cpt = _bn_.cpt(node);
130
131 const double nb = gum::randomProba();
132 double cumul = 0.0;
133 for (particule.setFirstVar(var); !particule.end(); particule.incVar(var)) {
134 cumul += cpt[particule];
135 if (cumul >= nb) break;
136 }
137 if (particule.end()) particule.setLastVar(var);
138
139 if ((!evs.empty()) && evs.contains(var) && (evs.val(var) != particule.val(var))) {
140 reject = true;
141 break;
142 }
143
144 sample.at(node) = particule.val(var);
145 _log2likelihood_ += std::log2(_bn_.cpt(node)[particule]);
146 }
147 if (timeout > 0 && timer.step() > timeout) { break; }
148 if (reject) { continue; }
149 idSample++;
150 }
151
152 if (idSample > 0) {
153 if (idSample < nbSamples) _database_.resize(idSample);
154 } else {
155 _database_.clear();
156 }
157 _drawnSamples_ = true;
158
159 if (onProgress.hasListener()) {
161 std::format("Database of size {}({}) generated in {} seconds. Log2likelihood : {}",
162 idSample,
163 nbSamples,
164 timer.step(),
166 }
167
168 return _log2likelihood_;
169 }
170
171 template < GUM_Numeric GUM_SCALAR >
173 if (!_drawnSamples_) { GUM_ERROR(OperationNotAllowed, "drawSamples() must be called first.") }
174
175 return _database_.size();
176 }
177
178 template < GUM_Numeric GUM_SCALAR >
180 if (!_drawnSamples_) { GUM_ERROR(OperationNotAllowed, "drawSamples() must be called first.") }
181
182 return _nbVars_;
183 }
184
185 template < GUM_Numeric GUM_SCALAR >
187 if (!_drawnSamples_) { GUM_ERROR(OperationNotAllowed, "drawSamples() must be called first.") }
188 return _database_.at(row).at(_varOrder_.at(col));
189 }
190
191 template < GUM_Numeric GUM_SCALAR >
193 if (!_drawnSamples_) { GUM_ERROR(OperationNotAllowed, "drawSamples() must be called first.") }
194 const auto j = _varOrder_.at(col);
195 return _label_(_database_.at(row), _bn_.variable(j), j);
196 }
197
198 template < GUM_Numeric GUM_SCALAR >
202
203 template < GUM_Numeric GUM_SCALAR >
207
208 template < GUM_Numeric GUM_SCALAR >
212
214 template < GUM_Numeric GUM_SCALAR >
215 void BNDatabaseGenerator< GUM_SCALAR >::toCSV(std::string_view csvFileURL,
216 bool useLabels,
217 bool append,
218 std::string csvSeparator,
219 bool checkOnAppend) const {
220 if (!_drawnSamples_) { GUM_ERROR(OperationNotAllowed, "drawSamples() must be called first.") }
221
222 if (csvSeparator.find('\n') != std::string::npos) {
223 GUM_ERROR(InvalidArgument, "csvSeparator must not contain end-line characters")
224 }
225
226 bool includeHeader = true;
227 if (append) {
228 std::ifstream csvFile(std::filesystem::path{csvFileURL});
229 if (csvFile) {
230 if (auto varOrder = _varOrderFromCSV_(csvFile, csvSeparator);
231 checkOnAppend && varOrder != _varOrder_)
233 "Inconsistent variable order in csvFile when appending. You "
234 "can use setVarOrderFromCSV(url) function to get the right "
235 "order. You could also set parameter checkOnAppend=false if you "
236 "know what you are doing.")
237 includeHeader = false;
238 }
239 csvFile.close();
240 }
241
242
243 auto ofstreamFlag = append ? std::ofstream::app : std::ofstream::out;
244
245 std::ofstream os(std::filesystem::path{csvFileURL}, ofstreamFlag);
246 bool firstCol = true;
247 if (includeHeader) {
248 for (const auto& i: _varOrder_) {
249 if (firstCol) {
250 firstCol = false;
251 } else {
252 os << csvSeparator;
253 }
254 os << _bn_.variable(i).name();
255 }
256 }
257 os << std::endl;
258
259 bool firstRow = true;
260 for (const auto& row: _database_) {
261 if (firstRow) {
262 firstRow = false;
263 } else {
264 os << std::endl;
265 }
266 firstCol = true;
267 for (const auto& i: _varOrder_) {
268 if (firstCol) {
269 firstCol = false;
270 } else {
271 os << csvSeparator;
272 }
273 if (useLabels) {
274 const auto& v = _bn_.variable(i);
275 if (v.varType() == VarType::DISCRETIZED) {
276 switch (_discretizedLabelMode_) {
277 case DiscretizedLabelMode::MEDIAN : os << v.numerical(row.at(i)); break;
279 os << static_cast< const IDiscretizedVariable& >(v).draw(row.at(i));
280 break;
281 case DiscretizedLabelMode::INTERVAL : os << v.label(row.at(i)); break;
282 }
283 } else {
284 os << v.label(row.at(i));
285 }
286 } else {
287 os << row[i];
288 }
289 }
290 }
291
292 os.close();
293 }
294
295 template < GUM_Numeric GUM_SCALAR >
296 std::string BNDatabaseGenerator< GUM_SCALAR >::_label_(const std::vector< Idx >& row,
297 const DiscreteVariable& v,
298 Idx i) const {
299 if (v.varType() == VarType::DISCRETIZED) {
300 switch (_discretizedLabelMode_) {
301 case DiscretizedLabelMode::MEDIAN : return std::to_string(v.numerical(row.at(i)));
303 return std::to_string(static_cast< const IDiscretizedVariable& >(v).draw(row.at(i)));
304 case DiscretizedLabelMode::INTERVAL : return v.label(row.at(i));
305 }
306 }
307
308 return v.label(row.at(i));
309 }
310
312 template < GUM_Numeric GUM_SCALAR >
314 if (!_drawnSamples_) GUM_ERROR(OperationNotAllowed, "proceed() must be called first.")
315
316 DatabaseTable db;
317 std::vector< std::string > varNames;
318 varNames.reserve(_nbVars_);
319 for (const auto& i: _varOrder_) {
320 varNames.push_back(_names2ids_.first(i));
321 }
322
323 // create the translators
324 for (std::size_t i = 0; i < _nbVars_; ++i) {
325 const Variable& var = _bn_.variable(_varOrder_[i]);
326 db.insertTranslator(var, i);
327 }
328
329 if (useLabels) {
330 std::vector< std::string > xrow(_nbVars_);
331 for (const auto& row: _database_) {
332 for (Idx i = 0; i < _nbVars_; ++i) {
333 const Idx j = _varOrder_.at(i);
334 xrow[i] = _label_(row, _bn_.variable(j), j);
335 }
336 db.insertRow(xrow);
337 }
338 } else {
339 std::vector< DBTranslatedValueType > translatorType(_nbVars_);
340 for (std::size_t i = 0; i < _nbVars_; ++i) {
341 translatorType[i] = db.translator(i).getValType();
342 }
344 const auto xmiss = gum::learning::DatabaseTable::IsMissing::False;
345 for (const auto& row: _database_) {
346 for (Idx i = 0; i < _nbVars_; ++i) {
347 const Idx j = _varOrder_.at(i);
348 if (translatorType[i] == DBTranslatedValueType::DISCRETE)
349 xrow[i].discr_val = std::size_t(row.at(j));
350 else xrow[i].cont_val = float(row.at(j));
351 }
352 }
353 db.insertRow(xrow, xmiss);
354 }
355
356 return db;
357 }
358
360 template < GUM_Numeric GUM_SCALAR >
361 std::vector< std::vector< Idx > > BNDatabaseGenerator< GUM_SCALAR >::database() const {
362 if (!_drawnSamples_) GUM_ERROR(OperationNotAllowed, "drawSamples() must be called first.")
363
364 auto db(_database_);
365 for (Idx i = 0; i < _database_.size(); ++i) {
366 for (Idx j = 0; j < _nbVars_; ++j) {
367 db.at(i).at(j) = (Idx)_database_.at(i).at(_varOrder_.at(j));
368 }
369 }
370 return db;
371 }
372
374 template < GUM_Numeric GUM_SCALAR >
376 if (varOrder.size() != _nbVars_)
377 GUM_ERROR(FatalError, "varOrder's size must be equal to the number of variables")
378
379 std::vector< bool > usedVars(_nbVars_, false);
380 for (const auto& i: varOrder) {
381 if (i >= _nbVars_) GUM_ERROR(FatalError, "varOrder contains invalid variables")
382 if (usedVars.at(i)) GUM_ERROR(FatalError, "varOrder must not have repeated variables")
383 usedVars.at(i) = true;
384 }
385
386 if (std::find(usedVars.begin(), usedVars.end(), false) != usedVars.end()) {
387 GUM_ERROR(FatalError, "varOrder must contain all variables")
388 }
389
391 }
392
394 template < GUM_Numeric GUM_SCALAR >
395 void BNDatabaseGenerator< GUM_SCALAR >::setVarOrder(const std::vector< std::string >& varOrder) {
396 std::vector< Idx > varOrderIdx;
397 varOrderIdx.reserve(varOrder.size());
398 for (const auto& vname: varOrder) {
399 varOrderIdx.push_back(_names2ids_.second(vname));
400 }
401 setVarOrder(varOrderIdx);
402 }
403
405 template < GUM_Numeric GUM_SCALAR >
407 std::string_view csvSeparator) {
408 setVarOrder(_varOrderFromCSV_(csvFileURL, csvSeparator));
409 }
410
412 template < GUM_Numeric GUM_SCALAR >
414 std::vector< Idx > varOrder;
415 varOrder.reserve(_nbVars_);
416 for (const auto& v: _bn_.topologicalOrder()) {
417 varOrder.push_back(v);
418 }
420 }
421
423 template < GUM_Numeric GUM_SCALAR >
425 std::vector< Idx > varOrder;
426 varOrder.reserve(_nbVars_);
427 for (const auto& v: _bn_.topologicalOrder()) {
428 varOrder.push_back(v);
429 }
430 std::reverse(varOrder.begin(), varOrder.end());
432 }
433
435 template < GUM_Numeric GUM_SCALAR >
437 std::vector< std::string > varOrder;
438 varOrder.reserve(_bn_.size());
439 for (const auto& var: _bn_.internalDag()) {
440 varOrder.push_back(_bn_.variable(var).name());
441 }
442 std::shuffle(varOrder.begin(), varOrder.end(), gum::randomGenerator());
444 }
445
447 template < GUM_Numeric GUM_SCALAR >
449 return _varOrder_;
450 }
451
453 template < GUM_Numeric GUM_SCALAR >
454 std::vector< std::string > BNDatabaseGenerator< GUM_SCALAR >::varOrderNames() const {
455 std::vector< std::string > varNames;
456 varNames.reserve(_nbVars_);
457 for (const auto& i: _varOrder_) {
458 varNames.push_back(_names2ids_.first(i));
459 }
460
461 return varNames;
462 }
463
465 template < GUM_Numeric GUM_SCALAR >
467 if (!_drawnSamples_) { GUM_ERROR(OperationNotAllowed, "drawSamples() must be called first.") }
468 return _log2likelihood_;
469 }
470
472 template < GUM_Numeric GUM_SCALAR >
473 std::vector< Idx >
475 std::string_view csvSeparator) const {
476 std::ifstream csvFile(std::filesystem::path{csvFileURL});
477 std::vector< Idx > varOrder;
478 if (csvFile) {
479 varOrder = _varOrderFromCSV_(csvFile, csvSeparator);
480 csvFile.close();
481 } else {
482 GUM_ERROR(NotFound, "csvFileURL does not exist")
483 }
484
485 return varOrder;
486 }
487
489 template < GUM_Numeric GUM_SCALAR >
490 std::vector< Idx >
492 std::string_view csvSeparator) const {
493 std::string line;
494 std::vector< std::string > header_found;
495 header_found.reserve(_nbVars_);
496 while (std::getline(csvFile, line)) {
497 std::size_t i = 0;
498 auto pos = line.find(csvSeparator);
499 while (pos != std::string::npos) {
500 header_found.push_back(line.substr(i, pos - i));
501 pos += csvSeparator.length();
502 i = pos;
503 pos = line.find(csvSeparator, pos);
504
505 if (pos == std::string::npos) header_found.push_back(line.substr(i, line.length()));
506 }
507 break;
508 }
509
510 std::vector< Size > varOrder;
511 varOrder.reserve(_nbVars_);
512
513 for (const auto& hf: header_found) {
514 varOrder.push_back(_names2ids_.second(hf));
515 }
516
517 return varOrder;
518 }
519
520 template < GUM_Numeric GUM_SCALAR >
521 const BayesNet< GUM_SCALAR >& BNDatabaseGenerator< GUM_SCALAR >::bn(void) {
522 return _bn_;
523 }
524
525} // namespace gum::learning
Base class for discrete random variable.
virtual double numerical(Idx indice) const =0
get a numerical representation of the indice-th value.
VarType varType() const override=0
returns the varType of variable
virtual std::string label(Idx i) const =0
get the indice-th label. This method is pure virtual.
Exception : fatal (unknown ?) error.
A base class for discretized variables, independent of the ticks type.
Class for assigning/browsing values to tuples of discrete variables.
bool end() const
Returns true if the Instantiation reached the end.
void incVar(const DiscreteVariable &v)
Operator increment for variable v only.
void add(const DiscreteVariable &v) final
Adds a new variable in the Instantiation.
bool empty() const final
Returns true if the instantiation is empty.
bool contains(const DiscreteVariable &v) const final
Indicates whether a given variable belongs to the Instantiation.
void setFirstVar(const DiscreteVariable &v)
Assign the first value in the Instantiation for var v.
Idx val(Idx i) const
Returns the current value of the variable at position i.
void setLastVar(const DiscreteVariable &v)
Assign the last value in the Instantiation for var v.
Exception: at least one argument passed to a function is not what was expected.
Exception : the element we looked for cannot be found.
Exception : operation not allowed.
Signaler< std::string_view > onStop
with a possible explanation for stopping
Signaler< Size, double > onProgress
Progression (percent) and time.
Class used to compute response times for benchmark purposes.
Definition timer.h:69
void reset()
Reset the timer.
Definition timer_inl.h:53
double step() const
Returns the delta time between now and the last reset() call (or the constructor).
Definition timer_inl.h:72
Base class for every random variable.
Definition variable.h:81
bool _drawnSamples_
whether drawSamples has been already called.
std::vector< Idx > varOrder() const
returns variable order indexes
DatabaseTable toDatabaseTable(bool useLabels=true) const
generates a DatabaseVectInRAM
std::vector< Idx > _varOrderFromCSV_(std::string_view csvFileURL, std::string_view csvSeparator=",") const
returns varOrder from a csv file
std::string _label_(const std::vector< Idx > &row, const DiscreteVariable &v, Idx i) const
return the final string for a label (taking into account the behavior for DiscretizedVariable) from a...
std::vector< std::vector< Idx > > database() const
generates database according to bn into a std::vector
void setDiscretizedLabelModeRandom()
set the behaviour of sampling for discretized variable to uniformly draw double value
double _log2likelihood_
log2Likelihood of generated samples
const BayesNet< GUM_SCALAR > & bn(void)
return const ref to the Bayes Net
BNDatabaseGenerator(const BayesNet< GUM_SCALAR > &bn)
default constructor
Size samplesNbCols() const
generate and stock database, returns log2likelihood using ProgressNotifier as notification
std::string samplesLabelAt(Idx row, Idx col) const
generate and stock database, returns log2likelihood using ProgressNotifier as notification
void setDiscretizedLabelModeInterval()
set the behaviour of sampling for discretized variable to select the label : "[min,...
Bijection< std::string, NodeId > _names2ids_
bijection nodes names
std::vector< std::vector< Idx > > _database_
generated database
const BayesNet< GUM_SCALAR > & _bn_
Bayesian network.
void setAntiTopologicalVarOrder()
set columns in antiTopoligical order
Size samplesNbRows() const
generate and stock database, returns log2likelihood using ProgressNotifier as notification
double log2likelihood() const
returns log2Likelihood of generated samples
void setTopologicalVarOrder()
set columns in topoligical order
void setDiscretizedLabelModeMedian()
set the behaviour of sampling for discretized variable to deterministic select double median of inter...
double drawSamples(Size nbSamples)
generate and stock database, returns log2likelihood using ProgressNotifier as notification
std::vector< Idx > _varOrder_
variable order in generated database
void setVarOrderFromCSV(std::string_view csvFileURL, std::string_view csvSeparator=",")
change columns order according to a csv file
std::vector< std::string > varOrderNames() const
returns variable order.
void toCSV(std::string_view csvFileURL, bool useLabels=true, bool append=false, std::string csvSeparator=",", bool checkOnAppend=false) const
generates csv representing the generated database
void setVarOrder(const std::vector< Idx > &varOrder)
change columns order
Idx samplesAt(Idx row, Idx col) const
generate and stock database, returns log2likelihood using ProgressNotifier as notification
void setRandomVarOrder()
set columns in random order
The class for storing a record in a database.
Definition DBRow.h:75
DBTranslatedValueType getValType() const
returns the type of values handled by the translator
The class representing a tabular database as used by learning tasks.
std::size_t insertTranslator(const DBTranslator &translator, const std::size_t input_column, const bool unique_column=true)
insert a new translator into the database table
const DBTranslator & translator(const std::size_t k, const bool k_is_input_col=false) const
returns either the kth translator of the database table or the first one reading the kth column of th...
void insertRow(const std::vector< std::string > &new_row) override
insert a new row at the end of the database
#define GUM_ERROR(type, msg)
Definition exceptions.h:76
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition types.h:74
Size Idx
Type for indexes.
Definition types.h:79
Size NodeId
Type for node ids.
std::mt19937 & randomGenerator()
define a random_engine with correct seed
double randomProba()
Returns a random double between 0 and 1 included (i.e.
include the inlined functions if necessary
Definition CSVParser.h:55
#define GUM_EMIT2(signal, arg1, arg2)
Definition signaler.h:290
#define GUM_EMIT1(signal, arg1)
Definition signaler.h:289
Class used to compute response times for benchmark purposes.