45#ifndef DOXYGEN_SHOULD_SKIP_THIS
53# include <agrum/base/external/json/json.hpp>
54using json = nlohmann::json;
56# include <unordered_map>
59 template < GUM_Numeric GUM_SCALAR >
61 std::string_view filename,
62 bool binary) :
BNReader< GUM_SCALAR >(bn, filename) {
63 GUM_CONSTRUCTOR(GumBNReader)
65 _streamName_ = filename;
70 template < GUM_Numeric GUM_SCALAR >
71 GumBNReader< GUM_SCALAR >::GumBNReader(BayesNet< GUM_SCALAR >* bn) :
72 BNReader< GUM_SCALAR >(bn,
"") {
73 GUM_CONSTRUCTOR(GumBNReader)
80 template < GUM_Numeric GUM_SCALAR >
81 GumBNReader< GUM_SCALAR >::~GumBNReader() {
82 GUM_DESTRUCTOR(GumBNReader)
85 template < GUM_Numeric GUM_SCALAR >
86 template <
typename JsonType >
87 Size GumBNReader< GUM_SCALAR >::_proceedFromJson_(
const JsonType& content) {
90 if (content.contains(
"type") && content[
"type"].template get< std::string >() !=
"BN") {
91 addError(
"Invalid GUM file format: expected 'BN' type, got '"
92 + content[
"type"].
template get< std::string >() +
"'",
99 if (!content.contains(
"nodes") || !content.contains(
"parents") || !content.contains(
"cpt")) {
100 addError(
"Invalid GUM file format: missing 'nodes', 'parents' or 'cpt' sections",
108 BayesNet< GUM_SCALAR > tmp;
113 const auto& cptSection = content[
"cpt"];
114 std::unordered_map< std::string, const JsonType* > cptByName;
115 cptByName.reserve(cptSection.size());
116 for (
const auto& entry: cptSection.items()) {
117 cptByName.emplace(entry.key(), &entry.value());
121 for (
const auto& node: content[
"nodes"]) {
122 auto var = fastVariable< GUM_SCALAR >(node.template get< std::string >());
123 if (var->domainSize() < 2)
125 const auto& nodeName = var->name();
126 const auto cptIt = cptByName.find(nodeName);
127 if (cptIt == cptByName.end())
128 GUM_ERROR(
NotFound,
"Node '" << nodeName <<
"' has no entry in the 'cpt' section")
129 const auto& cptEntry = *(cptIt->second);
131 if (cptEntry.is_object()) {
132 if (!cptEntry.contains(
"kind"))
134 const auto kind = cptEntry.at(
"kind").template get< std::string >();
135 if (kind ==
"aggregator") {
136 if (!cptEntry.contains(
"name"))
138 const auto name = cptEntry.at(
"name").template get<
std::
string >();
139 const Idx value = cptEntry.value(
"value", Idx(1));
140 tmp._addAggregator_(name, *var, value);
141 } else if (kind ==
"ici") {
142 if (!cptEntry.contains(
"name") || !cptEntry.contains(
"externalWeight"))
144 "Missing 'name' or 'externalWeight' for ici node '" << nodeName <<
"'")
145 const auto name = cptEntry.at(
"name").template get< std::string >();
146 const auto externalWeight = cptEntry.at(
"externalWeight").template get< GUM_SCALAR >();
147 tmp._addICIModel_(name, *var, externalWeight);
156 for (
const auto& parent: content[
"parents"].items()) {
157 const auto& nodeName = parent.key();
158 for (
const auto& p: parent.value()) {
159 tmp.addArc(p.template get< std::string >(), nodeName);
163 for (
const auto& cpt: content[
"cpt"].items()) {
164 const auto& nodeName = cpt.key();
165 const auto& values = cpt.value();
166 if (values.is_object()) {
167 if (!values.contains(
"kind"))
169 const auto kind = values.at(
"kind").template get<
std::
string >();
171 const auto* ici =
dynamic_cast< const MultiDimICIModel< GUM_SCALAR >*
>(
172 tmp.cpt(nodeName).content());
175 "Node " << nodeName <<
" is tagged as an ICI model but does not hold one")
176 if (!values.contains(
"causalWeights"))
178 for (
const auto& w: values.at(
"causalWeights").items()) {
179 ici->causalWeight(tmp.variable(w.key()), w.value().template get< GUM_SCALAR >());
184 tmp.cpt(nodeName).fillWith(values.template get< std::vector< double > >());
188 if (content.contains(
"properties")) {
189 for (
const auto& prop: content[
"properties"].items()) {
190 tmp.setProperty(prop.key(), prop.value().template get< std::string >());
193 *_bn_ = std::move(tmp);
202 template < GUM_Numeric GUM_SCALAR >
203 Size GumBNReader< GUM_SCALAR >::proceed() {
204 if (_parseDone_) {
return 0; }
205 if (_streamName_.empty()) {
207 "GumBNReader was constructed without a filename: use proceedFromString() instead "
212 std::ifstream file(_streamName_, _binary_ ? std::ios::binary : std::ios::in);
213 if (!file.is_open()) {
214 addException(
"No such file " + _streamName_, _streamName_);
219 = _binary_ ? json::from_msgpack(
_readVector_(file)) : json::parse(file, nullptr, false);
221 if (content.is_discarded()) {
222 addException(
"Error parsing file", _streamName_);
225 return _proceedFromJson_(content);
226 }
catch (
const std::exception& e) {
227 addException(std::string(
"Error reading binary file: ") + e.what(), _streamName_);
232 template < GUM_Numeric GUM_SCALAR >
233 Size GumBNReader< GUM_SCALAR >::proceedFromString(std::string_view content) {
234 if (_parseDone_) {
return 0; }
235 const auto j = json::parse(content,
nullptr,
false);
236 if (j.is_discarded()) {
237 addException(
"Invalid JSON string", _streamName_);
241 return _proceedFromJson_(j);
242 }
catch (
const std::exception& e) {
243 addException(std::string(
"Error reading string: ") + e.what(), _streamName_);
248 template < GUM_Numeric GUM_SCALAR >
249 void GumBNReader< GUM_SCALAR >::showElegantErrorsAndWarnings(std::ostream& stream)
const {
250 if (_parseDone_ || count() > 0) elegantErrorsAndWarnings(stream);
254 template < GUM_Numeric GUM_SCALAR >
255 void GumBNReader< GUM_SCALAR >::showErrorCounts(std::ostream& stream)
const {
256 if (_parseDone_ || count() > 0) syntheticResults(stream);
Shared binary I/O helpers for GUM (jgum/bgum) serialization.
Pure virtual class for reading a BN from a file.
Class representing a Bayesian network.
Base class for all aGrUM's exceptions.
GUM_NODISCARD std::string errorContent() const
Returns the message content.
GumBNReader(BayesNet< GUM_SCALAR > *bn, std::string_view filename, bool binary=false)
Constructor A reader is defined for reading a defined file.
Exception : the element we looked for cannot be found.
Exception : operation not allowed.
#define GUM_ERROR(type, msg)
Abstract base class for all multi dimensionnal Causal Independency models.
gum is the global namespace for all aGrUM entities
std::vector< uint8_t > _readVector_(std::istream &is)
Reads a length-prefixed byte vector from a binary stream (bgum format).