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