Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
JaniParser.cpp
Go to the documentation of this file.
1#include "JaniParser.h"
2
16
22
26
31
33
34#include <algorithm> // std::iter_swap
35#include <boost/lexical_cast.hpp>
36#include <fstream>
37#include <iostream>
38#include <optional>
39#include <sstream>
40
41#include "storm/io/file.h"
43
44namespace storm {
45namespace parser {
46
48// Defaults
50template<typename ValueType>
51const bool JaniParser<ValueType>::defaultVariableTransient = false;
52const std::string VARIABLE_AUTOMATON_DELIMITER = "_";
53template<typename ValueType>
54const std::set<std::string> JaniParser<ValueType>::unsupportedOpstrings({"tan", "cot", "sec", "csc", "asin", "acos", "atan",
55 "acot", "asec", "acsc", "sinh", "cosh", "tanh", "coth",
56 "sech", "csch", "asinh", "acosh", "atanh", "asinh", "acosh"});
57
58template<typename ValueType>
59std::string getString(typename JaniParser<ValueType>::Json const& structure, std::string const& errorInfo) {
60 STORM_LOG_THROW(structure.is_string(), storm::exceptions::InvalidJaniException,
61 "Expected a string in " << errorInfo << ", got '" << structure.dump() << "'.");
62 return structure.front();
63}
64
65template<typename ValueType>
66bool getBoolean(typename JaniParser<ValueType>::Json const& structure, std::string const& errorInfo) {
67 STORM_LOG_THROW(structure.is_boolean(), storm::exceptions::InvalidJaniException,
68 "Expected a Boolean in " << errorInfo << ", got " << structure.dump() << "'.");
69 return structure.front();
70}
71
72template<typename ValueType>
73uint64_t getUnsignedInt(typename JaniParser<ValueType>::Json const& structure, std::string const& errorInfo) {
74 STORM_LOG_THROW(structure.is_number(), storm::exceptions::InvalidJaniException,
75 "Expected a number in " << errorInfo << ", got '" << structure.dump() << "'.");
76 int64_t num = structure.front();
77 STORM_LOG_THROW(num >= 0, storm::exceptions::InvalidJaniException, "Expected a positive number in " << errorInfo << ", got '" << num << "'.");
78 return static_cast<uint64_t>(num);
79}
80
81template<typename ValueType>
82int64_t getSignedInt(typename JaniParser<ValueType>::Json const& structure, std::string const& errorInfo) {
83 STORM_LOG_THROW(structure.is_number(), storm::exceptions::InvalidJaniException,
84 "Expected a number in " << errorInfo << ", got '" << structure.dump() << "'.");
85 return structure.front();
86}
87
88template<typename ValueType>
89std::pair<storm::jani::Model, std::vector<storm::jani::Property>> JaniParser<ValueType>::parse(std::string const& path, bool parseProperties) {
91 parser.readFile(path);
92 return parser.parseModel(parseProperties);
93}
94
95template<typename ValueType>
96std::pair<storm::jani::Model, std::vector<storm::jani::Property>> JaniParser<ValueType>::parseFromString(std::string const& jsonstring, bool parseProperties) {
97 JaniParser parser(jsonstring);
98 return parser.parseModel(parseProperties);
99}
100
101template<typename ValueType>
102JaniParser<ValueType>::JaniParser(std::string const& jsonstring) : expressionManager(new storm::expressions::ExpressionManager()) {
103 parsedStructure = Json::parse(jsonstring);
104}
105
106template<typename ValueType>
107void JaniParser<ValueType>::readFile(std::string const& path) {
108 std::ifstream file;
109 storm::io::openFile(path, file);
110 file >> parsedStructure;
112}
113
114template<typename ValueType>
115std::pair<storm::jani::Model, std::vector<storm::jani::Property>> JaniParser<ValueType>::parseModel(bool parseProperties) {
116 // jani-version
117 STORM_LOG_THROW(parsedStructure.count("jani-version") == 1, storm::exceptions::InvalidJaniException, "Jani-version must be given exactly once.");
118 uint64_t version = getUnsignedInt<ValueType>(parsedStructure.at("jani-version"), "jani version");
119 STORM_LOG_WARN_COND(version >= 1 && version <= 1, "JANI Version " << version << " is not supported. Results may be wrong.");
120 // name
121 STORM_LOG_THROW(parsedStructure.count("name") == 1, storm::exceptions::InvalidJaniException, "A model must have a (single) name.");
122 std::string name = getString<ValueType>(parsedStructure.at("name"), "model name");
123 // model type
124 STORM_LOG_THROW(parsedStructure.count("type") == 1, storm::exceptions::InvalidJaniException, "A type must be given exactly once.");
125 std::string modeltypestring = getString<ValueType>(parsedStructure.at("type"), "type of the model");
127 STORM_LOG_THROW(type != storm::jani::ModelType::UNDEFINED, storm::exceptions::InvalidJaniException, "Model type " + modeltypestring + " not recognized.");
128 storm::jani::Model model(name, type, version, expressionManager);
129 uint_fast64_t featuresCount = parsedStructure.count("features");
130 STORM_LOG_THROW(featuresCount < 2, storm::exceptions::InvalidJaniException, "Features-declarations can be given at most once.");
131 if (featuresCount == 1) {
132 const auto allKnownModelFeatures = storm::jani::getAllKnownModelFeatures();
133 for (auto const& feature : parsedStructure.at("features")) {
134 std::string featureStr = getString<ValueType>(feature, "Model feature");
135 bool found = false;
136 for (auto const& knownFeature : allKnownModelFeatures.asSet()) {
137 if (featureStr == storm::jani::toString(knownFeature)) {
138 model.getModelFeatures().add(knownFeature);
139 found = true;
140 break;
141 }
142 }
143 STORM_LOG_THROW(found, storm::exceptions::NotSupportedException, "Storm does not support the model feature " << featureStr << ".");
144 }
145 }
146 uint_fast64_t actionCount = parsedStructure.count("actions");
147 STORM_LOG_THROW(actionCount < 2, storm::exceptions::InvalidJaniException, "Action-declarations can be given at most once.");
148 if (actionCount > 0) {
149 parseActions(parsedStructure.at("actions"), model);
150 }
151
152 Scope scope(name);
153
154 // Parse constants
155 ConstantsMap constants;
156 scope.constants = &constants;
157 uint_fast64_t constantsCount = parsedStructure.count("constants");
158 STORM_LOG_THROW(constantsCount < 2, storm::exceptions::InvalidJaniException, "Constant-declarations can be given at most once.");
159 if (constantsCount == 1) {
160 // Reserve enough space to make sure that pointers to constants remain valid after adding new ones.
161 model.getConstants().reserve(parsedStructure.at("constants").size());
162 for (auto const& constStructure : parsedStructure.at("constants")) {
163 std::shared_ptr<storm::jani::Constant> constant =
164 parseConstant(constStructure, scope.refine("constants[" + std::to_string(constants.size()) + "]"));
165 model.addConstant(*constant);
166 STORM_LOG_ASSERT(model.getConstants().back().getName() == constant->getName(), "Constant name mismatch.");
167 constants.emplace(constant->getName(), &model.getConstants().back());
168 }
169 }
170
171 // Parse variables
172 uint_fast64_t variablesCount = parsedStructure.count("variables");
173 STORM_LOG_THROW(variablesCount < 2, storm::exceptions::InvalidJaniException, "Variable-declarations can be given at most once for global variables.");
174 VariablesMap globalVars;
175 scope.globalVars = &globalVars;
176 if (variablesCount == 1) {
177 for (auto const& varStructure : parsedStructure.at("variables")) {
178 std::shared_ptr<storm::jani::Variable> variable = parseVariable(varStructure, scope.refine("variables[" + std::to_string(globalVars.size())));
179 globalVars.emplace(variable->getName(), &model.addVariable(*variable));
180 }
181 }
182
183 uint64_t funDeclCount = parsedStructure.count("functions");
184 STORM_LOG_THROW(funDeclCount < 2, storm::exceptions::InvalidJaniException, "Model '" << name << "' has more than one list of functions.");
185 FunctionsMap globalFuns;
186 scope.globalFunctions = &globalFuns;
187 if (funDeclCount > 0) {
188 // We require two passes through the function definitions array to allow referring to functions before they were defined.
189 std::vector<storm::jani::FunctionDefinition> dummyFunctionDefinitions;
190 for (auto const& funStructure : parsedStructure.at("functions")) {
191 // Skip parsing of function body
192 dummyFunctionDefinitions.push_back(
193 parseFunctionDefinition(funStructure, scope.refine("functions[" + std::to_string(globalFuns.size()) + "] of model " + name), true));
194 }
195 // Store references to the dummy function definitions. This needs to happen in a separate loop since otherwise, references to FunDefs can be invalidated
196 // after calling dummyFunctionDefinitions.push_back
197 for (auto const& funDef : dummyFunctionDefinitions) {
198 bool unused = globalFuns.emplace(funDef.getName(), &funDef).second;
199 STORM_LOG_THROW(unused, storm::exceptions::InvalidJaniException,
200 "Multiple definitions of functions with the name " << funDef.getName() << " in " << scope.description << ".");
201 }
202 for (auto const& funStructure : parsedStructure.at("functions")) {
203 // Actually parse the function body
205 parseFunctionDefinition(funStructure, scope.refine("functions[" + std::to_string(globalFuns.size()) + "] of model " + name), false);
206 STORM_LOG_ASSERT(globalFuns.count(funDef.getName()) == 1, "Global function not found.");
207 globalFuns[funDef.getName()] = &model.addFunctionDefinition(funDef);
208 }
209 }
210
211 // Parse Automata
212 STORM_LOG_THROW(parsedStructure.count("automata") == 1, storm::exceptions::InvalidJaniException, "Exactly one list of automata must be given.");
213 STORM_LOG_THROW(parsedStructure.at("automata").is_array(), storm::exceptions::InvalidJaniException, "Automata must be an array.");
214 // Automatons can only be parsed after constants and variables.
215 for (auto const& automataEntry : parsedStructure.at("automata")) {
216 model.addAutomaton(parseAutomaton(automataEntry, model, scope.refine("automata[" + std::to_string(model.getNumberOfAutomata()) + "]")));
217 }
218 STORM_LOG_THROW(parsedStructure.count("restrict-initial") < 2, storm::exceptions::InvalidJaniException, "Model has multiple initial value restrictions.");
219 storm::expressions::Expression initialValueRestriction = expressionManager->boolean(true);
220 if (parsedStructure.count("restrict-initial") > 0) {
221 STORM_LOG_THROW(parsedStructure.at("restrict-initial").count("exp") == 1, storm::exceptions::InvalidJaniException,
222 "Model needs an expression inside the initial restricion.");
223 initialValueRestriction = parseExpression(parsedStructure.at("restrict-initial").at("exp"), scope.refine("Initial value restriction"));
224 }
225 model.setInitialStatesRestriction(initialValueRestriction);
226 STORM_LOG_THROW(parsedStructure.count("system") == 1, storm::exceptions::InvalidJaniException, "Exactly one system description must be given.");
227 std::shared_ptr<storm::jani::Composition> composition = parseComposition(parsedStructure.at("system"));
228 model.setSystemComposition(composition);
229 model.finalize();
230
231 // Parse properties
233 STORM_LOG_THROW(parsedStructure.count("properties") <= 1, storm::exceptions::InvalidJaniException, "At most one list of properties can be given.");
234 std::vector<storm::jani::Property> properties;
235 if (parseProperties && parsedStructure.count("properties") == 1) {
236 STORM_LOG_THROW(parsedStructure.at("properties").is_array(), storm::exceptions::InvalidJaniException, "Properties should be an array.");
237 for (auto const& propertyEntry : parsedStructure.at("properties")) {
238 try {
239 auto prop = this->parseProperty(model, propertyEntry, scope.refine("property[" + std::to_string(properties.size()) + "]"));
240 // Eliminate reward accumulations as much as possible
241 rewAccEliminator.eliminateRewardAccumulations(prop);
242 properties.push_back(prop);
243 } catch (storm::exceptions::NotSupportedException const& ex) {
244 STORM_LOG_WARN("Cannot handle property: " << ex.what());
245 } catch (storm::exceptions::NotImplementedException const& ex) {
246 STORM_LOG_WARN("Cannot handle property: " << ex.what());
247 }
248 }
249 }
250 return {model, properties};
251}
252
253template<typename ValueType>
254std::vector<std::shared_ptr<storm::logic::Formula const>> JaniParser<ValueType>::parseUnaryFormulaArgument(storm::jani::Model& model,
255 Json const& propertyStructure,
256 storm::logic::FormulaContext formulaContext,
257 std::string const& opstring, Scope const& scope) {
258 STORM_LOG_THROW(propertyStructure.count("exp") == 1, storm::exceptions::InvalidJaniException,
259 "Expecting operand for operator " << opstring << " in " << scope.description << ".");
260 return {parseFormula(model, propertyStructure.at("exp"), formulaContext, scope.refine("Operand of operator " + opstring))};
261}
262
263template<typename ValueType>
264std::vector<std::shared_ptr<storm::logic::Formula const>> JaniParser<ValueType>::parseBinaryFormulaArguments(storm::jani::Model& model,
265 Json const& propertyStructure,
266 storm::logic::FormulaContext formulaContext,
267 std::string const& opstring, Scope const& scope) {
268 STORM_LOG_THROW(propertyStructure.count("left") == 1, storm::exceptions::InvalidJaniException,
269 "Expecting left operand for operator " << opstring << " in " << scope.description << ".");
270 STORM_LOG_THROW(propertyStructure.count("right") == 1, storm::exceptions::InvalidJaniException,
271 "Expecting right operand for operator " << opstring << " in " << scope.description << ".");
272 return {parseFormula(model, propertyStructure.at("left"), formulaContext, scope.refine("Operand of operator " + opstring)),
273 parseFormula(model, propertyStructure.at("right"), formulaContext, scope.refine("Operand of operator " + opstring))};
274}
275
276template<typename ValueType>
277storm::jani::PropertyInterval JaniParser<ValueType>::parsePropertyInterval(Json const& piStructure, Scope const& scope) {
278 storm::jani::PropertyInterval pi;
279 if (piStructure.count("lower") > 0) {
280 pi.lowerBound = parseExpression(piStructure.at("lower"), scope.refine("Lower bound for property interval"));
281 }
282 if (piStructure.count("lower-exclusive") > 0) {
283 STORM_LOG_THROW(pi.lowerBound.isInitialized(), storm::exceptions::InvalidJaniException, "Lower-exclusive can only be set if a lower bound is present.");
284 pi.lowerBoundStrict = piStructure.at("lower-exclusive");
285 }
286 if (piStructure.count("upper") > 0) {
287 pi.upperBound = parseExpression(piStructure.at("upper"), scope.refine("Upper bound for property interval"));
288 }
289 if (piStructure.count("upper-exclusive") > 0) {
290 STORM_LOG_THROW(pi.upperBound.isInitialized(), storm::exceptions::InvalidJaniException, "Lower-exclusive can only be set if a lower bound is present.");
291 pi.upperBoundStrict = piStructure.at("upper-exclusive");
292 }
293 STORM_LOG_THROW(pi.lowerBound.isInitialized() || pi.upperBound.isInitialized(), storm::exceptions::InvalidJaniException,
294 "Bounded operator must have a bounded interval, but no bounds found in '" << piStructure << "'.");
295 return pi;
296}
297
298template<typename ValueType>
299storm::logic::RewardAccumulation JaniParser<ValueType>::parseRewardAccumulation(Json const& accStructure, std::string const& context) {
300 bool accTime = false;
301 bool accSteps = false;
302 bool accExit = false;
303 STORM_LOG_THROW(accStructure.is_array(), storm::exceptions::InvalidJaniException, "Accumulate should be an array.");
304 for (auto const& accEntry : accStructure) {
305 if (accEntry == "steps") {
306 accSteps = true;
307 } else if (accEntry == "time") {
308 accTime = true;
309 } else if (accEntry == "exit") {
310 accExit = true;
311 } else {
312 STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException,
313 "One may only accumulate either 'steps' or 'time' or 'exit', got " << accEntry.dump() << " in " << context << ".");
314 }
315 }
316 return storm::logic::RewardAccumulation(accSteps, accTime, accExit);
317}
318
319void insertLowerUpperTimeBounds(std::vector<std::optional<storm::logic::TimeBound>>& lowerBounds,
320 std::vector<std::optional<storm::logic::TimeBound>>& upperBounds, storm::jani::PropertyInterval const& pi) {
321 if (pi.hasLowerBound()) {
322 lowerBounds.push_back(storm::logic::TimeBound(pi.lowerBoundStrict, pi.lowerBound));
323 } else {
324 lowerBounds.push_back(std::nullopt);
325 }
326 if (pi.hasUpperBound()) {
327 upperBounds.push_back(storm::logic::TimeBound(pi.upperBoundStrict, pi.upperBound));
328 } else {
329 upperBounds.push_back(std::nullopt);
330 }
331}
332
333std::shared_ptr<storm::logic::Formula const> makeBinaryBooleanFormula(storm::logic::BinaryBooleanOperatorType oper,
334 std::shared_ptr<storm::logic::Formula const> const& left,
335 std::shared_ptr<storm::logic::Formula const> const& right,
337 if (left->isPathFormula() || right->isPathFormula()) {
338 return std::make_shared<storm::logic::BinaryBooleanPathFormula const>(oper, left, right, context);
339 }
340 return std::make_shared<storm::logic::BinaryBooleanStateFormula const>(oper, left, right);
341}
342
343std::shared_ptr<storm::logic::Formula const> makeUnaryBooleanFormula(storm::logic::UnaryBooleanOperatorType oper,
344 std::shared_ptr<storm::logic::Formula const> const& sub,
346 if (sub->isPathFormula()) {
347 return std::make_shared<storm::logic::UnaryBooleanPathFormula const>(oper, sub, context);
348 }
349 return std::make_shared<storm::logic::UnaryBooleanStateFormula const>(oper, sub);
350}
351
352template<typename ValueType>
353std::shared_ptr<storm::logic::Formula const> JaniParser<ValueType>::parseFormula(storm::jani::Model& model, Json const& propertyStructure,
354 storm::logic::FormulaContext formulaContext, Scope const& scope,
355 boost::optional<storm::logic::Bound> bound) {
356 if (propertyStructure.is_boolean()) {
357 return std::make_shared<storm::logic::BooleanLiteralFormula>(propertyStructure.template get<bool>());
358 }
359 if (propertyStructure.is_string()) {
360 if (labels.count(propertyStructure.template get<std::string>()) > 0) {
361 return std::make_shared<storm::logic::AtomicLabelFormula>(propertyStructure.template get<std::string>());
362 }
363 }
364 storm::expressions::Expression expr = parseExpression(propertyStructure, scope.refine("expression in property"), true);
365 if (expr.isInitialized()) {
366 bool exprContainsLabel = false;
367 auto varsInExpr = expr.getVariables();
368 for (auto const& varInExpr : varsInExpr) {
369 if (labels.count(varInExpr.getName()) > 0) {
370 exprContainsLabel = true;
371 break;
372 }
373 }
374 if (!exprContainsLabel) {
375 STORM_LOG_ASSERT(bound == boost::none, "Unexpected bound for atomic expression formula.");
376 return std::make_shared<storm::logic::AtomicExpressionFormula>(expr);
377 }
378 }
379 if (propertyStructure.count("op") == 1) {
380 std::string opString = getString<ValueType>(propertyStructure.at("op"), "Operation description");
381
382 if (opString == "Pmin" || opString == "Pmax") {
383 std::vector<std::shared_ptr<storm::logic::Formula const>> args =
384 parseUnaryFormulaArgument(model, propertyStructure, storm::logic::FormulaContext::Probability, opString, scope);
385 STORM_LOG_ASSERT(args.size() == 1, "Expected one argument for probability operator.");
386 storm::logic::OperatorInformation opInfo;
387 opInfo.optimalityType = opString == "Pmin" ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize;
388 opInfo.bound = bound;
389 return std::make_shared<storm::logic::ProbabilityOperatorFormula>(args[0], opInfo);
390
391 } else if (opString == "∀" || opString == "∃") {
392 STORM_LOG_ASSERT(bound == boost::none, "Unexpected bound for forall/exists.");
393 STORM_LOG_THROW_UNCONDITIONALLY(storm::exceptions::NotImplementedException,
394 "Forall and Exists are currently not supported in " << scope.description << ".");
395 } else if (opString == "Emin" || opString == "Emax") {
396 STORM_LOG_WARN_COND(model.getJaniVersion() == 1, "Model not compliant: Contains Emin/Emax property in " << scope.description << ".");
397 STORM_LOG_THROW(propertyStructure.count("exp") == 1, storm::exceptions::InvalidJaniException,
398 "Expecting reward-expression for operator " << opString << " in " << scope.description << ".");
399 storm::expressions::Expression rewExpr = parseExpression(propertyStructure.at("exp"), scope.refine("Reward expression"));
400 STORM_LOG_THROW(rewExpr.hasNumericalType(), storm::exceptions::InvalidJaniException,
401 "Reward expression '" << rewExpr << "' does not have numerical type in " << scope.description << ".");
402 std::string rewardName = rewExpr.toString();
403
404 storm::logic::OperatorInformation opInfo;
405 opInfo.optimalityType = opString == "Emin" ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize;
406 opInfo.bound = bound;
407
408 storm::logic::RewardAccumulation rewardAccumulation(false, false, false);
409 if (propertyStructure.count("accumulate") > 0) {
410 rewardAccumulation = parseRewardAccumulation(propertyStructure.at("accumulate"), scope.description);
411 }
412
413 bool time = false;
414 if (propertyStructure.count("step-instant") > 0) {
415 STORM_LOG_THROW(propertyStructure.count("time-instant") == 0, storm::exceptions::NotSupportedException,
416 "Storm does not support to have a step-instant and a time-instant in " + scope.description + ".");
417 STORM_LOG_THROW(propertyStructure.count("reward-instants") == 0, storm::exceptions::NotSupportedException,
418 "Storm does not support to have a step-instant and a reward-instant in " + scope.description + ".");
419
420 storm::expressions::Expression stepInstantExpr = parseExpression(propertyStructure.at("step-instant"), scope.refine("Step instant"));
421 if (!rewExpr.isVariable()) {
422 model.addNonTrivialRewardExpression(rewardName, rewExpr);
423 }
424 if (rewardAccumulation.isEmpty()) {
425 return std::make_shared<storm::logic::RewardOperatorFormula>(
426 std::make_shared<storm::logic::InstantaneousRewardFormula>(stepInstantExpr, storm::logic::TimeBoundType::Steps), rewardName, opInfo);
427 } else {
428 return std::make_shared<storm::logic::RewardOperatorFormula>(
429 std::make_shared<storm::logic::CumulativeRewardFormula>(storm::logic::TimeBound(false, stepInstantExpr),
430 storm::logic::TimeBoundReference(storm::logic::TimeBoundType::Steps),
431 rewardAccumulation),
432 rewardName, opInfo);
433 }
434 } else if (propertyStructure.count("time-instant") > 0) {
435 STORM_LOG_THROW(propertyStructure.count("reward-instants") == 0, storm::exceptions::NotSupportedException,
436 "Storm does not support to have a time-instant and a reward-instant in " + scope.description + ".");
437 storm::expressions::Expression timeInstantExpr = parseExpression(propertyStructure.at("time-instant"), scope.refine("time instant"));
438 if (!rewExpr.isVariable()) {
439 model.addNonTrivialRewardExpression(rewardName, rewExpr);
440 }
441 if (rewardAccumulation.isEmpty()) {
442 return std::make_shared<storm::logic::RewardOperatorFormula>(
443 std::make_shared<storm::logic::InstantaneousRewardFormula>(timeInstantExpr, storm::logic::TimeBoundType::Time), rewardName, opInfo);
444 } else {
445 return std::make_shared<storm::logic::RewardOperatorFormula>(
446 std::make_shared<storm::logic::CumulativeRewardFormula>(storm::logic::TimeBound(false, timeInstantExpr),
447 storm::logic::TimeBoundReference(storm::logic::TimeBoundType::Time),
448 rewardAccumulation),
449 rewardName, opInfo);
450 }
451 } else if (propertyStructure.count("reward-instants") > 0) {
452 std::vector<storm::logic::TimeBound> bounds;
453 std::vector<storm::logic::TimeBoundReference> boundReferences;
454 for (auto const& rewInst : propertyStructure.at("reward-instants")) {
455 storm::expressions::Expression rewInstRewardModelExpression =
456 parseExpression(rewInst.at("exp"), scope.refine("Reward expression at reward instant"));
457 STORM_LOG_THROW(rewInstRewardModelExpression.hasNumericalType(), storm::exceptions::InvalidJaniException,
458 "Reward expression '" << rewInstRewardModelExpression << "' does not have numerical type in " << scope.description << ".");
459 storm::logic::RewardAccumulation boundRewardAccumulation = parseRewardAccumulation(rewInst.at("accumulate"), scope.description);
460 bool steps = (boundRewardAccumulation.isStepsSet() || boundRewardAccumulation.isExitSet()) && boundRewardAccumulation.size() == 1;
461 bool time = boundRewardAccumulation.isTimeSet() && boundRewardAccumulation.size() == 1 && !model.isDiscreteTimeModel();
462 if ((steps || time) && !rewInstRewardModelExpression.containsVariables() &&
463 storm::utility::isOne(rewInstRewardModelExpression.evaluateAsRational())) {
464 boundReferences.emplace_back(steps ? storm::logic::TimeBoundType::Steps : storm::logic::TimeBoundType::Time);
465 } else {
466 std::string rewInstRewardModelName = rewInstRewardModelExpression.toString();
467 if (!rewInstRewardModelExpression.isVariable()) {
468 model.addNonTrivialRewardExpression(rewInstRewardModelName, rewInstRewardModelExpression);
469 }
470 boundReferences.emplace_back(rewInstRewardModelName, boundRewardAccumulation);
471 }
472 storm::expressions::Expression rewInstantExpr = parseExpression(rewInst.at("instant"), scope.refine("reward instant"));
473 bounds.emplace_back(false, rewInstantExpr);
474 }
475 if (!rewExpr.isVariable()) {
476 model.addNonTrivialRewardExpression(rewardName, rewExpr);
477 }
478 return std::make_shared<storm::logic::RewardOperatorFormula>(
479 std::make_shared<storm::logic::CumulativeRewardFormula>(bounds, boundReferences, rewardAccumulation), rewardName, opInfo);
480 } else {
481 time = !rewExpr.containsVariables() && storm::utility::isOne(rewExpr.evaluateAsRational());
482 std::shared_ptr<storm::logic::Formula const> subformula;
483 if (propertyStructure.count("reach") > 0) {
485 subformula = std::make_shared<storm::logic::EventuallyFormula>(
486 parseFormula(model, propertyStructure.at("reach"), formulaContext, scope.refine("Reach-expression of operator " + opString)),
487 formulaContext, rewardAccumulation);
488 } else {
489 subformula = std::make_shared<storm::logic::TotalRewardFormula>(rewardAccumulation);
490 }
491 if (time) {
492 STORM_LOG_ASSERT(subformula->isTotalRewardFormula() || subformula->isTimePathFormula(), "Expected total reward or time path formula.");
493 return std::make_shared<storm::logic::TimeOperatorFormula>(subformula, opInfo);
494 } else {
495 if (!rewExpr.isVariable()) {
496 model.addNonTrivialRewardExpression(rewardName, rewExpr);
497 }
498 return std::make_shared<storm::logic::RewardOperatorFormula>(subformula, rewardName, opInfo);
499 }
500 }
501 } else if (opString == "Smin" || opString == "Smax") {
502 storm::logic::OperatorInformation opInfo;
503 opInfo.optimalityType = opString == "Smin" ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize;
504 opInfo.bound = bound;
505 // Reward accumulation is optional as it was not available in the early days...
506 boost::optional<storm::logic::RewardAccumulation> rewardAccumulation;
507 if (propertyStructure.count("accumulate") > 0) {
508 STORM_LOG_WARN_COND(model.getJaniVersion() == 1, "Unexpected accumulate field in " << scope.description << ".");
509 rewardAccumulation = parseRewardAccumulation(propertyStructure.at("accumulate"), scope.description);
510 }
511 STORM_LOG_THROW(propertyStructure.count("exp") > 0, storm::exceptions::InvalidJaniException,
512 "Expected an expression at steady state property at " << scope.description << ".");
513 auto exp = parseExpression(propertyStructure["exp"], scope.refine("steady-state operator"), true);
514 if (!exp.isInitialized() || exp.hasBooleanType()) {
515 STORM_LOG_THROW(!rewardAccumulation.is_initialized(), storm::exceptions::InvalidJaniException,
516 "Long-run average probabilities are not allowed to have a reward accumulation at" << scope.description << ".");
517 std::shared_ptr<storm::logic::Formula const> subformula =
518 parseUnaryFormulaArgument(model, propertyStructure, formulaContext, opString, scope.refine("Steady-state operator"))[0];
519 return std::make_shared<storm::logic::LongRunAverageOperatorFormula>(subformula, opInfo);
520 }
521 STORM_LOG_THROW(exp.hasNumericalType(), storm::exceptions::InvalidJaniException,
522 "Reward expression '" << exp << "' does not have numerical type in " << scope.description << ".");
523 std::string rewardName = exp.toString();
524 if (!exp.isVariable()) {
525 model.addNonTrivialRewardExpression(rewardName, exp);
526 }
527 auto subformula = std::make_shared<storm::logic::LongRunAverageRewardFormula>(rewardAccumulation);
528 return std::make_shared<storm::logic::RewardOperatorFormula>(subformula, rewardName, opInfo);
529
530 } else if (opString == "U" || opString == "F") {
531 STORM_LOG_ASSERT(bound == boost::none, "Unexpected bound for until/eventually.");
532 std::vector<std::shared_ptr<storm::logic::Formula const>> args;
533 if (opString == "U") {
534 args = parseBinaryFormulaArguments(model, propertyStructure, formulaContext, opString, scope);
535 } else {
536 STORM_LOG_ASSERT(opString == "F", "Expected F operator.");
537 args = parseUnaryFormulaArgument(model, propertyStructure, formulaContext, opString, scope);
538 args.push_back(args[0]);
540 }
541
542 std::vector<std::optional<storm::logic::TimeBound>> lowerBounds, upperBounds;
543 std::vector<storm::logic::TimeBoundReference> tbReferences;
544 if (propertyStructure.count("step-bounds") > 0) {
545 STORM_LOG_WARN_COND(model.getJaniVersion() == 1, "Jani model not compliant: Contains step-bounds in " << scope.description << ".");
546 storm::jani::PropertyInterval pi =
547 parsePropertyInterval(propertyStructure.at("step-bounds"), scope.refine("step-bounded until").clearVariables());
548 insertLowerUpperTimeBounds(lowerBounds, upperBounds, pi);
549 tbReferences.emplace_back(storm::logic::TimeBoundType::Steps);
550 }
551 if (propertyStructure.count("time-bounds") > 0) {
552 STORM_LOG_WARN_COND(model.getJaniVersion() == 1, "Jani model not compliant: Contains time-bounds in " << scope.description << ".");
553 storm::jani::PropertyInterval pi =
554 parsePropertyInterval(propertyStructure.at("time-bounds"), scope.refine("time-bounded until").clearVariables());
555 insertLowerUpperTimeBounds(lowerBounds, upperBounds, pi);
556 tbReferences.emplace_back(storm::logic::TimeBoundType::Time);
557 }
558 if (propertyStructure.count("reward-bounds") > 0) {
559 for (auto const& rbStructure : propertyStructure.at("reward-bounds")) {
560 storm::jani::PropertyInterval pi = parsePropertyInterval(rbStructure.at("bounds"), scope.refine("reward-bounded until").clearVariables());
561 insertLowerUpperTimeBounds(lowerBounds, upperBounds, pi);
562 STORM_LOG_THROW(rbStructure.count("exp") == 1, storm::exceptions::InvalidJaniException,
563 "Expecting reward-expression for operator " << opString << " in " << scope.description << ".");
564 storm::expressions::Expression rewInstRewardModelExpression =
565 parseExpression(rbStructure.at("exp"), scope.refine("Reward expression at reward-bounds"));
566 STORM_LOG_THROW(rewInstRewardModelExpression.hasNumericalType(), storm::exceptions::InvalidJaniException,
567 "Reward expression '" << rewInstRewardModelExpression << "' does not have numerical type in " << scope.description << ".");
568 storm::logic::RewardAccumulation boundRewardAccumulation = parseRewardAccumulation(rbStructure.at("accumulate"), scope.description);
569 bool steps = (boundRewardAccumulation.isStepsSet() || boundRewardAccumulation.isExitSet()) && boundRewardAccumulation.size() == 1;
570 bool time = boundRewardAccumulation.isTimeSet() && boundRewardAccumulation.size() == 1 && !model.isDiscreteTimeModel();
571 if ((steps || time) && !rewInstRewardModelExpression.containsVariables() &&
572 storm::utility::isOne(rewInstRewardModelExpression.evaluateAsRational())) {
574 } else {
575 std::string rewInstRewardModelName = rewInstRewardModelExpression.toString();
576 if (!rewInstRewardModelExpression.isVariable()) {
577 model.addNonTrivialRewardExpression(rewInstRewardModelName, rewInstRewardModelExpression);
578 }
579 tbReferences.emplace_back(rewInstRewardModelName, boundRewardAccumulation);
580 }
581 }
582 }
583 if (!tbReferences.empty()) {
584 return std::make_shared<storm::logic::BoundedUntilFormula const>(args[0], args[1], lowerBounds, upperBounds, tbReferences);
585 } else if (args[0]->isTrueFormula()) {
586 return std::make_shared<storm::logic::EventuallyFormula const>(args[1], formulaContext);
587 } else {
588 return std::make_shared<storm::logic::UntilFormula const>(args[0], args[1]);
589 }
590 } else if (opString == "G") {
591 STORM_LOG_ASSERT(bound == boost::none, "Unexpected bound for globally.");
592 std::vector<std::shared_ptr<storm::logic::Formula const>> args =
593 parseUnaryFormulaArgument(model, propertyStructure, formulaContext, opString, scope.refine("Subformula of globally operator "));
594 if (propertyStructure.count("step-bounds") > 0) {
595 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Globally and step-bounds are not supported.");
596 } else if (propertyStructure.count("time-bounds") > 0) {
597 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Globally and time bounds are not supported.");
598 } else if (propertyStructure.count("reward-bounds") > 0) {
599 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Globally and reward bounded properties are not supported.");
600 }
601 return std::make_shared<storm::logic::GloballyFormula const>(args[0]);
602
603 } else if (opString == "W") {
604 STORM_LOG_ASSERT(bound == boost::none, "Unexpected bound for weak until.");
605 std::vector<std::shared_ptr<storm::logic::Formula const>> args =
606 parseBinaryFormulaArguments(model, propertyStructure, formulaContext, opString, scope);
607 STORM_LOG_ASSERT(args.size() == 2, "Expected two arguments for weak until.");
608 if (propertyStructure.count("step-bounds") > 0) {
609 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Weak until and step-bounds are not supported.");
610 } else if (propertyStructure.count("time-bounds") > 0) {
611 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Weak until and time bounds are not supported.");
612 } else if (propertyStructure.count("reward-bounds") > 0) {
613 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Weak until and reward bounded properties are not supported.");
614 }
615 return std::make_shared<storm::logic::WeakUntilFormula const>(args[0], args[1]);
616 } else if (opString == "R") {
617 STORM_LOG_ASSERT(bound == boost::none, "Unexpected bound for release.");
618 std::vector<std::shared_ptr<storm::logic::Formula const>> args =
619 parseBinaryFormulaArguments(model, propertyStructure, formulaContext, opString, scope);
620 STORM_LOG_ASSERT(args.size() == 2, "Expected two arguments for release.");
621 if (propertyStructure.count("step-bounds") > 0) {
622 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Release and step-bounds are not supported.");
623 } else if (propertyStructure.count("time-bounds") > 0) {
624 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Release and time bounds are not supported.");
625 } else if (propertyStructure.count("reward-bounds") > 0) {
626 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Release and reward bounded properties are not supported.");
627 }
628 return std::make_shared<storm::logic::ReleaseFormula const>(args[0], args[1]);
629 } else if (opString == "∧" || opString == "∨") {
630 STORM_LOG_ASSERT(bound == boost::none, "Unexpected bound for conjunction/disjunction.");
631 std::vector<std::shared_ptr<storm::logic::Formula const>> args =
632 parseBinaryFormulaArguments(model, propertyStructure, formulaContext, opString, scope);
633 STORM_LOG_ASSERT(args.size() == 2, "Expected two arguments for conjunction/disjunction.");
636 return makeBinaryBooleanFormula(oper, args[0], args[1], formulaContext);
637 } else if (opString == "⇒") {
638 STORM_LOG_ASSERT(bound == boost::none, "Unexpected bound for implication.");
639 std::vector<std::shared_ptr<storm::logic::Formula const>> args =
640 parseBinaryFormulaArguments(model, propertyStructure, formulaContext, opString, scope);
641 STORM_LOG_ASSERT(args.size() == 2, "Expected two arguments for implication.");
642 std::shared_ptr<storm::logic::Formula const> tmp = makeUnaryBooleanFormula(storm::logic::UnaryBooleanOperatorType::Not, args[0], formulaContext);
643 return makeBinaryBooleanFormula(storm::logic::BinaryBooleanOperatorType::Or, tmp, args[1], formulaContext);
644 } else if (opString == "¬") {
645 STORM_LOG_ASSERT(bound == boost::none, "Unexpected bound for negation.");
646 std::vector<std::shared_ptr<storm::logic::Formula const>> args =
647 parseUnaryFormulaArgument(model, propertyStructure, formulaContext, opString, scope);
648 STORM_LOG_ASSERT(args.size() == 1, "Expected one argument for negation.");
650 } else if (!expr.isInitialized() && (opString == "≥" || opString == "≤" || opString == "<" || opString == ">" || opString == "=" || opString == "≠")) {
651 STORM_LOG_ASSERT(bound == boost::none, "Unexpected bound for comparison.");
653 if (opString == "≥") {
655 } else if (opString == "≤") {
657 } else if (opString == "<") {
659 } else if (opString == ">") {
661 }
662
663 std::vector<std::string> const leftRight = {"left", "right"};
664 for (uint64_t i = 0; i < 2; ++i) {
665 if (propertyStructure.at(leftRight[i]).count("op") > 0) {
666 std::string propertyOperatorString = getString<ValueType>(propertyStructure.at(leftRight[i]).at("op"), "property-operator");
667 std::set<std::string> const propertyOperatorStrings = {"Pmin", "Pmax", "Emin", "Emax", "Smin", "Smax"};
668 if (propertyOperatorStrings.count(propertyOperatorString) > 0) {
669 auto boundExpr =
670 parseExpression(propertyStructure.at(leftRight[1 - i]),
671 scope.refine("Threshold for operator " + propertyStructure.at(leftRight[i]).at("op").template get<std::string>()));
672 if ((opString == "=" || opString == "≠")) {
673 STORM_LOG_THROW(!boundExpr.containsVariables(), storm::exceptions::NotSupportedException,
674 "Comparison operators '=' or '≠' in property specifications are currently not supported.");
675 auto boundValue = boundExpr.evaluateAsRational();
676 if (storm::utility::isZero(boundValue)) {
677 if (opString == "=") {
679 } else {
681 }
682 } else if (storm::utility::isOne(boundValue) && (propertyOperatorString == "Pmin" || propertyOperatorString == "Pmax")) {
683 if (opString == "=") {
685 } else {
687 }
688 } else {
690 storm::exceptions::NotSupportedException,
691 "Comparison operators '=' or '≠' in property specifications are currently not supported in " << scope.description << ".");
692 }
693 }
694 return parseFormula(model, propertyStructure.at(leftRight[i]), formulaContext, scope, storm::logic::Bound(ct, boundExpr));
695 }
696 }
697 }
698 STORM_LOG_THROW_UNCONDITIONALLY(storm::exceptions::NotSupportedException, "No complex comparisons for properties are supported.");
699 } else if (opString == "Multi") {
702 << " not enabled but model contains multi-objective property in " << scope.description
703 << ". Continuing with that property anyways");
704 STORM_LOG_ASSERT(bound == boost::none, "Unexpected bound for multi-objective.");
705 STORM_LOG_THROW(propertyStructure.count("properties") == 1, storm::exceptions::InvalidJaniException,
706 "Expecting properties for multi-objective operator in " << scope.description << ".");
707 std::vector<std::shared_ptr<storm::logic::Formula const>> subformulas;
708 uint64_t i = 0;
709 for (auto const& subPropStructure : propertyStructure.at("properties")) {
710 STORM_LOG_THROW(subPropStructure.count("exp") == 1, storm::exceptions::InvalidJaniException,
711 "Expecting property expression in subproperty #" << i << " in " << scope.description << ".");
712 subformulas.push_back(parseFormula(model, subPropStructure["exp"], formulaContext,
713 scope.refine("Subproperty #" + std::to_string(i) + " of multi-objective operator")));
714 if (subPropStructure.count("opt") == 1) {
715 STORM_LOG_THROW(subformulas.back()->hasQuantitativeResult(), storm::exceptions::InvalidJaniException,
716 "Subformula #" << i << " has an optimization direction but is not numeric in " << scope.description << ".");
717 STORM_LOG_THROW(subformulas.back()->isOperatorFormula(), storm::exceptions::NotSupportedException,
718 "Subformula #" << i << " is not an operator formula in " << scope.description << ".");
719 std::string const optString =
720 getString<ValueType>(subPropStructure.at("opt"), "optimization direction for subproperty #" + std::to_string(i));
721 STORM_LOG_THROW(optString == "min" || optString == "max", storm::exceptions::InvalidJaniException,
722 "Unknown optimization direction " << optString << " for subproperty #" << i << " in " << scope.description << ".");
723 auto const opt = optString == "min" ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize;
724 auto newFormula = subformulas.back()->clone();
725 newFormula->asOperatorFormula().setOptimalityType(opt);
726 subformulas.back() = newFormula;
727 } else {
728 STORM_LOG_THROW(subformulas.back()->hasQualitativeResult(), storm::exceptions::InvalidJaniException,
729 "Subformula #" << i << " has non-Boolean result but no optimization direction in " << scope.description << ".");
730 }
731 ++i;
732 }
733 STORM_LOG_THROW(propertyStructure.count("type") == 1, storm::exceptions::InvalidJaniException,
734 "Expecting type for multi-objective operator in " << scope.description << ".");
735 std::string const typeString = getString<ValueType>(propertyStructure.at("type"), "type of multi-objective operator");
736 STORM_LOG_THROW(typeString == "tradeoff" || typeString == "lexicographic", storm::exceptions::InvalidJaniException,
737 "Unknown type " << typeString << " for multi-objective operator in " << scope.description << ".");
738 auto const type =
740 return std::make_shared<storm::logic::MultiObjectiveFormula const>(subformulas, type);
741 } else if (expr.isInitialized()) {
742 STORM_LOG_THROW_UNCONDITIONALLY(storm::exceptions::InvalidJaniException,
743 "Non-trivial Expression '" << expr
744 << "' contains a boolean transient variable. Can not translate to PRCTL-like formula at "
745 << scope.description << ".");
746 } else {
747 STORM_LOG_THROW_UNCONDITIONALLY(storm::exceptions::InvalidJaniException, "Unknown operator " << opString << ".");
748 }
749 } else {
750 STORM_LOG_THROW_UNCONDITIONALLY(storm::exceptions::InvalidJaniException,
751 "Looking for operator for formula " << propertyStructure.dump() << ", but did not find one.");
752 }
753}
754
755template<typename ValueType>
757 STORM_LOG_THROW(propertyStructure.count("name") == 1, storm::exceptions::InvalidJaniException, "Property must have a name.");
758 // TODO check unique name
759 std::string name = getString<ValueType>(propertyStructure.at("name"), "property-name");
760 STORM_LOG_TRACE("Parsing property named: " << name);
761 std::string comment = "";
762 if (propertyStructure.count("comment") > 0) {
763 comment = getString<ValueType>(propertyStructure.at("comment"), "comment for property named '" + name + "'.");
764 }
765 STORM_LOG_THROW(propertyStructure.count("expression") == 1, storm::exceptions::InvalidJaniException, "Property must have an expression.");
766 // Parse filter expression.
767 Json const& expressionStructure = propertyStructure.at("expression");
768
769 STORM_LOG_THROW(expressionStructure.count("op") == 1, storm::exceptions::InvalidJaniException,
770 "Expression in property must have an operation description.");
771 STORM_LOG_THROW(expressionStructure.at("op") == "filter", storm::exceptions::InvalidJaniException, "Top level operation of a property must be a filter.");
772 STORM_LOG_THROW(expressionStructure.count("fun") == 1, storm::exceptions::InvalidJaniException, "Filter must have a function descritpion.");
773 std::string funDescr = getString<ValueType>(expressionStructure.at("fun"), "Filter function in property named " + name);
775 if (funDescr == "min") {
777 } else if (funDescr == "max") {
779 } else if (funDescr == "sum") {
781 } else if (funDescr == "avg") {
783 } else if (funDescr == "count") {
785 } else if (funDescr == "∀") {
787 } else if (funDescr == "∃") {
789 } else if (funDescr == "argmin") {
791 } else if (funDescr == "argmax") {
793 } else if (funDescr == "values") {
795 } else {
796 STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "Unknown filter description " << funDescr << " in property named " << name << ".");
797 }
798
799 STORM_LOG_THROW(expressionStructure.count("states") == 1, storm::exceptions::InvalidJaniException, "Filter must have a states description.");
800 std::shared_ptr<storm::logic::Formula const> statesFormula;
801 if (expressionStructure.at("states").count("op") > 0) {
802 std::string statesDescr = getString<ValueType>(expressionStructure.at("states").at("op"), "Filtered states in property named " + name);
803 if (statesDescr == "initial") {
804 statesFormula = std::make_shared<storm::logic::AtomicLabelFormula>("init");
805 }
806 }
807 if (!statesFormula) {
808 try {
809 // Try to parse the states as formula.
810 statesFormula =
811 parseFormula(model, expressionStructure.at("states"), storm::logic::FormulaContext::Undefined, scope.refine("Values of property " + name));
812 } catch (storm::exceptions::NotSupportedException const& ex) {
813 throw ex;
814 } catch (storm::exceptions::NotImplementedException const& ex) {
815 throw ex;
816 }
817 }
818 STORM_LOG_THROW(statesFormula, storm::exceptions::NotImplementedException, "Could not derive states formula.");
819 STORM_LOG_THROW(expressionStructure.count("values") == 1, storm::exceptions::InvalidJaniException, "Values as input for a filter must be given.");
820 auto formula = parseFormula(model, expressionStructure.at("values"), storm::logic::FormulaContext::Undefined, scope.refine("Values of property " + name));
821 return storm::jani::Property(name, storm::jani::FilterExpression(formula, ft, statesFormula), {}, comment);
822}
823
824template<typename ValueType>
825std::shared_ptr<storm::jani::Constant> JaniParser<ValueType>::parseConstant(Json const& constantStructure, Scope const& scope) {
826 STORM_LOG_THROW(constantStructure.count("name") == 1, storm::exceptions::InvalidJaniException,
827 "Variable (scope: " + scope.description + ") must have a name.");
828 std::string name = getString<ValueType>(constantStructure.at("name"), "variable-name in " + scope.description + "-scope");
829 // TODO check existance of name.
830 // TODO store prefix in variable.
831 std::string exprManagerName = name;
832
833 STORM_LOG_THROW(constantStructure.count("type") == 1, storm::exceptions::InvalidJaniException,
834 "Constant '" + name + "' (scope: " + scope.description + ") must have a (single) type-declaration.");
835 auto type = parseType(constantStructure.at("type"), name, scope);
836 STORM_LOG_THROW((type.first->isBasicType() || type.first->isBoundedType()), storm::exceptions::InvalidJaniException,
837 "Constant '" + name + "' (scope: " + scope.description + ") has unexpected type.");
838
839 uint_fast64_t valueCount = constantStructure.count("value");
840 storm::expressions::Expression definingExpression;
841 STORM_LOG_THROW(valueCount < 2, storm::exceptions::InvalidJaniException,
842 "Value for constant '" + name + "' (scope: " + scope.description + ") must be given at most once.");
843 if (valueCount == 1) {
844 // Read initial value before; that makes creation later on a bit easier, and has as an additional benefit that we do not need to check whether the
845 // variable occurs also on the assignment.
846 definingExpression = parseExpression(constantStructure.at("value"), scope.refine("Value of constant " + name));
847 STORM_LOG_ASSERT(definingExpression.isInitialized(), "Defining expression not initialized.");
848 // Check that the defined and actual expression value match OR the defined value is a rational and the actual value is a numerical type.
849 STORM_LOG_THROW((type.second == definingExpression.getType() || (type.second.isRationalType() && definingExpression.getType().isNumericalType())),
850 storm::exceptions::InvalidJaniException,
851 "Type of value for constant '" + name + "' (scope: " + scope.description + ") does not match the given type '" +
852 type.first->getStringRepresentation() + ".");
853 }
854
855 storm::expressions::Variable var = expressionManager->declareVariable(exprManagerName, type.second);
856
857 storm::expressions::Expression constraintExpression;
858 if (type.first->isBoundedType()) {
859 auto const& bndType = type.first->asBoundedType();
860 if (bndType.hasLowerBound()) {
861 constraintExpression = var.getExpression() >= bndType.getLowerBound();
862 if (bndType.hasUpperBound()) {
863 constraintExpression = constraintExpression && var.getExpression() <= bndType.getUpperBound();
864 }
865 } else if (bndType.hasUpperBound()) {
866 constraintExpression = var.getExpression() <= bndType.getUpperBound();
867 }
868 }
869 return std::make_shared<storm::jani::Constant>(name, std::move(var), definingExpression, constraintExpression);
870}
871
872template<typename ValueType>
873std::pair<std::unique_ptr<storm::jani::JaniType>, storm::expressions::Type> JaniParser<ValueType>::parseType(Json const& typeStructure,
874 std::string variableName, Scope const& scope) {
875 std::pair<std::unique_ptr<storm::jani::JaniType>, storm::expressions::Type> result;
876 if (typeStructure.is_string()) {
877 if (typeStructure == "real") {
878 result.first = std::make_unique<storm::jani::BasicType>(storm::jani::BasicType::Type::Real);
879 result.second = expressionManager->getRationalType();
880 } else if (typeStructure == "bool") {
881 result.first = std::make_unique<storm::jani::BasicType>(storm::jani::BasicType::Type::Bool);
882 result.second = expressionManager->getBooleanType();
883 } else if (typeStructure == "int") {
884 result.first = std::make_unique<storm::jani::BasicType>(storm::jani::BasicType::Type::Int);
885 result.second = expressionManager->getIntegerType();
886 } else if (typeStructure == "clock") {
887 result.first = std::make_unique<storm::jani::ClockType>();
888 result.second = expressionManager->getRationalType();
889 } else if (typeStructure == "continuous") {
890 result.first = std::make_unique<storm::jani::ContinuousType>();
891 result.second = expressionManager->getRationalType();
892 } else {
893 STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException,
894 "Unsupported type " << typeStructure.dump() << " for variable '" << variableName << "' (scope: " << scope.description << ").");
895 }
896 } else if (typeStructure.is_object()) {
897 STORM_LOG_THROW(typeStructure.count("kind") == 1, storm::exceptions::InvalidJaniException,
898 "For complex type as in variable " << variableName << "(scope: " << scope.description << ") kind must be given.");
899 std::string kind =
900 getString<ValueType>(typeStructure.at("kind"), "kind for complex type as in variable " + variableName + "(scope: " + scope.description + ") ");
901 if (kind == "bounded") {
903 typeStructure.count("lower-bound") + typeStructure.count("upper-bound") > 0, storm::exceptions::InvalidJaniException,
904 "For bounded type as in variable " << variableName << "(scope: " << scope.description << ") lower-bound or upper-bound must be given.");
905 storm::expressions::Expression lowerboundExpr;
906 if (typeStructure.count("lower-bound") > 0) {
907 lowerboundExpr = parseExpression(typeStructure.at("lower-bound"), scope.refine("Lower bound for variable " + variableName));
908 }
909 storm::expressions::Expression upperboundExpr;
910 if (typeStructure.count("upper-bound") > 0) {
911 upperboundExpr = parseExpression(typeStructure.at("upper-bound"), scope.refine("Upper bound for variable " + variableName));
912 }
913 STORM_LOG_THROW(typeStructure.count("base") == 1, storm::exceptions::InvalidJaniException,
914 "For bounded type as in variable " << variableName << "(scope: " << scope.description << ") base must be given.");
915 std::string basictype =
916 getString<ValueType>(typeStructure.at("base"), "base for bounded type as in variable " + variableName + "(scope: " + scope.description + ") ");
917 if (basictype == "int") {
918 STORM_LOG_THROW(!lowerboundExpr.isInitialized() || lowerboundExpr.hasIntegerType(), storm::exceptions::InvalidJaniException,
919 "Lower bound for bounded integer variable " << variableName << "(scope: " << scope.description << ") must be integer-typed.");
920 STORM_LOG_THROW(!upperboundExpr.isInitialized() || upperboundExpr.hasIntegerType(), storm::exceptions::InvalidJaniException,
921 "Upper bound for bounded integer variable " << variableName << "(scope: " << scope.description << ") must be integer-typed.");
922 if (lowerboundExpr.isInitialized() && upperboundExpr.isInitialized() && !lowerboundExpr.containsVariables() &&
923 !upperboundExpr.containsVariables()) {
924 STORM_LOG_THROW(lowerboundExpr.evaluateAsInt() <= upperboundExpr.evaluateAsInt(), storm::exceptions::InvalidJaniException,
925 "Lower bound must not be larger than upper bound for bounded integer variable " << variableName
926 << "(scope: " << scope.description << ").");
927 }
928 result.first = std::make_unique<storm::jani::BoundedType>(storm::jani::BoundedType::BaseType::Int, lowerboundExpr, upperboundExpr);
929 result.second = expressionManager->getIntegerType();
930 } else if (basictype == "real") {
931 STORM_LOG_THROW(!lowerboundExpr.isInitialized() || lowerboundExpr.hasNumericalType(), storm::exceptions::InvalidJaniException,
932 "Lower bound for bounded real variable " << variableName << "(scope: " << scope.description << ") must be numeric.");
933 STORM_LOG_THROW(!upperboundExpr.isInitialized() || upperboundExpr.hasNumericalType(), storm::exceptions::InvalidJaniException,
934 "Upper bound for bounded real variable " << variableName << "(scope: " << scope.description << ") must be numeric.");
935 if (lowerboundExpr.isInitialized() && upperboundExpr.isInitialized() && !lowerboundExpr.containsVariables() &&
936 !upperboundExpr.containsVariables()) {
937 using SubMap = std::map<storm::expressions::Variable, storm::expressions::Expression>;
938 storm::expressions::JaniExpressionSubstitutionVisitor<SubMap> transcendentalVisitor(SubMap(), true);
939 const storm::RationalNumber lowerboundValue = transcendentalVisitor.substitute(lowerboundExpr).evaluateAsRational();
940 const storm::RationalNumber upperboundValue = transcendentalVisitor.substitute(upperboundExpr).evaluateAsRational();
941 STORM_LOG_THROW(lowerboundValue <= upperboundValue, storm::exceptions::InvalidJaniException,
942 "Lower bound must not be larger than upper bound for bounded real variable " << variableName
943 << "(scope: " << scope.description << ").");
944 }
945 result.first = std::make_unique<storm::jani::BoundedType>(storm::jani::BoundedType::BaseType::Real, lowerboundExpr, upperboundExpr);
946 result.second = expressionManager->getRationalType();
947 } else {
948 STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException,
949 "Unsupported base " << basictype << " for bounded variable " << variableName << "(scope: " << scope.description << ").");
950 }
951 } else if (kind == "array") {
952 STORM_LOG_THROW(typeStructure.count("base") == 1, storm::exceptions::InvalidJaniException,
953 "For array type as in variable " << variableName << "(scope: " << scope.description << ") base must be given.");
954 auto base = parseType(typeStructure.at("base"), variableName, scope);
955 result.first = std::make_unique<storm::jani::ArrayType>(std::move(base.first));
956 result.second = expressionManager->getArrayType(base.second);
957 } else {
958 STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException,
959 "Unsupported kind " << kind << " for complex type of variable " << variableName << "(scope: " << scope.description << ").");
960 }
961 }
962 return result;
963}
964
965template<typename ValueType>
966storm::jani::FunctionDefinition JaniParser<ValueType>::parseFunctionDefinition(Json const& functionDefinitionStructure, Scope const& scope, bool firstPass,
967 std::string const& parameterNamePrefix) {
968 STORM_LOG_THROW(functionDefinitionStructure.count("name") == 1, storm::exceptions::InvalidJaniException,
969 "Function definition (scope: " + scope.description + ") must have a name.");
970 std::string functionName = getString<ValueType>(functionDefinitionStructure.at("name"), "function-name in " + scope.description);
971 STORM_LOG_THROW(functionDefinitionStructure.count("type") == 1, storm::exceptions::InvalidJaniException,
972 "Function definition '" + functionName + "' (scope: " + scope.description + ") must have a (single) type-declaration.");
973 auto type = parseType(functionDefinitionStructure.at("type"), functionName, scope);
975 !type.first->isClockType() && !type.first->isContinuousType(), storm::exceptions::InvalidJaniException,
976 "Function definition '" + functionName + "' (scope: " + scope.description + ") uses illegal type '" + type.first->getStringRepresentation() + "'.");
977
978 std::unordered_map<std::string, storm::expressions::Variable> parameterNameToVariableMap;
979 std::vector<storm::expressions::Variable> parameters;
980 if (!firstPass && functionDefinitionStructure.count("parameters") > 0) {
981 STORM_LOG_THROW(functionDefinitionStructure.count("parameters") == 1, storm::exceptions::InvalidJaniException,
982 "Function definition '" + functionName + "' (scope: " + scope.description + ") must have exactly one list of parameters.");
983 for (auto const& parameterStructure : functionDefinitionStructure.at("parameters")) {
984 STORM_LOG_THROW(parameterStructure.count("name") == 1, storm::exceptions::InvalidJaniException,
985 "Parameter declaration of parameter " + std::to_string(parameters.size()) + " of Function definition '" + functionName +
986 "' (scope: " + scope.description + ") must have a name.");
987 std::string parameterName =
988 getString<ValueType>(parameterStructure.at("name"), "parameter-name of parameter " + std::to_string(parameters.size()) +
989 " of Function definition '" + functionName + "' (scope: " + scope.description + ").");
990 STORM_LOG_THROW(parameterStructure.count("type") == 1, storm::exceptions::InvalidJaniException,
991 "Parameter declaration of parameter " + std::to_string(parameters.size()) + " of Function definition '" + functionName +
992 "' (scope: " + scope.description + ") must have exactly one type.");
993 auto parameterType =
994 parseType(parameterStructure.at("type"), parameterName,
995 scope.refine("parameter declaration of parameter " + std::to_string(parameters.size()) + " of Function definition " + functionName));
996 STORM_LOG_THROW(!parameterType.first->isClockType() && !parameterType.first->isContinuousType(), storm::exceptions::InvalidJaniException,
997 "Type of parameter " + std::to_string(parameters.size()) + " of function definition '" + functionName +
998 "' (scope: " + scope.description + ") uses illegal type '" + parameterType.first->getStringRepresentation() + "'.");
999 STORM_LOG_WARN_COND(!parameterType.first->isBoundedType(),
1000 "Bounds on parameter" + parameterName + " of function definition " + functionName + " will be ignored.");
1001
1002 std::string exprParameterName = parameterNamePrefix + functionName + VARIABLE_AUTOMATON_DELIMITER + parameterName;
1003 parameters.push_back(expressionManager->declareVariable(exprParameterName, parameterType.second));
1004 parameterNameToVariableMap.emplace(parameterName, parameters.back());
1005 }
1006 }
1007
1008 STORM_LOG_THROW(functionDefinitionStructure.count("body") == 1, storm::exceptions::InvalidJaniException,
1009 "Function definition '" + functionName + "' (scope: " + scope.description + ") must have a (single) body.");
1010 storm::expressions::Expression functionBody;
1011 if (!firstPass) {
1012 functionBody = parseExpression(functionDefinitionStructure.at("body"), scope.refine("body of function definition " + functionName), false,
1013 parameterNameToVariableMap);
1014 STORM_LOG_WARN_COND(functionBody.getType() == type.second || (functionBody.getType().isIntegerType() && type.second.isRationalType()),
1015 "Type of body of function " + functionName + "' (scope: " + scope.description + ") has type "
1016 << functionBody.getType() << " although the function type is given as " << type.second);
1017 }
1018 return storm::jani::FunctionDefinition(functionName, type.second, parameters, functionBody);
1019}
1020
1021template<typename ValueType>
1022std::shared_ptr<storm::jani::Variable> JaniParser<ValueType>::parseVariable(Json const& variableStructure, Scope const& scope, std::string const& namePrefix) {
1023 STORM_LOG_THROW(variableStructure.count("name") == 1, storm::exceptions::InvalidJaniException,
1024 "Variable (scope: " + scope.description + ") must have a name.");
1025 std::string name = getString<ValueType>(variableStructure.at("name"), "variable-name in " + scope.description + "-scope");
1026 // TODO check existance of name.
1027 // TODO store prefix in variable.
1028 std::string exprManagerName = namePrefix + name;
1029 bool transientVar = defaultVariableTransient; // Default value for variables.
1030 uint_fast64_t tvarcount = variableStructure.count("transient");
1031 STORM_LOG_THROW(tvarcount <= 1, storm::exceptions::InvalidJaniException,
1032 "Multiple definitions of transient not allowed in variable '" + name + "' (scope: " + scope.description + ").");
1033 if (tvarcount == 1) {
1034 transientVar =
1035 getBoolean<ValueType>(variableStructure.at("transient"), "transient-attribute in variable '" + name + "' (scope: " + scope.description + ").");
1036 }
1037 STORM_LOG_THROW(variableStructure.count("type") == 1, storm::exceptions::InvalidJaniException,
1038 "Variable '" + name + "' (scope: " + scope.description + ") must have a (single) type-declaration.");
1039 auto type = parseType(variableStructure.at("type"), name, scope);
1040
1041 uint_fast64_t initvalcount = variableStructure.count("initial-value");
1042 if (transientVar) {
1043 STORM_LOG_THROW(initvalcount == 1, storm::exceptions::InvalidJaniException,
1044 "Initial value must be given once for transient variable '" + name + "' (scope: " + scope.description + ") " + name +
1045 "' (scope: " + scope.description + ").");
1046 } else {
1047 STORM_LOG_THROW(initvalcount <= 1, storm::exceptions::InvalidJaniException,
1048 "Initial value can be given at most one for variable " + name + "' (scope: " + scope.description + ").");
1049 }
1050 boost::optional<storm::expressions::Expression> initVal;
1051 if (initvalcount == 1 && !variableStructure.at("initial-value").is_null()) {
1052 initVal = parseExpression(variableStructure.at("initial-value"), scope.refine("Initial value for variable " + name));
1053 // STORM_LOG_THROW((type.second == initVal->getType() || type.second.isRationalType() && initVal->getType().isIntegerType()),
1054 // storm::exceptions::InvalidJaniException,"Type of initial value for variable " + name + "' (scope: " + scope.description + ") does not match the
1055 // variable type '" + type.first->getStringRepresentation() + "'.");
1056 } else {
1057 STORM_LOG_ASSERT(!transientVar, "Unexpected transient variable.");
1058 }
1059
1060 if (transientVar && type.first->isBasicType() && type.first->asBasicType().isBooleanType()) {
1061 labels.insert(name);
1062 }
1063
1064 auto expressionVariable = expressionManager->declareVariable(exprManagerName, type.second);
1065 return storm::jani::Variable::makeVariable(name, *type.first, expressionVariable, initVal, transientVar);
1066}
1067
1071void ensureNumberOfArguments(uint64_t expected, uint64_t actual, std::string const& opstring, std::string const& errorInfo) {
1072 STORM_LOG_THROW(expected == actual, storm::exceptions::InvalidJaniException,
1073 "Operator " << opstring << " expects " << expected << " arguments, but got " << actual << " in " << errorInfo << ".");
1074}
1075
1076template<typename ValueType>
1077std::vector<storm::expressions::Expression> JaniParser<ValueType>::parseUnaryExpressionArguments(
1078 Json const& expressionDecl, std::string const& opstring, Scope const& scope, bool returnNoneInitializedOnUnknownOperator,
1079 std::unordered_map<std::string, storm::expressions::Variable> const& auxiliaryVariables) {
1081 parseExpression(expressionDecl.at("exp"), scope.refine("Argument of operator " + opstring), returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1082 return {left};
1083}
1084
1085template<typename ValueType>
1086std::vector<storm::expressions::Expression> JaniParser<ValueType>::parseBinaryExpressionArguments(
1087 Json const& expressionDecl, std::string const& opstring, Scope const& scope, bool returnNoneInitializedOnUnknownOperator,
1088 std::unordered_map<std::string, storm::expressions::Variable> const& auxiliaryVariables) {
1089 storm::expressions::Expression left = parseExpression(expressionDecl.at("left"), scope.refine("Left argument of operator " + opstring),
1090 returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1091 storm::expressions::Expression right = parseExpression(expressionDecl.at("right"), scope.refine("Right argument of operator " + opstring),
1092 returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1093 return {left, right};
1094}
1098void ensureBooleanType(storm::expressions::Expression const& expr, std::string const& opstring, unsigned argNr, std::string const& errorInfo) {
1099 STORM_LOG_THROW(expr.hasBooleanType(), storm::exceptions::InvalidJaniException,
1100 "Operator " << opstring << " expects argument[" << argNr << "]: '" << expr << "' to be Boolean in " << errorInfo << ".");
1101}
1102
1106void ensureNumericalType(storm::expressions::Expression const& expr, std::string const& opstring, unsigned argNr, std::string const& errorInfo) {
1107 STORM_LOG_THROW(expr.hasNumericalType(), storm::exceptions::InvalidJaniException,
1108 "Operator " << opstring << " expects argument " + std::to_string(argNr) + " to be numerical in " << errorInfo << ".");
1109}
1110
1114void ensureIntegerType(storm::expressions::Expression const& expr, std::string const& opstring, unsigned argNr, std::string const& errorInfo) {
1115 STORM_LOG_THROW(expr.hasIntegerType(), storm::exceptions::InvalidJaniException,
1116 "Operator " << opstring << " expects argument " + std::to_string(argNr) + " to be numerical in " << errorInfo << ".");
1117}
1118
1122void ensureArrayType(storm::expressions::Expression const& expr, std::string const& opstring, unsigned argNr, std::string const& errorInfo) {
1123 STORM_LOG_THROW(expr.getType().isArrayType(), storm::exceptions::InvalidJaniException,
1124 "Operator " << opstring << " expects argument " + std::to_string(argNr) + " to be of type 'array' in " << errorInfo << ".");
1125}
1126
1127template<typename ValueType>
1129 if (lValueStructure.is_string()) {
1130 std::string ident = getString<ValueType>(lValueStructure, scope.description);
1131 storm::jani::Variable const* var = nullptr;
1132 if (scope.localVars != nullptr) {
1133 auto localVar = scope.localVars->find(ident);
1134 if (localVar != scope.localVars->end()) {
1135 var = localVar->second;
1136 }
1137 }
1138 if (var == nullptr) {
1139 STORM_LOG_THROW(scope.globalVars != nullptr, storm::exceptions::InvalidJaniException,
1140 "Unknown identifier '" << ident << "' occurs in " << scope.description << ".");
1141 auto globalVar = scope.globalVars->find(ident);
1142 STORM_LOG_THROW(globalVar != scope.globalVars->end(), storm::exceptions::InvalidJaniException,
1143 "Unknown identifier '" << ident << "' occurs in " << scope.description << ".");
1144 var = globalVar->second;
1145 }
1146
1147 return storm::jani::LValue(*var);
1148 } else if (lValueStructure.count("op") == 1) {
1149 // structure will be something like "op": "aa", "exp": {}, "index": {}
1150 // in exp we have something that is either a variable, or some other array access.
1151 // e.g. a[1][4] will look like: "op": "aa", "exp": {"op": "aa", "exp": "a", "index": {1}}, "index": {4}
1152 std::string opstring = getString<ValueType>(lValueStructure.at("op"), scope.description);
1153 STORM_LOG_THROW(opstring == "aa", storm::exceptions::InvalidJaniException,
1154 "Unknown operation '" << opstring << "' occurs in " << scope.description << ".");
1155 STORM_LOG_THROW(lValueStructure.count("exp") == 1, storm::exceptions::InvalidJaniException,
1156 "Missing 'exp' in array access at " << scope.description << ".");
1157 auto expLValue = parseLValue(lValueStructure.at("exp"), scope.refine("Expression of array access"));
1158 STORM_LOG_THROW(expLValue.isArray(), storm::exceptions::InvalidJaniException,
1159 "Array access considers non-array expression at " << scope.description << ".");
1160 STORM_LOG_THROW(lValueStructure.count("index"), storm::exceptions::InvalidJaniException,
1161 "Missing 'index' in array access at " << scope.description << ".");
1162 auto indexExpression = parseExpression(lValueStructure.at("index"), scope.refine("Index of array access"));
1163 expLValue.addArrayAccessIndex(indexExpression);
1164 return expLValue;
1165 } else {
1166 STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException,
1167 "Unknown LValue '" << lValueStructure.dump() << "' occurs in " << scope.description << ".");
1168 // Silly warning suppression.
1169 return storm::jani::LValue(*scope.globalVars->end()->second);
1170 }
1171}
1172
1173template<typename ValueType>
1174storm::expressions::Variable JaniParser<ValueType>::getVariableOrConstantExpression(
1175 std::string const& ident, Scope const& scope, std::unordered_map<std::string, storm::expressions::Variable> const& auxiliaryVariables) {
1176 {
1177 auto it = auxiliaryVariables.find(ident);
1178 if (it != auxiliaryVariables.end()) {
1179 return it->second;
1180 }
1181 }
1182 if (scope.localVars != nullptr) {
1183 auto it = scope.localVars->find(ident);
1184 if (it != scope.localVars->end()) {
1185 return it->second->getExpressionVariable();
1186 }
1187 }
1188 if (scope.globalVars != nullptr) {
1189 auto it = scope.globalVars->find(ident);
1190 if (it != scope.globalVars->end()) {
1191 return it->second->getExpressionVariable();
1192 }
1193 }
1194 if (scope.constants != nullptr) {
1195 auto it = scope.constants->find(ident);
1196 if (it != scope.constants->end()) {
1197 return it->second->getExpressionVariable();
1198 }
1199 }
1200 STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "Unknown identifier '" << ident << "' occurs in " << scope.description << ".");
1201 // Silly warning suppression.
1202 return storm::expressions::Variable();
1203}
1204
1205template<typename ValueType>
1207 bool returnNoneInitializedOnUnknownOperator,
1208 std::unordered_map<std::string, storm::expressions::Variable> const& auxiliaryVariables) {
1209 if (expressionStructure.is_boolean()) {
1210 if (expressionStructure.template get<bool>()) {
1211 return expressionManager->boolean(true);
1212 } else {
1213 return expressionManager->boolean(false);
1214 }
1215 } else if (expressionStructure.is_number_integer()) {
1216 return expressionManager->integer(expressionStructure.template get<int64_t>());
1217 } else if (expressionStructure.is_number_float()) {
1218 return expressionManager->rational(storm::utility::convertNumber<storm::RationalNumber>(expressionStructure.template get<ValueType>()));
1219 } else if (expressionStructure.is_string()) {
1220 std::string ident = expressionStructure.template get<std::string>();
1221 return storm::expressions::Expression(getVariableOrConstantExpression(ident, scope, auxiliaryVariables));
1222 } else if (expressionStructure.is_object()) {
1223 if (expressionStructure.count("distribution") == 1) {
1225 false, storm::exceptions::InvalidJaniException,
1226 "Distributions are not supported by storm expressions, cannot import " << expressionStructure.dump() << " in " << scope.description << ".");
1227 }
1228 if (expressionStructure.count("op") == 1) {
1229 std::string opstring = getString<ValueType>(expressionStructure.at("op"), scope.description);
1230 std::vector<storm::expressions::Expression> arguments = {};
1231 if (opstring == "ite") {
1232 STORM_LOG_THROW(expressionStructure.count("if") == 1, storm::exceptions::InvalidJaniException, "If operator required.");
1233 STORM_LOG_THROW(expressionStructure.count("else") == 1, storm::exceptions::InvalidJaniException, "Else operator required.");
1234 STORM_LOG_THROW(expressionStructure.count("then") == 1, storm::exceptions::InvalidJaniException, "Then operator required.");
1235 arguments.push_back(
1236 parseExpression(expressionStructure.at("if"), scope.refine("if-formula"), returnNoneInitializedOnUnknownOperator, auxiliaryVariables));
1237 arguments.push_back(
1238 parseExpression(expressionStructure.at("then"), scope.refine("then-formula"), returnNoneInitializedOnUnknownOperator, auxiliaryVariables));
1239 arguments.push_back(
1240 parseExpression(expressionStructure.at("else"), scope.refine("else-formula"), returnNoneInitializedOnUnknownOperator, auxiliaryVariables));
1241 ensureNumberOfArguments(3, arguments.size(), opstring, scope.description);
1242 STORM_LOG_ASSERT(arguments.size() == 3, "Expected three arguments.");
1243 ensureBooleanType(arguments[0], opstring, 0, scope.description);
1244 return storm::expressions::ite(arguments[0], arguments[1], arguments[2]);
1245 } else if (opstring == "∨") {
1246 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1247 STORM_LOG_ASSERT(arguments.size() == 2, "Expected two arguments.");
1248 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1250 }
1251 ensureBooleanType(arguments[0], opstring, 0, scope.description);
1252 ensureBooleanType(arguments[1], opstring, 1, scope.description);
1253 return arguments[0] || arguments[1];
1254 } else if (opstring == "∧") {
1255 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1256 STORM_LOG_ASSERT(arguments.size() == 2, "Expected two arguments.");
1257 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1259 }
1260 ensureBooleanType(arguments[0], opstring, 0, scope.description);
1261 ensureBooleanType(arguments[1], opstring, 1, scope.description);
1262 return arguments[0] && arguments[1];
1263 } else if (opstring == "⇒") {
1264 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1265 STORM_LOG_ASSERT(arguments.size() == 2, "Expected two arguments.");
1266 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1268 }
1269 ensureBooleanType(arguments[0], opstring, 0, scope.description);
1270 ensureBooleanType(arguments[1], opstring, 1, scope.description);
1271 return (!arguments[0]) || arguments[1];
1272 } else if (opstring == "¬") {
1273 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1274 STORM_LOG_ASSERT(arguments.size() == 1, "Expected one argument.");
1275 if (!arguments[0].isInitialized()) {
1277 }
1278 ensureBooleanType(arguments[0], opstring, 0, scope.description);
1279 return !arguments[0];
1280 } else if (opstring == "=") {
1281 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1282 STORM_LOG_ASSERT(arguments.size() == 2, "Expected two arguments.");
1283 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1285 }
1286 if (arguments[0].hasBooleanType()) {
1287 ensureBooleanType(arguments[1], opstring, 1, scope.description);
1288 return storm::expressions::iff(arguments[0], arguments[1]);
1289 } else {
1290 ensureNumericalType(arguments[1], opstring, 1, scope.description);
1291 return arguments[0] == arguments[1];
1292 }
1293 } else if (opstring == "≠") {
1294 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1295 STORM_LOG_ASSERT(arguments.size() == 2, "Expected two arguments.");
1296 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1298 }
1299 if (arguments[0].hasBooleanType()) {
1300 ensureBooleanType(arguments[1], opstring, 1, scope.description);
1301 return storm::expressions::xclusiveor(arguments[0], arguments[1]);
1302 } else {
1303 ensureNumericalType(arguments[1], opstring, 1, scope.description);
1304 return arguments[0] != arguments[1];
1305 }
1306 } else if (opstring == "<") {
1307 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1308 STORM_LOG_ASSERT(arguments.size() == 2, "Expected two arguments.");
1309 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1311 }
1312 ensureNumericalType(arguments[0], opstring, 0, scope.description);
1313 ensureNumericalType(arguments[1], opstring, 1, scope.description);
1314 return arguments[0] < arguments[1];
1315 } else if (opstring == "≤") {
1316 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1317 STORM_LOG_ASSERT(arguments.size() == 2, "Expected two arguments.");
1318 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1320 }
1321 ensureNumericalType(arguments[0], opstring, 0, scope.description);
1322 ensureNumericalType(arguments[1], opstring, 1, scope.description);
1323 return arguments[0] <= arguments[1];
1324 } else if (opstring == ">") {
1325 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1326 STORM_LOG_ASSERT(arguments.size() == 2, "Expected two arguments.");
1327 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1329 }
1330 ensureNumericalType(arguments[0], opstring, 0, scope.description);
1331 ensureNumericalType(arguments[1], opstring, 1, scope.description);
1332 return arguments[0] > arguments[1];
1333 } else if (opstring == "≥") {
1334 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1335 STORM_LOG_ASSERT(arguments.size() == 2, "Expected two arguments.");
1336 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1338 }
1339 ensureNumericalType(arguments[0], opstring, 0, scope.description);
1340 ensureNumericalType(arguments[1], opstring, 1, scope.description);
1341 return arguments[0] >= arguments[1];
1342 } else if (opstring == "+") {
1343 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1344 STORM_LOG_ASSERT(arguments.size() == 2, "Expected two arguments.");
1345 ensureNumericalType(arguments[0], opstring, 0, scope.description);
1346 ensureNumericalType(arguments[1], opstring, 1, scope.description);
1347 return arguments[0] + arguments[1];
1348 } else if (opstring == "-" && expressionStructure.count("left") > 0) {
1349 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1350 STORM_LOG_ASSERT(arguments.size() == 2, "Expected two arguments.");
1351 ensureNumericalType(arguments[0], opstring, 0, scope.description);
1352 ensureNumericalType(arguments[1], opstring, 1, scope.description);
1353 return arguments[0] - arguments[1];
1354 } else if (opstring == "-") {
1355 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1356 STORM_LOG_ASSERT(arguments.size() == 1, "Expected one argument.");
1357 ensureNumericalType(arguments[0], opstring, 0, scope.description);
1358 return -arguments[0];
1359 } else if (opstring == "*") {
1360 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1361 STORM_LOG_ASSERT(arguments.size() == 2, "Expected two arguments.");
1362 ensureNumericalType(arguments[0], opstring, 0, scope.description);
1363 ensureNumericalType(arguments[1], opstring, 1, scope.description);
1364 return arguments[0] * arguments[1];
1365 } else if (opstring == "/") {
1366 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1367 STORM_LOG_ASSERT(arguments.size() == 2, "Expected two arguments.");
1368 ensureNumericalType(arguments[0], opstring, 0, scope.description);
1369 ensureNumericalType(arguments[1], opstring, 1, scope.description);
1370 return arguments[0] / arguments[1];
1371 } else if (opstring == "%") {
1372 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1373 STORM_LOG_ASSERT(arguments.size() == 2, "Expected two arguments.");
1374 ensureNumericalType(arguments[0], opstring, 0, scope.description);
1375 ensureNumericalType(arguments[1], opstring, 1, scope.description);
1376 return arguments[0] % arguments[1];
1377 } else if (opstring == "max") {
1378 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1379 STORM_LOG_ASSERT(arguments.size() == 2, "Expected two arguments.");
1380 ensureNumericalType(arguments[0], opstring, 0, scope.description);
1381 ensureNumericalType(arguments[1], opstring, 1, scope.description);
1382 return storm::expressions::maximum(arguments[0], arguments[1]);
1383 } else if (opstring == "min") {
1384 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1385 STORM_LOG_ASSERT(arguments.size() == 2, "Expected two arguments.");
1386 ensureNumericalType(arguments[0], opstring, 0, scope.description);
1387 ensureNumericalType(arguments[1], opstring, 1, scope.description);
1388 return storm::expressions::minimum(arguments[0], arguments[1]);
1389 } else if (opstring == "floor") {
1390 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1391 STORM_LOG_ASSERT(arguments.size() == 1, "Expected one argument.");
1392 ensureNumericalType(arguments[0], opstring, 0, scope.description);
1393 return storm::expressions::floor(arguments[0]);
1394 } else if (opstring == "ceil") {
1395 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1396 STORM_LOG_ASSERT(arguments.size() == 1, "Expected one argument.");
1397 ensureNumericalType(arguments[0], opstring, 0, scope.description);
1398 return storm::expressions::ceil(arguments[0]);
1399 } else if (opstring == "abs") {
1400 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1401 STORM_LOG_ASSERT(arguments.size() == 1, "Expected one argument.");
1402 ensureNumericalType(arguments[0], opstring, 0, scope.description);
1403 return storm::expressions::abs(arguments[0]);
1404 } else if (opstring == "sgn") {
1405 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1406 STORM_LOG_ASSERT(arguments.size() == 1, "Expected one argument.");
1407 ensureNumericalType(arguments[0], opstring, 0, scope.description);
1408 return storm::expressions::sign(arguments[0]);
1409 } else if (opstring == "trc") {
1410 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1411 STORM_LOG_ASSERT(arguments.size() == 1, "Expected one argument.");
1412 ensureNumericalType(arguments[0], opstring, 0, scope.description);
1413 return storm::expressions::truncate(arguments[0]);
1414 } else if (opstring == "pow") {
1415 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1416 STORM_LOG_ASSERT(arguments.size() == 2, "Expected two arguments.");
1417 ensureNumericalType(arguments[0], opstring, 0, scope.description);
1418 ensureNumericalType(arguments[1], opstring, 1, scope.description);
1419 return storm::expressions::pow(arguments[0], arguments[1]);
1420 } else if (opstring == "exp") {
1421 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1422 STORM_LOG_ASSERT(arguments.size() == 2, "Expected two arguments.");
1423 ensureNumericalType(arguments[0], opstring, 0, scope.description);
1424 ensureNumericalType(arguments[1], opstring, 1, scope.description);
1425 // TODO implement
1426 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Exp operation is not yet implemented.");
1427 } else if (opstring == "log") {
1428 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1429 STORM_LOG_ASSERT(arguments.size() == 2, "Expected two arguments.");
1430 ensureNumericalType(arguments[0], opstring, 0, scope.description);
1431 ensureNumericalType(arguments[1], opstring, 1, scope.description);
1432 return storm::expressions::logarithm(arguments[0], arguments[1]);
1433 } else if (opstring == "cos") {
1434 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1435 STORM_LOG_ASSERT(arguments.size() == 1, "Expected one argument.");
1436 ensureNumericalType(arguments[0], opstring, 0, scope.description);
1437 return storm::expressions::cos(arguments[0]);
1438 } else if (opstring == "sin") {
1439 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1440 STORM_LOG_ASSERT(arguments.size() == 1, "Expected one argument.");
1441 ensureNumericalType(arguments[0], opstring, 0, scope.description);
1442 return storm::expressions::sin(arguments[0]);
1443 } else if (opstring == "aa") {
1444 STORM_LOG_THROW(expressionStructure.count("exp") == 1, storm::exceptions::InvalidJaniException,
1445 "Array access operator requires exactly one exp (at " + scope.description + ").");
1446 storm::expressions::Expression exp = parseExpression(expressionStructure.at("exp"), scope.refine("'exp' of array access operator"),
1447 returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1448 STORM_LOG_THROW(expressionStructure.count("index") == 1, storm::exceptions::InvalidJaniException,
1449 "Array access operator requires exactly one index (at " + scope.description + ").");
1450 storm::expressions::Expression index = parseExpression(expressionStructure.at("index"), scope.refine("index of array access operator"),
1451 returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1452 ensureArrayType(exp, opstring, 0, scope.description);
1453 ensureIntegerType(index, opstring, 1, scope.description);
1454 return std::make_shared<storm::expressions::ArrayAccessExpression>(exp.getManager(), exp.getType().getElementType(),
1456 ->toExpression();
1457 } else if (opstring == "av") {
1458 STORM_LOG_THROW(expressionStructure.count("elements") == 1, storm::exceptions::InvalidJaniException,
1459 "Array value operator requires exactly one 'elements' (at " + scope.description + ").");
1460 std::vector<std::shared_ptr<storm::expressions::BaseExpression const>> elements;
1461 storm::expressions::Type commonType;
1462 bool first = true;
1463 for (auto const& element : expressionStructure.at("elements")) {
1464 elements.push_back(parseExpression(element, scope.refine("element " + std::to_string(elements.size()) + " of array value expression"),
1465 returnNoneInitializedOnUnknownOperator, auxiliaryVariables)
1466 .getBaseExpressionPointer());
1467 if (first) {
1468 commonType = elements.back()->getType();
1469 first = false;
1470 } else if (commonType != elements.back()->getType()) {
1471 if (commonType.isIntegerType() && elements.back()->getType().isRationalType()) {
1472 commonType = elements.back()->getType();
1473 } else {
1474 STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException,
1475 "Incompatible element types " << commonType << " and " << elements.back()->getType()
1476 << " of array value expression at " << scope.description << ".");
1477 }
1478 }
1479 }
1480 return std::make_shared<storm::expressions::ValueArrayExpression>(*expressionManager, expressionManager->getArrayType(commonType), elements)
1481 ->toExpression();
1482 } else if (opstring == "ac") {
1483 STORM_LOG_THROW(expressionStructure.count("length") == 1, storm::exceptions::InvalidJaniException,
1484 "Array access operator requires exactly one length (at " + scope.description + ").");
1485 storm::expressions::Expression length = parseExpression(expressionStructure.at("length"), scope.refine("index of array constructor expression"),
1486 returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1487 ensureIntegerType(length, opstring, 1, scope.description);
1488 STORM_LOG_THROW(expressionStructure.count("var") == 1, storm::exceptions::InvalidJaniException,
1489 "Array access operator requires exactly one var (at " + scope.description + ").");
1490 std::string indexVarName =
1491 getString<ValueType>(expressionStructure.at("var"), "Field 'var' of Array access operator (at " + scope.description + ").");
1492 STORM_LOG_THROW(auxiliaryVariables.find(indexVarName) == auxiliaryVariables.end(), storm::exceptions::InvalidJaniException,
1493 "Index variable " << indexVarName << " is already defined as an auxiliary variable (at " + scope.description + ").");
1494 auto newAuxVars = auxiliaryVariables;
1495 storm::expressions::Variable indexVar = expressionManager->declareFreshIntegerVariable(false, "ac_" + indexVarName);
1496 newAuxVars.emplace(indexVarName, indexVar);
1497 STORM_LOG_THROW(expressionStructure.count("exp") == 1, storm::exceptions::InvalidJaniException,
1498 "Array constructor operator requires exactly one exp (at " + scope.description + ").");
1499 storm::expressions::Expression exp = parseExpression(expressionStructure.at("exp"), scope.refine("exp of array constructor"),
1500 returnNoneInitializedOnUnknownOperator, newAuxVars);
1501 return std::make_shared<storm::expressions::ConstructorArrayExpression>(*expressionManager, expressionManager->getArrayType(exp.getType()),
1502 length.getBaseExpressionPointer(), indexVar,
1504 ->toExpression();
1505 } else if (opstring == "call") {
1506 STORM_LOG_THROW(expressionStructure.count("function") == 1, storm::exceptions::InvalidJaniException,
1507 "Function call operator requires exactly one function (at " + scope.description + ").");
1508 std::string functionName =
1509 getString<ValueType>(expressionStructure.at("function"), "in function call operator (at " + scope.description + ").");
1510 storm::jani::FunctionDefinition const* functionDefinition;
1511 if (scope.localFunctions != nullptr && scope.localFunctions->count(functionName) > 0) {
1512 functionDefinition = scope.localFunctions->at(functionName);
1513 } else if (scope.globalFunctions != nullptr && scope.globalFunctions->count(functionName) > 0) {
1514 functionDefinition = scope.globalFunctions->at(functionName);
1515 } else {
1516 STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException,
1517 "Function call operator calls unknown function '" + functionName + "' (at " + scope.description + ").");
1518 }
1519 STORM_LOG_THROW(expressionStructure.count("args") == 1, storm::exceptions::InvalidJaniException,
1520 "Function call operator requires exactly one args (at " + scope.description + ").");
1521 std::vector<std::shared_ptr<storm::expressions::BaseExpression const>> args;
1522 if (expressionStructure.count("args") > 0) {
1523 STORM_LOG_THROW(expressionStructure.count("args") == 1, storm::exceptions::InvalidJaniException,
1524 "Function call operator requires exactly one args (at " + scope.description + ").");
1525 for (auto const& arg : expressionStructure.at("args")) {
1526 args.push_back(parseExpression(arg, scope.refine("argument " + std::to_string(args.size()) + " of function call expression"),
1527 returnNoneInitializedOnUnknownOperator, auxiliaryVariables)
1528 .getBaseExpressionPointer());
1529 }
1530 }
1531 return std::make_shared<storm::expressions::FunctionCallExpression>(*expressionManager, functionDefinition->getType(), functionName, args)
1532 ->toExpression();
1533 } else if (unsupportedOpstrings.count(opstring) > 0) {
1534 STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "Opstring " + opstring + " is not supported by storm.");
1535 } else {
1536 if (returnNoneInitializedOnUnknownOperator) {
1538 }
1539 STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "Unknown operator " << opstring << " in " << scope.description << ".");
1540 }
1541 }
1542 if (expressionStructure.count("constant") == 1) {
1543 // Convert constants to a numeric value (only PI and Euler number, as in Jani specification)
1544 const std::string constantStr = getString<ValueType>(expressionStructure.at("constant"), scope.description);
1545 if (constantStr == "Ï€") {
1546 return std::make_shared<storm::expressions::TranscendentalNumberLiteralExpression>(
1548 ->toExpression();
1549 }
1550 if (constantStr == "e") {
1551 return std::make_shared<storm::expressions::TranscendentalNumberLiteralExpression>(
1553 ->toExpression();
1554 }
1555 }
1557 false, storm::exceptions::InvalidJaniException,
1558 "No supported operator declaration found for complex expressions as " << expressionStructure.dump() << " in " << scope.description << ".");
1559 }
1560 STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException,
1561 "No supported expression found at " << expressionStructure.dump() << " in " << scope.description << ".");
1562 // Silly warning suppression.
1564}
1565
1566template<typename ValueType>
1567void JaniParser<ValueType>::parseActions(Json const& actionStructure, storm::jani::Model& parentModel) {
1568 std::set<std::string> actionNames;
1569 for (auto const& actionEntry : actionStructure) {
1570 STORM_LOG_THROW(actionEntry.count("name") == 1, storm::exceptions::InvalidJaniException, "Actions must have exactly one name.");
1571 std::string actionName = getString<ValueType>(actionEntry.at("name"), "name of action");
1572 STORM_LOG_THROW(actionNames.count(actionName) == 0, storm::exceptions::InvalidJaniException, "Action with name " + actionName + " already exists.");
1573 parentModel.addAction(storm::jani::Action(actionName));
1574 actionNames.emplace(actionName);
1575 }
1576}
1577
1578template<typename ValueType>
1579storm::jani::Automaton JaniParser<ValueType>::parseAutomaton(Json const& automatonStructure, storm::jani::Model const& parentModel, Scope const& globalScope) {
1580 STORM_LOG_THROW(automatonStructure.count("name") == 1, storm::exceptions::InvalidJaniException, "Each automaton must have a name.");
1581 std::string name = getString<ValueType>(automatonStructure.at("name"), " the name field for automaton");
1582 Scope scope = globalScope.refine(name);
1583 storm::jani::Automaton automaton(name, expressionManager->declareIntegerVariable("_loc_" + name));
1584
1585 uint64_t varDeclCount = automatonStructure.count("variables");
1586 STORM_LOG_THROW(varDeclCount < 2, storm::exceptions::InvalidJaniException, "Automaton '" << name << "' has more than one list of variables.");
1587 VariablesMap localVars;
1588 scope.localVars = &localVars;
1589 if (varDeclCount > 0) {
1590 for (auto const& varStructure : automatonStructure.at("variables")) {
1591 std::shared_ptr<storm::jani::Variable> var = parseVariable(
1592 varStructure, scope.refine("variables[" + std::to_string(localVars.size()) + "] of automaton " + name), name + VARIABLE_AUTOMATON_DELIMITER);
1593 STORM_LOG_ASSERT(localVars.count(var->getName()) == 0, "Local variable already exists.");
1594 localVars.emplace(var->getName(), &automaton.addVariable(*var));
1595 }
1596 }
1597
1598 uint64_t funDeclCount = automatonStructure.count("functions");
1599 STORM_LOG_THROW(funDeclCount < 2, storm::exceptions::InvalidJaniException, "Automaton '" << name << "' has more than one list of functions.");
1600 FunctionsMap localFuns;
1601 scope.localFunctions = &localFuns;
1602 if (funDeclCount > 0) {
1603 // We require two passes through the function definitions array to allow referring to functions before they were defined.
1604 std::vector<storm::jani::FunctionDefinition> dummyFunctionDefinitions;
1605 for (auto const& funStructure : automatonStructure.at("functions")) {
1606 // Skip parsing of function body
1607 dummyFunctionDefinitions.push_back(
1608 parseFunctionDefinition(funStructure, scope.refine("functions[" + std::to_string(localFuns.size()) + "] of automaton " + name), true));
1609 }
1610 // Store references to the dummy function definitions. This needs to happen in a separate loop since otherwise, references to FunDefs can be invalidated
1611 // after calling dummyFunctionDefinitions.push_back
1612 for (auto const& funDef : dummyFunctionDefinitions) {
1613 bool unused = localFuns.emplace(funDef.getName(), &funDef).second;
1614 STORM_LOG_THROW(unused, storm::exceptions::InvalidJaniException,
1615 "Multiple definitions of functions with the name " << funDef.getName() << " in " << scope.description << ".");
1616 }
1617 for (auto const& funStructure : automatonStructure.at("functions")) {
1618 // Actually parse the function body
1620 parseFunctionDefinition(funStructure, scope.refine("functions[" + std::to_string(localFuns.size()) + "] of automaton " + name), false,
1622 STORM_LOG_ASSERT(localFuns.count(funDef.getName()) == 1, "Local function not found.");
1623 localFuns[funDef.getName()] = &automaton.addFunctionDefinition(funDef);
1624 }
1625 }
1626
1627 STORM_LOG_THROW(automatonStructure.count("locations") > 0, storm::exceptions::InvalidJaniException, "Automaton '" << name << "' does not have locations.");
1628 std::unordered_map<std::string, uint64_t> locIds;
1629 for (auto const& locEntry : automatonStructure.at("locations")) {
1630 STORM_LOG_THROW(locEntry.count("name") == 1, storm::exceptions::InvalidJaniException,
1631 "Locations for automaton '" << name << "' must have exactly one name.");
1632 std::string locName = getString<ValueType>(locEntry.at("name"), "location of automaton " + name);
1633 STORM_LOG_THROW(locIds.count(locName) == 0, storm::exceptions::InvalidJaniException,
1634 "Location with name '" + locName + "' already exists in automaton '" + name + "'.");
1635 STORM_LOG_THROW(locEntry.count("invariant") == 0, storm::exceptions::InvalidJaniException,
1636 "Invariants in locations as in '" + locName + "' in automaton '" + name + "' are not supported.");
1637 // STORM_LOG_THROW(locEntry.count("invariant") > 0 && !supportsInvariants(parentModel.getModelType()), storm::exceptions::InvalidJaniException,
1638 // "Invariants are not supported in the model type " + to_string(parentModel.getModelType()));
1639 std::vector<storm::jani::Assignment> transientAssignments;
1640 if (locEntry.count("transient-values") > 0) {
1641 for (auto const& transientValueEntry : locEntry.at("transient-values")) {
1642 STORM_LOG_THROW(transientValueEntry.count("ref") == 1, storm::exceptions::InvalidJaniException,
1643 "Transient values in location " << locName << " need exactly one ref that is assigned to.");
1644 STORM_LOG_THROW(transientValueEntry.count("value") == 1, storm::exceptions::InvalidJaniException,
1645 "Transient values in location " << locName << " need exactly one assigned value.");
1646 storm::jani::LValue lValue = parseLValue(transientValueEntry.at("ref"), scope.refine("LHS of assignment in location " + locName));
1647 STORM_LOG_THROW(lValue.isTransient(), storm::exceptions::InvalidJaniException,
1648 "Assigned non-transient variable " << lValue << " in location " + locName + " (automaton: '" + name + "').");
1650 parseExpression(transientValueEntry.at("value"), scope.refine("Assignment of lValue in location " + locName));
1651 transientAssignments.emplace_back(lValue, rhs);
1652 }
1653 }
1654 uint64_t id = automaton.addLocation(storm::jani::Location(locName, transientAssignments));
1655 locIds.emplace(locName, id);
1656 }
1657 STORM_LOG_THROW(automatonStructure.count("initial-locations") == 1, storm::exceptions::InvalidJaniException,
1658 "Automaton '" << name << "' does not have initial locations.");
1659 for (Json const& initLocStruct : automatonStructure.at("initial-locations")) {
1660 automaton.addInitialLocation(getString<ValueType>(initLocStruct, "Initial locations for automaton '" + name + "'."));
1661 }
1662 STORM_LOG_THROW(automatonStructure.count("restrict-initial") < 2, storm::exceptions::InvalidJaniException,
1663 "Automaton '" << name << "' has multiple initial value restrictions.");
1664 storm::expressions::Expression initialValueRestriction = expressionManager->boolean(true);
1665 if (automatonStructure.count("restrict-initial") > 0) {
1666 STORM_LOG_THROW(automatonStructure.at("restrict-initial").count("exp") == 1, storm::exceptions::InvalidJaniException,
1667 "Automaton '" << name << "' needs an expression inside the initial restricion.");
1668 initialValueRestriction = parseExpression(automatonStructure.at("restrict-initial").at("exp"), scope.refine("Initial value restriction"));
1669 }
1670 automaton.setInitialStatesRestriction(initialValueRestriction);
1671
1672 STORM_LOG_THROW(automatonStructure.count("edges") > 0, storm::exceptions::InvalidJaniException, "Automaton '" << name << "' must have a list of edges.");
1673 for (auto const& edgeEntry : automatonStructure.at("edges")) {
1674 // source location
1675 STORM_LOG_THROW(edgeEntry.count("location") == 1, storm::exceptions::InvalidJaniException,
1676 "Each edge in automaton '" << name << "' must have a source.");
1677 std::string sourceLoc = getString<ValueType>(edgeEntry.at("location"), "source location for edge in automaton '" + name + "'");
1678 STORM_LOG_THROW(locIds.count(sourceLoc) == 1, storm::exceptions::InvalidJaniException,
1679 "Source of edge has unknown location '" << sourceLoc << "' in automaton '" << name << "'.");
1680 // action
1681 STORM_LOG_THROW(edgeEntry.count("action") < 2, storm::exceptions::InvalidJaniException,
1682 "Edge from " << sourceLoc << " in automaton " << name << " has multiple actions.");
1683 std::string action = storm::jani::Model::SILENT_ACTION_NAME; // def is tau
1684 if (edgeEntry.count("action") > 0) {
1685 action = getString<ValueType>(edgeEntry.at("action"), "action name in edge from '" + sourceLoc + "' in automaton '" + name + "'");
1686 // TODO check if action is known
1687 STORM_LOG_ASSERT(action != "", "Action is empty.");
1688 }
1689 // rate
1690 STORM_LOG_THROW(edgeEntry.count("rate") < 2, storm::exceptions::InvalidJaniException,
1691 "Edge from '" << sourceLoc << "' in automaton '" << name << "' has multiple rates.");
1693 if (edgeEntry.count("rate") > 0) {
1694 STORM_LOG_THROW(edgeEntry.at("rate").count("exp") == 1, storm::exceptions::InvalidJaniException,
1695 "Rate in edge from '" << sourceLoc << "' in automaton '" << name << "' must have a defing expression.");
1696 rateExpr = parseExpression(edgeEntry.at("rate").at("exp"), scope.refine("rate expression in edge from '" + sourceLoc));
1697 STORM_LOG_THROW(rateExpr.hasNumericalType(), storm::exceptions::InvalidJaniException, "Rate '" << rateExpr << "' has not a numerical type.");
1699 storm::exceptions::InvalidJaniException, "Only positive rates are allowed but rate '" << rateExpr << " was found.");
1700 }
1701 // guard
1702 STORM_LOG_THROW(edgeEntry.count("guard") <= 1, storm::exceptions::InvalidJaniException,
1703 "Guard can be given at most once in edge from '" << sourceLoc << "' in automaton '" << name << "'.");
1704 storm::expressions::Expression guardExpr = expressionManager->boolean(true);
1705 if (edgeEntry.count("guard") == 1) {
1706 STORM_LOG_THROW(edgeEntry.at("guard").count("exp") == 1, storm::exceptions::InvalidJaniException,
1707 "Guard in edge from '" + sourceLoc + "' in automaton '" + name + "' must have one expression.");
1708 guardExpr = parseExpression(edgeEntry.at("guard").at("exp"), scope.refine("guard expression in edge from '" + sourceLoc));
1709 STORM_LOG_THROW(guardExpr.hasBooleanType(), storm::exceptions::InvalidJaniException, "Guard " << guardExpr << " does not have Boolean type.");
1710 }
1711 STORM_LOG_ASSERT(guardExpr.isInitialized(), "Guard expression not initialized.");
1712 std::shared_ptr<storm::jani::TemplateEdge> templateEdge = std::make_shared<storm::jani::TemplateEdge>(guardExpr);
1713
1714 // edge assignments
1715 if (edgeEntry.count("assignments") > 0) {
1716 STORM_LOG_THROW(edgeEntry.count("assignments") == 1, storm::exceptions::InvalidJaniException,
1717 "Multiple edge assignments in edge from '" + sourceLoc + "' in automaton '" + name + "'.");
1718 for (auto const& assignmentEntry : edgeEntry.at("assignments")) {
1719 // ref
1720 STORM_LOG_THROW(assignmentEntry.count("ref") == 1, storm::exceptions::InvalidJaniException,
1721 "Assignment in edge from '" << sourceLoc << "' in automaton '" << name << "'must have one ref field.");
1722 storm::jani::LValue lValue =
1723 parseLValue(assignmentEntry.at("ref"), scope.refine("Assignment variable in edge from '" + sourceLoc + "' in automaton '" + name + "'"));
1724 // value
1725 STORM_LOG_THROW(assignmentEntry.count("value") == 1, storm::exceptions::InvalidJaniException,
1726 "Assignment in edge from '" << sourceLoc << "' in automaton '" << name << "' must have one value field.");
1727 storm::expressions::Expression assignmentExpr =
1728 parseExpression(assignmentEntry.at("value"), scope.refine("assignment in edge from '" + sourceLoc + "' in automaton '" + name + "'"));
1729 // TODO check types
1730 // index
1731 int64_t assignmentIndex = 0; // default.
1732 if (assignmentEntry.count("index") > 0) {
1733 assignmentIndex =
1734 getSignedInt<ValueType>(assignmentEntry.at("index"), "assignment index in edge from '" + sourceLoc + "' in automaton '" + name + "'");
1735 }
1736 templateEdge->getAssignments().add(storm::jani::Assignment(lValue, assignmentExpr, assignmentIndex));
1737 }
1738 }
1739
1740 // destinations
1741 STORM_LOG_THROW(edgeEntry.count("destinations") == 1, storm::exceptions::InvalidJaniException,
1742 "A single list of destinations must be given in edge from '" << sourceLoc << "' in automaton '" << name << "'.");
1743 std::vector<std::pair<uint64_t, storm::expressions::Expression>> destinationLocationsAndProbabilities;
1744 for (auto const& destEntry : edgeEntry.at("destinations")) {
1745 // target location
1746 STORM_LOG_THROW(destEntry.count("location") == 1, storm::exceptions::InvalidJaniException,
1747 "Each destination in edge from '" << sourceLoc << "' in automaton '" << name << "' must have a target location.");
1748 std::string targetLoc =
1749 getString<ValueType>(destEntry.at("location"), "target location for edge from '" + sourceLoc + "' in automaton '" + name + "'");
1750 STORM_LOG_THROW(locIds.count(targetLoc) == 1, storm::exceptions::InvalidJaniException,
1751 "Target of edge has unknown location '" << targetLoc << "' in automaton '" << name << "'.");
1752 // probability
1754 unsigned probDeclCount = destEntry.count("probability");
1755 STORM_LOG_THROW(probDeclCount < 2, storm::exceptions::InvalidJaniException,
1756 "Destination in edge from '" << sourceLoc << "' to '" << targetLoc << "' in automaton '" << name << "' has multiple probabilites.");
1757 if (probDeclCount == 0) {
1758 probExpr = expressionManager->rational(1.0);
1759 } else {
1760 STORM_LOG_THROW(destEntry.at("probability").count("exp") == 1, storm::exceptions::InvalidJaniException,
1761 "Destination in edge from '" << sourceLoc << "' to '" << targetLoc << "' in automaton '" << name
1762 << "' must have a probability expression.");
1763 probExpr = parseExpression(destEntry.at("probability").at("exp"), scope.refine("probability expression in edge from '" + sourceLoc + "' to '" +
1764 targetLoc + "' in automaton '" + name + "'"));
1765 }
1766 STORM_LOG_ASSERT(probExpr.isInitialized(), "Probability expression not initialized.");
1767 STORM_LOG_THROW(probExpr.hasNumericalType(), storm::exceptions::InvalidJaniException,
1768 "Probability expression " << probExpr << " does not have a numerical type.");
1769 // assignments
1770 std::vector<storm::jani::Assignment> assignments;
1771 unsigned assignmentDeclCount = destEntry.count("assignments");
1773 assignmentDeclCount < 2, storm::exceptions::InvalidJaniException,
1774 "Destination in edge from '" << sourceLoc << "' to '" << targetLoc << "' in automaton '" << name << "' has multiple assignment lists.");
1775 if (assignmentDeclCount > 0) {
1776 for (auto const& assignmentEntry : destEntry.at("assignments")) {
1777 // ref
1779 assignmentEntry.count("ref") == 1, storm::exceptions::InvalidJaniException,
1780 "Assignment in edge from '" << sourceLoc << "' to '" << targetLoc << "' in automaton '" << name << "' must have one ref field.");
1781 storm::jani::LValue lValue = parseLValue(assignmentEntry.at("ref"), scope.refine("Assignment variable in edge from '" + sourceLoc +
1782 "' to '" + targetLoc + "' in automaton '" + name + "'"));
1783 // value
1785 assignmentEntry.count("value") == 1, storm::exceptions::InvalidJaniException,
1786 "Assignment in edge from '" << sourceLoc << "' to '" << targetLoc << "' in automaton '" << name << "' must have one value field.");
1787 storm::expressions::Expression assignmentExpr =
1788 parseExpression(assignmentEntry.at("value"),
1789 scope.refine("assignment in edge from '" + sourceLoc + "' to '" + targetLoc + "' in automaton '" + name + "'"));
1790 // TODO check types
1791 // index
1792 int64_t assignmentIndex = 0; // default.
1793 if (assignmentEntry.count("index") > 0) {
1794 assignmentIndex = getSignedInt<ValueType>(assignmentEntry.at("index"), "assignment index in edge from '" + sourceLoc + "' to '" +
1795 targetLoc + "' in automaton '" + name + "'");
1796 }
1797 assignments.emplace_back(lValue, assignmentExpr, assignmentIndex);
1798 }
1799 }
1800 destinationLocationsAndProbabilities.emplace_back(locIds.at(targetLoc), probExpr);
1801 templateEdge->addDestination(storm::jani::TemplateEdgeDestination(assignments));
1802 }
1803 automaton.addEdge(storm::jani::Edge(locIds.at(sourceLoc), parentModel.getActionIndex(action),
1804 rateExpr.isInitialized() ? boost::optional<storm::expressions::Expression>(rateExpr) : boost::none, templateEdge,
1805 destinationLocationsAndProbabilities));
1806 }
1807
1808 return automaton;
1809}
1810
1811template<typename ValueType>
1812std::vector<storm::jani::SynchronizationVector> parseSyncVectors(typename JaniParser<ValueType>::Json const& syncVectorStructure) {
1813 std::vector<storm::jani::SynchronizationVector> syncVectors;
1814 // TODO add error checks
1815 for (auto const& syncEntry : syncVectorStructure) {
1816 std::vector<std::string> inputs;
1817 for (auto const& syncInput : syncEntry.at("synchronise")) {
1818 if (syncInput.is_null()) {
1820 } else {
1821 inputs.push_back(syncInput);
1822 }
1823 }
1824 std::string syncResult;
1825 if (syncEntry.count("result")) {
1826 syncResult = syncEntry.at("result");
1827 } else {
1829 }
1830 syncVectors.emplace_back(inputs, syncResult);
1831 }
1832 return syncVectors;
1833}
1834
1835template<typename ValueType>
1836std::shared_ptr<storm::jani::Composition> JaniParser<ValueType>::parseComposition(Json const& compositionStructure) {
1837 if (compositionStructure.count("automaton")) {
1838 std::set<std::string> inputEnabledActions;
1839 if (compositionStructure.count("input-enable")) {
1840 for (auto const& actionDecl : compositionStructure.at("input-enable")) {
1841 inputEnabledActions.insert(actionDecl.template get<std::string>());
1842 }
1843 }
1844 return std::shared_ptr<storm::jani::AutomatonComposition>(
1845 new storm::jani::AutomatonComposition(compositionStructure.at("automaton").template get<std::string>(), inputEnabledActions));
1846 }
1847
1848 STORM_LOG_THROW(compositionStructure.count("elements") == 1, storm::exceptions::InvalidJaniException,
1849 "Elements of a composition must be given, got " << compositionStructure.dump() << ".");
1850
1851 if (compositionStructure.at("elements").size() == 1 && compositionStructure.count("syncs") == 0) {
1852 // We might have an automaton.
1853 STORM_LOG_THROW(compositionStructure.at("elements").back().count("automaton") == 1, storm::exceptions::InvalidJaniException,
1854 "Automaton must be given in composition.");
1855 if (compositionStructure.at("elements").back().at("automaton").is_string()) {
1856 std::string name = compositionStructure.at("elements").back().at("automaton");
1857 // TODO check whether name exist?
1858 return std::shared_ptr<storm::jani::AutomatonComposition>(new storm::jani::AutomatonComposition(name));
1859 }
1860 STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "Trivial nesting parallel composition is not yet supported.");
1861 }
1862
1863 std::vector<std::shared_ptr<storm::jani::Composition>> compositions;
1864 for (auto const& elemDecl : compositionStructure.at("elements")) {
1865 if (!allowRecursion) {
1866 STORM_LOG_THROW(elemDecl.count("automaton") == 1, storm::exceptions::InvalidJaniException, "Automaton must be given in the element.");
1867 }
1868 compositions.push_back(parseComposition(elemDecl));
1869 }
1870
1871 STORM_LOG_THROW(compositionStructure.count("syncs") < 2, storm::exceptions::InvalidJaniException, "Sync vectors can be given at most once.");
1872 std::vector<storm::jani::SynchronizationVector> syncVectors;
1873 if (compositionStructure.count("syncs") > 0) {
1874 syncVectors = parseSyncVectors<ValueType>(compositionStructure.at("syncs"));
1875 }
1876
1877 return std::shared_ptr<storm::jani::Composition>(new storm::jani::ParallelComposition(compositions, syncVectors));
1878}
1879
1882} // namespace parser
1883} // namespace storm
int_fast64_t evaluateAsInt(Valuation const *valuation=nullptr) const
Evaluates the expression under the valuation of variables given by the valuation and returns the resu...
bool isVariable() const
Retrieves whether the expression is a variable.
bool hasNumericalType() const
Retrieves whether the expression has a numerical return type, i.e., integer or double.
storm::RationalNumber evaluateAsRational() const
Evaluates the expression and returns the resulting rational number.
bool hasBooleanType() const
Retrieves whether the expression has a boolean return type.
bool containsVariables() const
Retrieves whether the expression contains a variable.
std::set< storm::expressions::Variable > getVariables() const
Retrieves the set of all variables that appear in the expression.
std::shared_ptr< BaseExpression const > const & getBaseExpressionPointer() const
Retrieves a pointer to the base expression underlying this expression object.
bool hasIntegerType() const
Retrieves whether the expression has an integral return type.
std::string toString() const
Converts the expression into a string.
Type const & getType() const
Retrieves the type of the expression.
ExpressionManager const & getManager() const
Retrieves the manager responsible for this expression.
bool isInitialized() const
Checks whether the object encapsulates a base-expression.
Expression substitute(Expression const &expression)
Substitutes the identifiers in the given expression according to the previously given map and returns...
Type getElementType() const
Retrieves the element type of the type, provided that it is an Array type.
Definition Type.cpp:230
bool isIntegerType() const
Checks whether this type is an integral type.
Definition Type.cpp:198
bool isNumericalType() const
Checks whether this type is a numerical type.
Definition Type.cpp:206
bool isArrayType() const
Checks whether this type is an array type.
Definition Type.cpp:210
storm::expressions::Expression getExpression() const
Retrieves an expression that represents the variable.
Definition Variable.cpp:34
void addEdge(Edge const &edge)
Adds an edge to the automaton.
void setInitialStatesRestriction(storm::expressions::Expression const &initialStatesRestriction)
Sets the expression restricting the legal initial values of the automaton's variables.
Variable const & addVariable(Variable const &variable)
Adds the given variable to this automaton.
Definition Automaton.cpp:51
FunctionDefinition const & addFunctionDefinition(FunctionDefinition const &functionDefinition)
Adds the given function definition.
Definition Automaton.cpp:79
void addInitialLocation(std::string const &name)
Adds the location with the given name to the initial locations.
uint64_t addLocation(Location const &location)
Adds the given location to the automaton.
std::string const & getName() const
Retrieves the name of the function.
storm::expressions::Type const & getType() const
Retrieves the type of the function.
bool isTransient() const
Definition LValue.cpp:70
Jani Location:
Definition Location.h:15
bool hasMultiObjectiveProperties() const
ModelFeatures & add(ModelFeature const &modelFeature)
void setInitialStatesRestriction(storm::expressions::Expression const &initialStatesRestriction)
Sets the expression restricting the legal initial values of the global variables.
Definition Model.cpp:1291
bool addNonTrivialRewardExpression(std::string const &identifier, storm::expressions::Expression const &rewardExpression)
Adds a reward expression, i.e., a reward model that does not consist of a single, global,...
Definition Model.cpp:797
void setSystemComposition(std::shared_ptr< Composition > const &composition)
Sets the system composition expression of the JANI model.
Definition Model.cpp:1014
std::vector< Constant > const & getConstants() const
Retrieves the constants of the model.
Definition Model.cpp:685
void addConstant(Constant const &constant)
Adds the given constant to the model.
Definition Model.cpp:650
uint64_t getJaniVersion() const
Retrieves the JANI-version of the model.
Definition Model.cpp:113
Variable const & addVariable(Variable const &variable)
Adds the given variable to this model.
Definition Model.cpp:713
FunctionDefinition const & addFunctionDefinition(FunctionDefinition const &functionDefinition)
Adds the given function definition.
Definition Model.cpp:770
static const std::string SILENT_ACTION_NAME
The name of the silent action.
Definition Model.h:655
uint64_t addAction(Action const &action)
Adds an action to the model.
Definition Model.cpp:613
std::size_t getNumberOfAutomata() const
Retrieves the number of automata in this model.
Definition Model.cpp:910
bool isDiscreteTimeModel() const
Determines whether this model is a discrete-time model.
Definition Model.cpp:1384
ModelFeatures const & getModelFeatures() const
Retrieves the enabled model features.
Definition Model.cpp:125
uint64_t addAutomaton(Automaton const &automaton)
Adds the given automaton to the automata of this model.
Definition Model.cpp:859
uint64_t getActionIndex(std::string const &name) const
Get the index of the action.
Definition Model.cpp:632
void finalize()
After adding all components to the model, this method has to be called.
Definition Model.cpp:1410
static const std::string NO_ACTION_INPUT
static std::shared_ptr< Variable > makeVariable(std::string const &name, JaniType const &type, storm::expressions::Variable const &variable, boost::optional< storm::expressions::Expression > const &initValue, bool transient)
Convenience functions to call the appropriate constructor and return a shared pointer to the variable...
Definition Variable.cpp:95
static std::shared_ptr< Formula const > getTrueFormula()
Definition Formula.cpp:227
std::shared_ptr< Formula > eliminateRewardAccumulations(Formula const &f) const
Eliminates any reward accumulations of the formula, where the presence of the reward accumulation doe...
storm::jani::Property parseProperty(storm::jani::Model &model, storm::json< ValueType > const &propertyStructure, Scope const &scope)
storm::json< ValueType > Json
Definition JaniParser.h:40
static std::pair< storm::jani::Model, std::vector< storm::jani::Property > > parseFromString(std::string const &jsonstring, bool parseProperties=true)
static std::pair< storm::jani::Model, std::vector< storm::jani::Property > > parse(std::string const &path, bool parseProperties=true)
void readFile(std::string const &path)
std::pair< std::unique_ptr< storm::jani::JaniType >, storm::expressions::Type > parseType(storm::json< ValueType > const &typeStructure, std::string variableName, Scope const &scope)
std::pair< storm::jani::Model, std::vector< storm::jani::Property > > parseModel(bool parseProperties=true)
std::unordered_map< std::string, storm::jani::Variable const * > VariablesMap
Definition JaniParser.h:37
std::unordered_map< std::string, storm::jani::Constant const * > ConstantsMap
Definition JaniParser.h:38
std::unordered_map< std::string, storm::jani::FunctionDefinition const * > FunctionsMap
Definition JaniParser.h:39
std::shared_ptr< storm::jani::Variable > parseVariable(storm::json< ValueType > const &variableStructure, Scope const &scope, std::string const &namePrefix="")
storm::expressions::Expression parseExpression(storm::json< ValueType > const &expressionStructure, Scope const &scope, bool returnNoneOnUnknownOpString=false, std::unordered_map< std::string, storm::expressions::Variable > const &auxiliaryVariables={})
storm::jani::Automaton parseAutomaton(storm::json< ValueType > const &automatonStructure, storm::jani::Model const &parentModel, Scope const &scope)
storm::jani::LValue parseLValue(storm::json< ValueType > const &lValueStructure, Scope const &scope)
#define STORM_LOG_WARN(message)
Definition logging.h:35
#define STORM_LOG_TRACE(message)
Definition logging.h:22
#define STORM_LOG_THROW_UNCONDITIONALLY(exception, message)
Definition macros.h:48
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_WARN_COND(cond, message)
Definition macros.h:54
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:39
Expression maximum(Expression const &first, Expression const &second)
Expression ceil(Expression const &first)
Expression ite(Expression const &condition, Expression const &thenExpression, Expression const &elseExpression)
Expression iff(Expression const &first, Expression const &second)
Expression abs(Expression const &first)
Expression pow(Expression const &base, Expression const &exponent, bool allowIntegerType)
The type of the resulting expression is.
Expression minimum(Expression const &first, Expression const &second)
Expression floor(Expression const &first)
Expression sin(Expression const &first)
Expression sign(Expression const &first)
Expression truncate(Expression const &first)
Expression xclusiveor(Expression const &first, Expression const &second)
Expression logarithm(Expression const &first, Expression const &second)
Expression cos(Expression const &first)
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
std::string toString(ModelFeature const &modelFeature)
ModelType getModelType(std::string const &input)
Definition ModelType.cpp:42
ModelFeatures getAllKnownModelFeatures()
Contains all file parsers and helper classes.
void insertLowerUpperTimeBounds(std::vector< std::optional< storm::logic::TimeBound > > &lowerBounds, std::vector< std::optional< storm::logic::TimeBound > > &upperBounds, storm::jani::PropertyInterval const &pi)
void ensureNumberOfArguments(uint64_t expected, uint64_t actual, std::string const &opstring, std::string const &errorInfo)
Helper for parse expression.
std::string getString(typename JaniParser< ValueType >::Json const &structure, std::string const &errorInfo)
std::vector< storm::jani::SynchronizationVector > parseSyncVectors(typename JaniParser< ValueType >::Json const &syncVectorStructure)
std::shared_ptr< storm::logic::Formula const > makeUnaryBooleanFormula(storm::logic::UnaryBooleanOperatorType oper, std::shared_ptr< storm::logic::Formula const > const &sub, storm::logic::FormulaContext context)
int64_t getSignedInt(typename JaniParser< ValueType >::Json const &structure, std::string const &errorInfo)
bool getBoolean(typename JaniParser< ValueType >::Json const &structure, std::string const &errorInfo)
void ensureArrayType(storm::expressions::Expression const &expr, std::string const &opstring, unsigned argNr, std::string const &errorInfo)
Helper for parse expression.
void ensureBooleanType(storm::expressions::Expression const &expr, std::string const &opstring, unsigned argNr, std::string const &errorInfo)
Helper for parse expression.
std::shared_ptr< storm::logic::Formula const > makeBinaryBooleanFormula(storm::logic::BinaryBooleanOperatorType oper, std::shared_ptr< storm::logic::Formula const > const &left, std::shared_ptr< storm::logic::Formula const > const &right, storm::logic::FormulaContext context)
void ensureIntegerType(storm::expressions::Expression const &expr, std::string const &opstring, unsigned argNr, std::string const &errorInfo)
Helper for parse expression.
const std::string VARIABLE_AUTOMATON_DELIMITER
uint64_t getUnsignedInt(typename JaniParser< ValueType >::Json const &structure, std::string const &errorInfo)
void ensureNumericalType(storm::expressions::Expression const &expr, std::string const &opstring, unsigned argNr, std::string const &errorInfo)
Helper for parse expression.
bool isOne(ValueType const &a)
Definition constants.cpp:39
bool isZero(ValueType const &a)
Definition constants.cpp:44
ValueType zero()
Definition constants.cpp:26
TargetType convertNumber(SourceType const &number)
#define STORM_PARSERS_API
Property intervals as per Jani Specification.
Definition Property.h:21
storm::expressions::Expression upperBound
Definition Property.h:24
storm::expressions::Expression lowerBound
Definition Property.h:22
boost::optional< Bound > bound
boost::optional< storm::solver::OptimizationDirection > optimalityType
FunctionsMap const * globalFunctions
Definition JaniParser.h:64
VariablesMap const * localVars
Definition JaniParser.h:65
FunctionsMap const * localFunctions
Definition JaniParser.h:66
VariablesMap const * globalVars
Definition JaniParser.h:63
Scope refine(std::string const &prependedDescription="") const
Definition JaniParser.h:67
ConstantsMap const * constants
Definition JaniParser.h:62