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