aGrUM 3.0.0
a C++ library for (probabilistic) graphical models
GumIDReader_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
42#pragma once
43
44#include <agrum/ID/io/GUM/GumIDReader.h> // to ease IDE parser
45#ifndef DOXYGEN_SHOULD_SKIP_THIS
46
47// to ease parsing in IDE
50
51# include <agrum/base/external/json/json.hpp>
52using json = nlohmann::json;
53
54namespace gum {
55 template < GUM_Numeric GUM_SCALAR >
57 std::string_view filename,
58 bool binary) : IDReader< GUM_SCALAR >(id, filename) {
59 GUM_CONSTRUCTOR(GumIDReader)
60 _id_ = id;
61 _streamName_ = filename;
62 _parseDone_ = false;
63 _binary_ = binary;
64 }
65
66 template < GUM_Numeric GUM_SCALAR >
67 GumIDReader< GUM_SCALAR >::GumIDReader(InfluenceDiagram< GUM_SCALAR >* id) :
68 IDReader< GUM_SCALAR >(id, "") {
69 GUM_CONSTRUCTOR(GumIDReader)
70 _id_ = id;
71 _streamName_ = "";
72 _parseDone_ = false;
73 _binary_ = false;
74 }
75
76 template < GUM_Numeric GUM_SCALAR >
77 GumIDReader< GUM_SCALAR >::~GumIDReader() {
78 GUM_DESTRUCTOR(GumIDReader)
79 }
80
81 template < GUM_Numeric GUM_SCALAR >
82 template < typename JsonType >
83 Size GumIDReader< GUM_SCALAR >::_proceedFromJson_(const JsonType& content) {
84 Size nberrors = 0;
85
86 if (content.contains("type") && content["type"].template get< std::string >() != "ID") {
87 addError("Invalid GUM file format: expected 'ID' type, got '"
88 + content["type"].template get< std::string >() + "'",
89 _streamName_,
90 0,
91 0);
92 return ++nberrors;
93 }
94
95 if (!content.contains("chance") || !content.contains("utility") || !content.contains("decision")
96 || !content.contains("parents")) {
97 addError(
98 "Invalid GUM file format: missing 'chance', 'utility', 'decision' or 'parents' sections",
99 _streamName_,
100 0,
101 0);
102 return ++nberrors;
103 }
104
105 try {
106 InfluenceDiagram< GUM_SCALAR > tmp;
107
108 // add chance nodes
109 for (const auto& node: content["chance"]) {
110 tmp.addChanceNode(node.template get< std::string >());
111 }
112 // add utility nodes
113 for (const auto& node: content["utility"]) {
114 tmp.addUtilityNode(node.template get< std::string >());
115 }
116 // add decision nodes
117 for (const auto& node: content["decision"]) {
118 tmp.addDecisionNode(node.template get< std::string >());
119 }
120
121 // add arcs
122 tmp.beginTopologyTransformation();
123 for (const auto& entry: content["parents"].items()) {
124 const auto& nodeName = entry.key();
125 for (const auto& p: entry.value()) {
126 tmp.addArc(p.template get< std::string >(), nodeName);
127 }
128 }
129 tmp.endTopologyTransformation();
130
131 // fill CPTs for chance nodes
132 if (content.contains("cpt")) {
133 for (const auto& entry: content["cpt"].items()) {
134 tmp.cpt(entry.key()).fillWith(entry.value().template get< std::vector< double > >());
135 }
136 }
137
138 // fill utility tables
139 if (content.contains("reward")) {
140 for (const auto& entry: content["reward"].items()) {
141 tmp.utility(entry.key()).fillWith(entry.value().template get< std::vector< double > >());
142 }
143 }
144
145 // properties
146 if (content.contains("properties")) {
147 for (const auto& prop: content["properties"].items()) {
148 tmp.setProperty(prop.key(), prop.value().template get< std::string >());
149 }
150 }
151
152 *_id_ = std::move(tmp);
153 _parseDone_ = true;
154 } catch (const gum::Exception& e) {
155 addError(e.errorContent(), _streamName_, 0, 0);
156 return ++nberrors;
157 }
158 return nberrors;
159 }
160
161 template < GUM_Numeric GUM_SCALAR >
162 Size GumIDReader< GUM_SCALAR >::proceed() {
163 if (_parseDone_) { return 0; }
164 if (_streamName_.empty()) {
166 "GumIDReader was constructed without a filename: use proceedFromString() instead "
167 "of proceed()")
168 }
169 Size nberrors = 0;
170
171 std::ifstream file(_streamName_, _binary_ ? std::ios::binary : std::ios::in);
172 if (!file.is_open()) {
173 addException("No such file " + _streamName_, _streamName_);
174 return ++nberrors;
175 }
176 try {
177 const auto content
178 = _binary_ ? json::from_msgpack(_readVector_(file)) : json::parse(file, nullptr, false);
179 file.close();
180 if (content.is_discarded()) {
181 addException("Error parsing file", _streamName_);
182 return ++nberrors;
183 }
184 return _proceedFromJson_(content);
185 } catch (const std::exception& e) {
186 addException(std::string("Error reading binary file: ") + e.what(), _streamName_);
187 return ++nberrors;
188 }
189 }
190
191 template < GUM_Numeric GUM_SCALAR >
192 Size GumIDReader< GUM_SCALAR >::proceedFromString(std::string_view content) {
193 if (_parseDone_) { return 0; }
194 const auto j = json::parse(content, nullptr, false);
195 if (j.is_discarded()) {
196 addException("Invalid JSON string", _streamName_);
197 return 1;
198 }
199 return _proceedFromJson_(j);
200 }
201
202 template < GUM_Numeric GUM_SCALAR >
203 void GumIDReader< GUM_SCALAR >::showElegantErrorsAndWarnings(std::ostream& stream) const {
204 if (_parseDone_ || count() > 0) {
205 elegantErrorsAndWarnings(stream);
206 } else {
207 GUM_ERROR(OperationNotAllowed, "File not parsed yet")
208 }
209 }
210
211 template < GUM_Numeric GUM_SCALAR >
212 void GumIDReader< GUM_SCALAR >::showErrorCounts(std::ostream& stream) const {
213 if (_parseDone_ || count() > 0) {
214 syntheticResults(stream);
215 } else {
216 GUM_ERROR(OperationNotAllowed, "File not parsed yet")
217 }
218 }
219} // namespace gum
220
221#endif // DOXYGEN_SHOULD_SKIP_THIS
Shared binary I/O helpers for GUM (jgum/bgum) serialization.
Definition of class for reading an InfluenceDiagram from a GUM (json) file.
Base class for all aGrUM's exceptions.
Definition exceptions.h:122
GUM_NODISCARD std::string errorContent() const
Returns the message content.
GumIDReader(InfluenceDiagram< GUM_SCALAR > *id, std::string_view filename, bool binary=false)
Constructor.
Pure virtual class for importing an ID from a file.
Definition IDReader.h:76
Class representing an Influence Diagram.
Exception : operation not allowed.
#define GUM_ERROR(type, msg)
Definition exceptions.h:76
gum is the global namespace for all aGrUM entities
Definition agrum.h:46
std::vector< uint8_t > _readVector_(std::istream &is)
Reads a length-prefixed byte vector from a binary stream (bgum format).