Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
DFTJsonParser.cpp
Go to the documentation of this file.
2
3#include <boost/algorithm/string.hpp>
4
14#include "storm/io/file.h"
16
17namespace storm::dft {
18namespace parser {
19
20template<typename ValueType>
22 std::ifstream file;
23 storm::io::openFile(filename, file);
24 Json jsonInput;
25 file >> jsonInput;
27 return parseJson(jsonInput);
28}
29
30template<typename ValueType>
32 Json jsonInput = Json::parse(jsonString);
33 return parseJson(jsonInput);
34}
35
36template<typename ValueType>
37storm::dft::storage::DFT<ValueType> DFTJsonParser<ValueType>::parseJson(Json const& jsonInput) {
38 // Initialize DFT builder and value parser
42
43 std::string currentLocation;
44 try {
45 // Try to parse parameters
46 currentLocation = "parameters";
47 if (jsonInput.count("parameters") > 0) {
48 Json parameters = jsonInput.at("parameters");
49 STORM_LOG_THROW(parameters.empty() || (std::is_same<ValueType, storm::RationalFunction>::value), storm::exceptions::NotSupportedException,
50 "Parameters are only allowed when using rational functions.");
51 for (auto const& parameter : parameters) {
52 valueParser.addParameter(parseValue(parameter));
53 }
54 }
55
56 currentLocation = "nodes";
57 Json const& nodes = jsonInput.at("nodes");
58 // Start by building mapping from ids to their unique names
59 std::map<std::string, std::string> nameMapping;
60 std::set<std::string> names;
61 for (auto const& element : nodes) {
62 Json data = element.at("data");
63 std::string id = data.at("id");
64 std::string name = parseName(data.at("name"));
65 STORM_LOG_THROW(names.find(name) == names.end(), storm::exceptions::WrongFormatException, "Element '" << name << "' was already declared.");
66 names.insert(name);
67 nameMapping[id] = name;
68 }
69
70 // Parse nodes
71 for (auto const& element : nodes) {
72 currentLocation = parseValue(element);
73 Json const& data = element.at("data");
74 std::string name = parseName(data.at("name"));
75 // TODO: use contains() if modernjson is updated
76 if (data.count("relevant") > 0) {
77 bool isRelevant = data.at("relevant");
78 if (isRelevant) {
79 relevantEvents.insert(name);
80 }
81 }
82 // Create list of children
83 std::vector<std::string> childNames;
84 // TODO: use contains() if modernjson is updated
85 if (data.count("children") > 0) {
86 for (auto const& child : data.at("children")) {
87 STORM_LOG_THROW(nameMapping.find(child) != nameMapping.end(), storm::exceptions::WrongFormatException,
88 "Child '" << child << "' for element '" << name << "' was not defined.");
89 childNames.push_back(nameMapping.at(child));
90 }
91 }
92
93 std::string type = data.at("type");
94 if (type == "and") {
95 builder.addAndGate(name, childNames);
96 } else if (type == "or") {
97 builder.addOrGate(name, childNames);
98 } else if (type == "vot") {
99 STORM_LOG_THROW(data.count("voting") > 0, storm::exceptions::WrongFormatException, "Voting gate '" << name << "' requires parameter 'voting'.");
100 std::string votThreshold = parseValue(data.at("voting"));
101 builder.addVotingGate(name, storm::parser::parseNumber<size_t>(votThreshold), childNames);
102 } else if (type == "pand") {
103 if (data.count("inclusive") > 0) {
104 bool inclusive = data.at("inclusive");
105 builder.addPandGate(name, childNames, inclusive);
106 } else {
107 builder.addPandGate(name, childNames);
108 }
109 } else if (type == "por") {
110 if (data.count("inclusive") > 0) {
111 bool inclusive = data.at("inclusive");
112 builder.addPorGate(name, childNames, inclusive);
113 } else {
114 builder.addPorGate(name, childNames);
115 }
116 } else if (type == "spare") {
117 builder.addSpareGate(name, childNames);
118 } else if (type == "seq") {
119 builder.addSequenceEnforcer(name, childNames);
120 } else if (type == "mutex") {
121 builder.addMutex(name, childNames);
122 } else if (type == "fdep") {
123 builder.addPdep(name, childNames, storm::utility::one<ValueType>());
124 } else if (type == "pdep") {
125 STORM_LOG_THROW(data.count("probability") > 0, storm::exceptions::WrongFormatException,
126 "PDEP '" << name << "' requires parameter 'probability'.");
127 ValueType probability = valueParser.parseValue(parseValue(data.at("probability")));
128 if (storm::utility::isZero<ValueType>(probability)) {
129 // Skip element. Otherwise, trying to add layout information later on will fail
130 STORM_LOG_WARN("Dependency " << name << " with probability 0 is superfluous and will not be added.");
131 continue;
132 }
133 builder.addPdep(name, childNames, probability);
134 } else if (boost::starts_with(type, "be")) {
135 parseBasicElement(name, type, data, builder, valueParser);
136 } else if (type == "compound") {
137 STORM_LOG_TRACE("Ignoring compound node '" << name << "'.");
138 } else {
139 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Type name '" << type << "' not recognized.");
140 }
141
142 if (element.find("position") != element.end()) {
143 // Set layout positions
144 Json position = element.at("position");
145 double x = position.at("x");
146 double y = position.at("y");
147 builder.addLayoutInfo(name, x / 7, y / 7);
148 }
149 }
150
151 STORM_LOG_THROW(jsonInput.count("toplevel") > 0, storm::exceptions::WrongFormatException,
152 "Top level element must be specified via parameter 'toplevel'.");
153 std::string topLevelId = parseValue(jsonInput.at("toplevel"));
154 STORM_LOG_THROW(nameMapping.find(topLevelId) != nameMapping.end(), storm::exceptions::WrongFormatException,
155 "Top level element with id '" << topLevelId << "' was not defined.");
156 builder.setTopLevel(nameMapping.at(topLevelId));
157
158 } catch (storm::exceptions::BaseException const& exception) {
159 STORM_LOG_THROW(false, storm::exceptions::FileIoException, "A parsing exception occurred in " << currentLocation << ": " << exception.what() << ".");
160 } catch (std::exception const& exception) {
161 STORM_LOG_THROW(false, storm::exceptions::FileIoException,
162 "An exception occurred during parsing in " << currentLocation << ": " << exception.what() << ".");
163 }
164
165 // Build DFT
166 storm::dft::storage::DFT<ValueType> dft = builder.build();
167 // Set relevant events
168 dft.setRelevantEvents(relevantEvents, false);
169 STORM_LOG_DEBUG("Relevant events: " << dft.getRelevantEventsString());
170 return dft;
171}
172
173template<typename ValueType>
174std::string DFTJsonParser<ValueType>::parseName(std::string const& name) {
175 std::string newName = name;
176 std::replace(newName.begin(), newName.end(), ' ', '_');
177 std::replace(newName.begin(), newName.end(), '-', '_');
178 return newName;
179}
180
181template<typename ValueType>
182std::string DFTJsonParser<ValueType>::parseValue(Json value) {
183 if (value.is_string()) {
184 return value.get<std::string>();
185 } else {
186 std::stringstream stream;
187 stream << value;
188 return stream.str();
189 }
190}
191
192template<typename ValueType>
193void DFTJsonParser<ValueType>::parseBasicElement(std::string const& name, std::string const& type, Json input,
194 storm::dft::builder::DFTBuilder<ValueType>& builder, storm::parser::ValueParser<ValueType>& valueParser) {
195 std::string distribution = "exponential"; // Default is exponential distribution
196 if (input.count("distribution") > 0) {
197 distribution = input.at("distribution");
198 // Handle short-form for exponential distribution
199 if (distribution == "exp") {
200 distribution = "exponential";
201 }
202 }
203 STORM_LOG_THROW(type == "be" || (type == "be_exp" && distribution == "exponential"), storm::exceptions::WrongFormatException,
204 "BE type '" << type << "' and distribution '" << distribution << " do not agree.");
205
206 if (distribution == "const") {
207 // Constant failed/failsafe
208 STORM_LOG_THROW(input.count("failed") > 0, storm::exceptions::WrongFormatException, "Constant BE '" << name << "' requires parameter 'failed'.");
209 bool failed = input.at("failed");
210 builder.addBasicElementConst(name, failed);
211 } else if (distribution == "probability") {
212 // Constant probability distribution
213 STORM_LOG_THROW(input.count("prob") > 0, storm::exceptions::WrongFormatException,
214 "BE '" << name << "' with probability distribution requires parameter 'prob'.");
215 ValueType probability = valueParser.parseValue(parseValue(input.at("prob")));
217 if (input.count("dorm") > 0) {
218 dormancy = valueParser.parseValue(parseValue(input.at("dorm")));
219 } else {
220 STORM_LOG_WARN("No dormancy factor was provided for basic element '" << name << "'. Assuming dormancy factor of 1.");
221 }
222 builder.addBasicElementProbability(name, probability, dormancy);
223 } else if (distribution == "exponential") {
224 // Exponential distribution
225 STORM_LOG_THROW(input.count("rate") > 0, storm::exceptions::WrongFormatException,
226 "BE '" << name << "' with exponential distribution requires parameter 'rate'.");
227 ValueType rate = valueParser.parseValue(parseValue(input.at("rate")));
229 if (input.count("dorm") > 0) {
230 dormancy = valueParser.parseValue(parseValue(input.at("dorm")));
231 } else {
232 STORM_LOG_WARN("No dormancy factor was provided for basic element '" << name << "'. Assuming dormancy factor of 1.");
233 }
234 bool transient = false;
235 if (input.count("transient") > 0) {
236 transient = input.at("transient");
237 }
238 builder.addBasicElementExponential(name, rate, dormancy, transient);
239 } else if (distribution == "erlang") {
240 // Erlang distribution
241 STORM_LOG_THROW(input.count("rate") > 0, storm::exceptions::WrongFormatException,
242 "BE '" << name << "' with Erlang distribution requires parameter 'rate'.");
243 ValueType rate = valueParser.parseValue(parseValue(input.at("rate")));
244 STORM_LOG_THROW(input.count("phases") > 0, storm::exceptions::WrongFormatException,
245 "BE '" << name << "' with Erlang distribution requires parameter 'phases'.");
246 size_t phases = storm::parser::parseNumber<size_t>(parseValue(input.at("phases")));
248 if (input.count("dorm") > 0) {
249 dormancy = valueParser.parseValue(parseValue(input.at("dorm")));
250 } else {
251 STORM_LOG_WARN("No dormancy factor was provided for basic element '" << name << "'. Assuming dormancy factor of 1.");
252 }
253 builder.addBasicElementErlang(name, rate, phases, dormancy);
254 } else if (distribution == "weibull") {
255 // Weibull distribution
256 STORM_LOG_THROW(input.count("shape") > 0, storm::exceptions::WrongFormatException,
257 "BE '" << name << "' with Weibull distribution requires parameter 'shape'.");
258 ValueType shape = valueParser.parseValue(parseValue(input.at("shape")));
259 STORM_LOG_THROW(input.count("rate") > 0, storm::exceptions::WrongFormatException,
260 "BE '" << name << "' with Weibull distribution requires parameter 'rate'.");
261 ValueType rate = valueParser.parseValue(parseValue(input.at("rate")));
262 builder.addBasicElementWeibull(name, shape, rate);
263 } else if (distribution == "lognormal") {
264 // Log-normal distribution
265 STORM_LOG_THROW(input.count("mean") > 0, storm::exceptions::WrongFormatException,
266 "BE '" << name << "' with Log-normal distribution requires parameter 'mean'.");
267 ValueType mean = valueParser.parseValue(parseValue(input.at("mean")));
268 STORM_LOG_THROW(input.count("stddev") > 0, storm::exceptions::WrongFormatException,
269 "BE '" << name << "' with Log-normal distribution requires parameter 'stddev'.");
270 ValueType stddev = valueParser.parseValue(parseValue(input.at("stddev")));
271 builder.addBasicElementLogNormal(name, mean, stddev);
272 } else {
273 STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, "Distribution " << distribution << " not known.");
274 }
275}
276
277// Explicitly instantiate the class.
278template class DFTJsonParser<double>;
280
281} // namespace parser
282} // namespace storm::dft
void addPdep(std::string const &name, std::vector< std::string > const &children, ValueType probability)
Create (probabilistic) dependency (PDEP) and add it to DFT.
void addOrGate(std::string const &name, std::vector< std::string > const &children)
Create OR-gate and add it to DFT.
void addVotingGate(std::string const &name, unsigned threshold, std::vector< std::string > const &children)
Create VOTing-gate and add it to DFT.
void addBasicElementErlang(std::string const &name, ValueType rate, unsigned phases, ValueType dormancyFactor)
Create BE with Erlang distribution and add it to DFT.
void addLayoutInfo(std::string const &name, double x, double y)
Add layout information for DFT element.
void addSpareGate(std::string const &name, std::vector< std::string > const &children)
Create SPARE-gate and add it to DFT.
void addBasicElementConst(std::string const &name, bool failed)
Create BE which is constant failed or constant failsafe and add it to DFT.
storm::dft::storage::DFT< ValueType > build()
Create DFT.
void addAndGate(std::string const &name, std::vector< std::string > const &children)
Create AND-gate and add it to DFT.
void addBasicElementProbability(std::string const &name, ValueType probability, ValueType dormancyFactor)
Create BE with constant (Bernoulli) distribution and add it to DFT.
void setTopLevel(std::string const &tle)
Set top level element.
void addBasicElementExponential(std::string const &name, ValueType rate, ValueType dormancyFactor, bool transient=false)
Create BE with exponential distribution and add it to DFT.
void addPorGate(std::string const &name, std::vector< std::string > const &children, bool inclusive=true)
Create POR-gate and add it to DFT.
void addSequenceEnforcer(std::string const &name, std::vector< std::string > const &children)
Create sequence enforcer (SEQ) and add it to DFT.
void addPandGate(std::string const &name, std::vector< std::string > const &children, bool inclusive=true)
Create PAND-gate and add it to DFT.
void addBasicElementWeibull(std::string const &name, ValueType shape, ValueType rate)
Create BE with Weibull distribution and add it to DFT.
void addBasicElementLogNormal(std::string const &name, ValueType mean, ValueType standardDeviation)
Create BE with log-normal distribution and add it to DFT.
void addMutex(std::string const &name, std::vector< std::string > const &children)
Create mutual exclusion-gate (MUTEX) and add it to DFT.
Parser for DFT in custom JSON format.
static storm::dft::storage::DFT< ValueType > parseJsonFromFile(std::string const &filename)
Parse DFT from JSON format given as file and build DFT.
static storm::dft::storage::DFT< ValueType > parseJsonFromString(std::string const &jsonString)
Parse DFT from JSON format given as a string and build DFT.
Represents a Dynamic Fault Tree.
Definition DFT.h:49
std::string getRelevantEventsString() const
Get a string containing the list of all relevant events.
Definition DFT.cpp:717
void setRelevantEvents(storm::dft::utility::RelevantEvents const &relevantEvents, bool const allowDCForRelevant) const
Set the relevance flag for all elements according to the given relevant events.
Definition DFT.cpp:691
void insert(std::string const &name)
Add relevant event.
virtual const char * what() const noexcept override
Retrieves the message associated with this exception.
Parser for values according to their ValueType.
Definition ValueParser.h:23
void addParameter(std::string const &parameter)
Add declaration of parameter.
ValueType parseValue(std::string const &value) const
Parse ValueType from string.
#define STORM_LOG_WARN(message)
Definition logging.h:28
#define STORM_LOG_DEBUG(message)
Definition logging.h:21
#define STORM_LOG_TRACE(message)
Definition logging.h:15
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:28
SFTBDDChecker::ValueType ValueType
void closeFile(std::ofstream &stream)
Close the given file after writing.
Definition file.h:47
void openFile(std::string const &filepath, std::ofstream &filestream, bool append=false, bool silent=false)
Open the given file for writing.
Definition file.h:18
ValueType parseValue(std::string const &valueStr, std::unordered_map< std::string, ValueType > const &placeholders, ValueParser< ValueType > const &valueParser)
NumberType parseNumber(std::string const &value)
Parse number from string.
bool isZero(ValueType const &a)
Definition constants.cpp:42
ValueType one()
Definition constants.cpp:19