33#include <boost/lexical_cast.hpp>
47template<
typename ValueType>
48const bool JaniParser<ValueType>::defaultVariableTransient =
false;
50template<
typename ValueType>
52 "acot",
"asec",
"acsc",
"sinh",
"cosh",
"tanh",
"coth",
53 "sech",
"csch",
"asinh",
"acosh",
"atanh",
"asinh",
"acosh"});
55template<
typename ValueType>
57 STORM_LOG_THROW(structure.is_string(), storm::exceptions::InvalidJaniException,
58 "Expected a string in " << errorInfo <<
", got '" << structure.dump() <<
"'");
59 return structure.front();
62template<
typename ValueType>
64 STORM_LOG_THROW(structure.is_boolean(), storm::exceptions::InvalidJaniException,
65 "Expected a Boolean in " << errorInfo <<
", got " << structure.dump() <<
"'");
66 return structure.front();
69template<
typename ValueType>
71 STORM_LOG_THROW(structure.is_number(), storm::exceptions::InvalidJaniException,
72 "Expected a number in " << errorInfo <<
", got '" << structure.dump() <<
"'");
73 int64_t num = structure.front();
74 STORM_LOG_THROW(num >= 0, storm::exceptions::InvalidJaniException,
"Expected a positive number in " << errorInfo <<
", got '" << num <<
"'");
75 return static_cast<uint64_t
>(num);
78template<
typename ValueType>
80 STORM_LOG_THROW(structure.is_number(), storm::exceptions::InvalidJaniException,
81 "Expected a number in " << errorInfo <<
", got '" << structure.dump() <<
"'");
82 return structure.front();
85template<
typename ValueType>
92template<
typename ValueType>
98template<
typename ValueType>
100 parsedStructure = Json::parse(jsonstring);
103template<
typename ValueType>
107 file >> parsedStructure;
111template<
typename ValueType>
114 STORM_LOG_THROW(parsedStructure.count(
"jani-version") == 1, storm::exceptions::InvalidJaniException,
"Jani-version must be given exactly once.");
115 uint64_t version = getUnsignedInt<ValueType>(parsedStructure.at(
"jani-version"),
"jani version");
116 STORM_LOG_WARN_COND(version >= 1 && version <= 1,
"JANI Version " << version <<
" is not supported. Results may be wrong.");
118 STORM_LOG_THROW(parsedStructure.count(
"name") == 1, storm::exceptions::InvalidJaniException,
"A model must have a (single) name");
119 std::string name = getString<ValueType>(parsedStructure.at(
"name"),
"model name");
121 STORM_LOG_THROW(parsedStructure.count(
"type") == 1, storm::exceptions::InvalidJaniException,
"A type must be given exactly once");
122 std::string modeltypestring = getString<ValueType>(parsedStructure.at(
"type"),
"type of the model");
126 uint_fast64_t featuresCount = parsedStructure.count(
"features");
127 STORM_LOG_THROW(featuresCount < 2, storm::exceptions::InvalidJaniException,
"features-declarations can be given at most once.");
128 if (featuresCount == 1) {
130 for (
auto const& feature : parsedStructure.at(
"features")) {
131 std::string featureStr = getString<ValueType>(feature,
"Model feature");
133 for (
auto const& knownFeature : allKnownModelFeatures.asSet()) {
140 STORM_LOG_THROW(found, storm::exceptions::NotSupportedException,
"Storm does not support the model feature " << featureStr);
143 uint_fast64_t actionCount = parsedStructure.count(
"actions");
144 STORM_LOG_THROW(actionCount < 2, storm::exceptions::InvalidJaniException,
"Action-declarations can be given at most once.");
145 if (actionCount > 0) {
146 parseActions(parsedStructure.at(
"actions"), model);
154 uint_fast64_t constantsCount = parsedStructure.count(
"constants");
155 STORM_LOG_THROW(constantsCount < 2, storm::exceptions::InvalidJaniException,
"Constant-declarations can be given at most once.");
156 if (constantsCount == 1) {
158 model.
getConstants().reserve(parsedStructure.at(
"constants").size());
159 for (
auto const& constStructure : parsedStructure.at(
"constants")) {
160 std::shared_ptr<storm::jani::Constant> constant =
161 parseConstant(constStructure, scope.
refine(
"constants[" + std::to_string(constants.size()) +
"]"));
163 assert(model.
getConstants().back().getName() == constant->getName());
164 constants.emplace(constant->getName(), &model.
getConstants().back());
169 uint_fast64_t variablesCount = parsedStructure.count(
"variables");
170 STORM_LOG_THROW(variablesCount < 2, storm::exceptions::InvalidJaniException,
"Variable-declarations can be given at most once for global variables.");
173 if (variablesCount == 1) {
174 for (
auto const& varStructure : parsedStructure.at(
"variables")) {
175 std::shared_ptr<storm::jani::Variable> variable = parseVariable(varStructure, scope.
refine(
"variables[" + std::to_string(globalVars.size())));
176 globalVars.emplace(variable->getName(), &model.
addVariable(*variable));
180 uint64_t funDeclCount = parsedStructure.count(
"functions");
181 STORM_LOG_THROW(funDeclCount < 2, storm::exceptions::InvalidJaniException,
"Model '" << name <<
"' has more than one list of functions");
184 if (funDeclCount > 0) {
186 std::vector<storm::jani::FunctionDefinition> dummyFunctionDefinitions;
187 for (
auto const& funStructure : parsedStructure.at(
"functions")) {
189 dummyFunctionDefinitions.push_back(
190 parseFunctionDefinition(funStructure, scope.
refine(
"functions[" + std::to_string(globalFuns.size()) +
"] of model " + name),
true));
194 for (
auto const& funDef : dummyFunctionDefinitions) {
195 bool unused = globalFuns.emplace(funDef.getName(), &funDef).second;
197 "Multiple definitions of functions with the name " << funDef.getName() <<
" in " << scope.
description);
199 for (
auto const& funStructure : parsedStructure.at(
"functions")) {
202 parseFunctionDefinition(funStructure, scope.
refine(
"functions[" + std::to_string(globalFuns.size()) +
"] of model " + name),
false);
203 assert(globalFuns.count(funDef.
getName()) == 1);
209 STORM_LOG_THROW(parsedStructure.count(
"automata") == 1, storm::exceptions::InvalidJaniException,
"Exactly one list of automata must be given");
210 STORM_LOG_THROW(parsedStructure.at(
"automata").is_array(), storm::exceptions::InvalidJaniException,
"Automata must be an array");
212 for (
auto const& automataEntry : parsedStructure.at(
"automata")) {
215 STORM_LOG_THROW(parsedStructure.count(
"restrict-initial") < 2, storm::exceptions::InvalidJaniException,
"Model has multiple initial value restrictions");
217 if (parsedStructure.count(
"restrict-initial") > 0) {
218 STORM_LOG_THROW(parsedStructure.at(
"restrict-initial").count(
"exp") == 1, storm::exceptions::InvalidJaniException,
219 "Model needs an expression inside the initial restricion");
220 initialValueRestriction = parseExpression(parsedStructure.at(
"restrict-initial").at(
"exp"), scope.
refine(
"Initial value restriction"));
223 STORM_LOG_THROW(parsedStructure.count(
"system") == 1, storm::exceptions::InvalidJaniException,
"Exactly one system description must be given");
224 std::shared_ptr<storm::jani::Composition> composition = parseComposition(parsedStructure.at(
"system"));
230 STORM_LOG_THROW(parsedStructure.count(
"properties") <= 1, storm::exceptions::InvalidJaniException,
"At most one list of properties can be given");
231 std::vector<storm::jani::Property> properties;
232 if (parseProperties && parsedStructure.count(
"properties") == 1) {
233 STORM_LOG_THROW(parsedStructure.at(
"properties").is_array(), storm::exceptions::InvalidJaniException,
"Properties should be an array");
234 for (
auto const& propertyEntry : parsedStructure.at(
"properties")) {
236 auto prop = this->parseProperty(model, propertyEntry, scope.
refine(
"property[" + std::to_string(properties.size()) +
"]"));
239 properties.push_back(prop);
240 }
catch (storm::exceptions::NotSupportedException
const& ex) {
242 }
catch (storm::exceptions::NotImplementedException
const& ex) {
247 return {model, properties};
250template<
typename ValueType>
252 Json
const& propertyStructure,
254 std::string
const& opstring, Scope
const& scope) {
255 STORM_LOG_THROW(propertyStructure.count(
"exp") == 1, storm::exceptions::InvalidJaniException,
256 "Expecting operand for operator " << opstring <<
" in " << scope.description);
257 return {parseFormula(model, propertyStructure.at(
"exp"), formulaContext, scope.refine(
"Operand of operator " + opstring))};
260template<
typename ValueType>
261std::vector<std::shared_ptr<storm::logic::Formula const>> JaniParser<ValueType>::parseBinaryFormulaArguments(
storm::jani::Model& model,
262 Json
const& propertyStructure,
264 std::string
const& opstring, Scope
const& scope) {
265 STORM_LOG_THROW(propertyStructure.count(
"left") == 1, storm::exceptions::InvalidJaniException,
266 "Expecting left operand for operator " << opstring <<
" in " << scope.description);
267 STORM_LOG_THROW(propertyStructure.count(
"right") == 1, storm::exceptions::InvalidJaniException,
268 "Expecting right operand for operator " << opstring <<
" in " << scope.description);
269 return {parseFormula(model, propertyStructure.at(
"left"), formulaContext, scope.refine(
"Operand of operator " + opstring)),
270 parseFormula(model, propertyStructure.at(
"right"), formulaContext, scope.refine(
"Operand of operator " + opstring))};
273template<
typename ValueType>
276 if (piStructure.count(
"lower") > 0) {
277 pi.
lowerBound = parseExpression(piStructure.at(
"lower"), scope.refine(
"Lower bound for property interval"));
279 if (piStructure.count(
"lower-exclusive") > 0) {
283 if (piStructure.count(
"upper") > 0) {
284 pi.
upperBound = parseExpression(piStructure.at(
"upper"), scope.refine(
"Upper bound for property interval"));
286 if (piStructure.count(
"upper-exclusive") > 0) {
291 "Bounded operator must have a bounded interval, but no bounds found in '" << piStructure <<
"'");
295template<
typename ValueType>
297 bool accTime =
false;
298 bool accSteps =
false;
299 bool accExit =
false;
300 STORM_LOG_THROW(accStructure.is_array(), storm::exceptions::InvalidJaniException,
"Accumulate should be an array");
301 for (
auto const& accEntry : accStructure) {
302 if (accEntry ==
"steps") {
304 }
else if (accEntry ==
"time") {
306 }
else if (accEntry ==
"exit") {
310 "One may only accumulate either 'steps' or 'time' or 'exit', got " << accEntry.dump() <<
" in " << context);
321 lowerBounds.push_back(boost::none);
326 upperBounds.push_back(boost::none);
330template<
typename ValueType>
331std::shared_ptr<storm::logic::Formula const> JaniParser<ValueType>::parseFormula(
storm::jani::Model& model, Json
const& propertyStructure,
333 boost::optional<storm::logic::Bound> bound) {
334 if (propertyStructure.is_boolean()) {
335 return std::make_shared<storm::logic::BooleanLiteralFormula>(propertyStructure.template get<bool>());
337 if (propertyStructure.is_string()) {
338 if (labels.count(propertyStructure.template get<std::string>()) > 0) {
339 return std::make_shared<storm::logic::AtomicLabelFormula>(propertyStructure.template get<std::string>());
344 bool exprContainsLabel =
false;
346 for (
auto const& varInExpr : varsInExpr) {
347 if (labels.count(varInExpr.getName()) > 0) {
348 exprContainsLabel =
true;
352 if (!exprContainsLabel) {
353 assert(bound == boost::none);
354 return std::make_shared<storm::logic::AtomicExpressionFormula>(expr);
357 if (propertyStructure.count(
"op") == 1) {
358 std::string opString = getString<ValueType>(propertyStructure.at(
"op"),
"Operation description");
360 if (opString ==
"Pmin" || opString ==
"Pmax") {
361 std::vector<std::shared_ptr<storm::logic::Formula const>> args =
363 assert(args.size() == 1);
365 opInfo.
optimalityType = opString ==
"Pmin" ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize;
366 opInfo.
bound = bound;
367 return std::make_shared<storm::logic::ProbabilityOperatorFormula>(args[0], opInfo);
369 }
else if (opString ==
"∀" || opString ==
"∃") {
370 assert(bound == boost::none);
371 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"Forall and Exists are currently not supported in " << scope.description);
372 }
else if (opString ==
"Emin" || opString ==
"Emax") {
374 STORM_LOG_THROW(propertyStructure.count(
"exp") == 1, storm::exceptions::InvalidJaniException,
375 "Expecting reward-expression for operator " << opString <<
" in " << scope.description);
378 "Reward expression '" << rewExpr <<
"' does not have numerical type in " << scope.description);
379 std::string rewardName = rewExpr.
toString();
382 opInfo.
optimalityType = opString ==
"Emin" ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize;
383 opInfo.
bound = bound;
386 if (propertyStructure.count(
"accumulate") > 0) {
387 rewardAccumulation = parseRewardAccumulation(propertyStructure.at(
"accumulate"), scope.description);
391 if (propertyStructure.count(
"step-instant") > 0) {
392 STORM_LOG_THROW(propertyStructure.count(
"time-instant") == 0, storm::exceptions::NotSupportedException,
393 "Storm does not support to have a step-instant and a time-instant in " + scope.description);
394 STORM_LOG_THROW(propertyStructure.count(
"reward-instants") == 0, storm::exceptions::NotSupportedException,
395 "Storm does not support to have a step-instant and a reward-instant in " + scope.description);
401 if (rewardAccumulation.isEmpty()) {
402 return std::make_shared<storm::logic::RewardOperatorFormula>(
405 return std::make_shared<storm::logic::RewardOperatorFormula>(
411 }
else if (propertyStructure.count(
"time-instant") > 0) {
412 STORM_LOG_THROW(propertyStructure.count(
"reward-instants") == 0, storm::exceptions::NotSupportedException,
413 "Storm does not support to have a time-instant and a reward-instant in " + scope.description);
418 if (rewardAccumulation.isEmpty()) {
419 return std::make_shared<storm::logic::RewardOperatorFormula>(
422 return std::make_shared<storm::logic::RewardOperatorFormula>(
428 }
else if (propertyStructure.count(
"reward-instants") > 0) {
429 std::vector<storm::logic::TimeBound> bounds;
430 std::vector<storm::logic::TimeBoundReference> boundReferences;
431 for (
auto const& rewInst : propertyStructure.at(
"reward-instants")) {
433 parseExpression(rewInst.at(
"exp"), scope.refine(
"Reward expression at reward instant"));
435 "Reward expression '" << rewInstRewardModelExpression <<
"' does not have numerical type in " << scope.description);
437 bool steps = (boundRewardAccumulation.
isStepsSet() || boundRewardAccumulation.
isExitSet()) && boundRewardAccumulation.
size() == 1;
443 std::string rewInstRewardModelName = rewInstRewardModelExpression.
toString();
444 if (!rewInstRewardModelExpression.
isVariable()) {
447 boundReferences.emplace_back(rewInstRewardModelName, boundRewardAccumulation);
450 bounds.emplace_back(
false, rewInstantExpr);
455 return std::make_shared<storm::logic::RewardOperatorFormula>(
456 std::make_shared<storm::logic::CumulativeRewardFormula>(bounds, boundReferences, rewardAccumulation), rewardName, opInfo);
459 std::shared_ptr<storm::logic::Formula const> subformula;
460 if (propertyStructure.count(
"reach") > 0) {
462 subformula = std::make_shared<storm::logic::EventuallyFormula>(
463 parseFormula(model, propertyStructure.at(
"reach"), formulaContext, scope.refine(
"Reach-expression of operator " + opString)),
464 formulaContext, rewardAccumulation);
466 subformula = std::make_shared<storm::logic::TotalRewardFormula>(rewardAccumulation);
469 assert(subformula->isTotalRewardFormula() || subformula->isTimePathFormula());
470 return std::make_shared<storm::logic::TimeOperatorFormula>(subformula, opInfo);
475 return std::make_shared<storm::logic::RewardOperatorFormula>(subformula, rewardName, opInfo);
478 }
else if (opString ==
"Smin" || opString ==
"Smax") {
480 opInfo.
optimalityType = opString ==
"Smin" ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize;
481 opInfo.
bound = bound;
483 boost::optional<storm::logic::RewardAccumulation> rewardAccumulation;
484 if (propertyStructure.count(
"accumulate") > 0) {
486 rewardAccumulation = parseRewardAccumulation(propertyStructure.at(
"accumulate"), scope.description);
488 STORM_LOG_THROW(propertyStructure.count(
"exp") > 0, storm::exceptions::InvalidJaniException,
489 "Expected an expression at steady state property at " << scope.description);
490 auto exp = parseExpression(propertyStructure[
"exp"], scope.refine(
"steady-state operator"),
true);
491 if (!exp.isInitialized() || exp.hasBooleanType()) {
492 STORM_LOG_THROW(!rewardAccumulation.is_initialized(), storm::exceptions::InvalidJaniException,
493 "Long-run average probabilities are not allowed to have a reward accumulation at" << scope.description);
494 std::shared_ptr<storm::logic::Formula const> subformula =
495 parseUnaryFormulaArgument(model, propertyStructure, formulaContext, opString, scope.refine(
"Steady-state operator"))[0];
496 return std::make_shared<storm::logic::LongRunAverageOperatorFormula>(subformula, opInfo);
498 STORM_LOG_THROW(exp.hasNumericalType(), storm::exceptions::InvalidJaniException,
499 "Reward expression '" << exp <<
"' does not have numerical type in " << scope.description);
500 std::string rewardName = exp.toString();
501 if (!exp.isVariable()) {
504 auto subformula = std::make_shared<storm::logic::LongRunAverageRewardFormula>(rewardAccumulation);
505 return std::make_shared<storm::logic::RewardOperatorFormula>(subformula, rewardName, opInfo);
507 }
else if (opString ==
"U" || opString ==
"F") {
508 assert(bound == boost::none);
509 std::vector<std::shared_ptr<storm::logic::Formula const>> args;
510 if (opString ==
"U") {
511 args = parseBinaryFormulaArguments(model, propertyStructure, formulaContext, opString, scope);
513 assert(opString ==
"F");
514 args = parseUnaryFormulaArgument(model, propertyStructure, formulaContext, opString, scope);
515 args.push_back(args[0]);
519 std::vector<boost::optional<storm::logic::TimeBound>> lowerBounds, upperBounds;
520 std::vector<storm::logic::TimeBoundReference> tbReferences;
521 if (propertyStructure.count(
"step-bounds") > 0) {
524 parsePropertyInterval(propertyStructure.at(
"step-bounds"), scope.refine(
"step-bounded until").clearVariables());
528 if (propertyStructure.count(
"time-bounds") > 0) {
531 parsePropertyInterval(propertyStructure.at(
"time-bounds"), scope.refine(
"time-bounded until").clearVariables());
535 if (propertyStructure.count(
"reward-bounds") > 0) {
536 for (
auto const& rbStructure : propertyStructure.at(
"reward-bounds")) {
539 STORM_LOG_THROW(rbStructure.count(
"exp") == 1, storm::exceptions::InvalidJaniException,
540 "Expecting reward-expression for operator " << opString <<
" in " << scope.description);
542 parseExpression(rbStructure.at(
"exp"), scope.refine(
"Reward expression at reward-bounds"));
544 "Reward expression '" << rewInstRewardModelExpression <<
"' does not have numerical type in " << scope.description);
546 bool steps = (boundRewardAccumulation.
isStepsSet() || boundRewardAccumulation.
isExitSet()) && boundRewardAccumulation.
size() == 1;
552 std::string rewInstRewardModelName = rewInstRewardModelExpression.
toString();
553 if (!rewInstRewardModelExpression.
isVariable()) {
556 tbReferences.emplace_back(rewInstRewardModelName, boundRewardAccumulation);
560 if (!tbReferences.empty()) {
561 return std::make_shared<storm::logic::BoundedUntilFormula const>(args[0], args[1], lowerBounds, upperBounds, tbReferences);
562 }
else if (args[0]->isTrueFormula()) {
563 return std::make_shared<storm::logic::EventuallyFormula const>(args[1], formulaContext);
565 return std::make_shared<storm::logic::UntilFormula const>(args[0], args[1]);
567 }
else if (opString ==
"G") {
568 assert(bound == boost::none);
569 std::vector<std::shared_ptr<storm::logic::Formula const>> args =
570 parseUnaryFormulaArgument(model, propertyStructure, formulaContext, opString, scope.refine(
"Subformula of globally operator "));
571 if (propertyStructure.count(
"step-bounds") > 0) {
572 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"Globally and step-bounds are not supported.");
573 }
else if (propertyStructure.count(
"time-bounds") > 0) {
574 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"Globally and time bounds are not supported.");
575 }
else if (propertyStructure.count(
"reward-bounds") > 0) {
576 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"Globally and reward bounded properties are not supported.");
578 return std::make_shared<storm::logic::GloballyFormula const>(args[0]);
580 }
else if (opString ==
"W") {
581 assert(bound == boost::none);
582 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"Weak until is not supported");
583 }
else if (opString ==
"R") {
584 assert(bound == boost::none);
585 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"Release is not supported");
586 }
else if (opString ==
"∧" || opString ==
"∨") {
587 assert(bound == boost::none);
588 std::vector<std::shared_ptr<storm::logic::Formula const>> args =
589 parseBinaryFormulaArguments(model, propertyStructure, formulaContext, opString, scope);
590 assert(args.size() == 2);
592 opString ==
"∧" ? storm::logic::BinaryBooleanStateFormula::OperatorType::And : storm::logic::BinaryBooleanStateFormula::OperatorType::Or;
593 return std::make_shared<storm::logic::BinaryBooleanStateFormula const>(oper, args[0], args[1]);
594 }
else if (opString ==
"⇒") {
595 assert(bound == boost::none);
596 std::vector<std::shared_ptr<storm::logic::Formula const>> args =
597 parseBinaryFormulaArguments(model, propertyStructure, formulaContext, opString, scope);
598 assert(args.size() == 2);
599 std::shared_ptr<storm::logic::UnaryBooleanStateFormula const> tmp =
600 std::make_shared<storm::logic::UnaryBooleanStateFormula const>(storm::logic::UnaryBooleanStateFormula::OperatorType::Not, args[0]);
601 return std::make_shared<storm::logic::BinaryBooleanStateFormula const>(storm::logic::BinaryBooleanStateFormula::OperatorType::Or, tmp, args[1]);
602 }
else if (opString ==
"¬") {
603 assert(bound == boost::none);
604 std::vector<std::shared_ptr<storm::logic::Formula const>> args =
605 parseUnaryFormulaArgument(model, propertyStructure, formulaContext, opString, scope);
606 assert(args.size() == 1);
607 return std::make_shared<storm::logic::UnaryBooleanStateFormula const>(storm::logic::UnaryBooleanStateFormula::OperatorType::Not, args[0]);
608 }
else if (!expr.
isInitialized() && (opString ==
"≥" || opString ==
"≤" || opString ==
"<" || opString ==
">" || opString ==
"=" || opString ==
"≠")) {
609 assert(bound == boost::none);
611 if (opString ==
"≥") {
613 }
else if (opString ==
"≤") {
615 }
else if (opString ==
"<") {
617 }
else if (opString ==
">") {
621 std::vector<std::string>
const leftRight = {
"left",
"right"};
622 for (uint64_t i = 0;
i < 2; ++
i) {
623 if (propertyStructure.at(leftRight[i]).count(
"op") > 0) {
624 std::string propertyOperatorString = getString<ValueType>(propertyStructure.at(leftRight[i]).at(
"op"),
"property-operator");
625 std::set<std::string>
const propertyOperatorStrings = {
"Pmin",
"Pmax",
"Emin",
"Emax",
"Smin",
"Smax"};
626 if (propertyOperatorStrings.count(propertyOperatorString) > 0) {
628 parseExpression(propertyStructure.at(leftRight[1 - i]),
629 scope.refine(
"Threshold for operator " + propertyStructure.at(leftRight[i]).at(
"op").template get<std::string>()));
630 if ((opString ==
"=" || opString ==
"≠")) {
631 STORM_LOG_THROW(!boundExpr.containsVariables(), storm::exceptions::NotSupportedException,
632 "Comparison operators '=' or '≠' in property specifications are currently not supported.");
633 auto boundValue = boundExpr.evaluateAsRational();
635 if (opString ==
"=") {
640 }
else if (
storm::utility::isOne(boundValue) && (propertyOperatorString ==
"Pmin" || propertyOperatorString ==
"Pmax")) {
641 if (opString ==
"=") {
648 false, storm::exceptions::NotSupportedException,
649 "Comparison operators '=' or '≠' in property specifications are currently not supported in " << scope.description <<
".");
652 return parseFormula(model, propertyStructure.at(leftRight[i]), formulaContext, scope,
storm::logic::Bound(ct, boundExpr));
656 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"No complex comparisons for properties are supported.");
659 "Non-trivial Expression '" << expr <<
"' contains a boolean transient variable. Can not translate to PRCTL-like formula at "
660 << scope.description <<
".");
662 STORM_LOG_THROW(
false, storm::exceptions::InvalidJaniException,
"Unknown operator " << opString);
666 "Looking for operator for formula " << propertyStructure.dump() <<
", but did not find one");
670template<
typename ValueType>
672 STORM_LOG_THROW(propertyStructure.count(
"name") == 1, storm::exceptions::InvalidJaniException,
"Property must have a name");
674 std::string name = getString<ValueType>(propertyStructure.at(
"name"),
"property-name");
676 std::string comment =
"";
677 if (propertyStructure.count(
"comment") > 0) {
678 comment = getString<ValueType>(propertyStructure.at(
"comment"),
"comment for property named '" + name +
"'.");
680 STORM_LOG_THROW(propertyStructure.count(
"expression") == 1, storm::exceptions::InvalidJaniException,
"Property must have an expression");
682 Json const& expressionStructure = propertyStructure.at(
"expression");
684 STORM_LOG_THROW(expressionStructure.count(
"op") == 1, storm::exceptions::InvalidJaniException,
"Expression in property must have an operation description");
685 STORM_LOG_THROW(expressionStructure.at(
"op") ==
"filter", storm::exceptions::InvalidJaniException,
"Top level operation of a property must be a filter");
686 STORM_LOG_THROW(expressionStructure.count(
"fun") == 1, storm::exceptions::InvalidJaniException,
"Filter must have a function descritpion");
687 std::string funDescr = getString<ValueType>(expressionStructure.at(
"fun"),
"Filter function in property named " + name);
689 if (funDescr ==
"min") {
691 }
else if (funDescr ==
"max") {
693 }
else if (funDescr ==
"sum") {
695 }
else if (funDescr ==
"avg") {
697 }
else if (funDescr ==
"count") {
699 }
else if (funDescr ==
"∀") {
701 }
else if (funDescr ==
"∃") {
703 }
else if (funDescr ==
"argmin") {
705 }
else if (funDescr ==
"argmax") {
707 }
else if (funDescr ==
"values") {
710 STORM_LOG_THROW(
false, storm::exceptions::InvalidJaniException,
"Unknown filter description " << funDescr <<
" in property named " << name);
713 STORM_LOG_THROW(expressionStructure.count(
"states") == 1, storm::exceptions::InvalidJaniException,
"Filter must have a states description");
714 std::shared_ptr<storm::logic::Formula const> statesFormula;
715 if (expressionStructure.at(
"states").count(
"op") > 0) {
716 std::string statesDescr = getString<ValueType>(expressionStructure.at(
"states").at(
"op"),
"Filtered states in property named " + name);
717 if (statesDescr ==
"initial") {
718 statesFormula = std::make_shared<storm::logic::AtomicLabelFormula>(
"init");
721 if (!statesFormula) {
726 }
catch (storm::exceptions::NotSupportedException
const& ex) {
728 }
catch (storm::exceptions::NotImplementedException
const& ex) {
732 STORM_LOG_THROW(statesFormula, storm::exceptions::NotImplementedException,
"Could not derive states formula.");
733 STORM_LOG_THROW(expressionStructure.count(
"values") == 1, storm::exceptions::InvalidJaniException,
"Values as input for a filter must be given");
738template<
typename ValueType>
740 STORM_LOG_THROW(constantStructure.count(
"name") == 1, storm::exceptions::InvalidJaniException,
741 "Variable (scope: " + scope.description +
") must have a name");
742 std::string name = getString<ValueType>(constantStructure.at(
"name"),
"variable-name in " + scope.description +
"-scope");
745 std::string exprManagerName = name;
747 STORM_LOG_THROW(constantStructure.count(
"type") == 1, storm::exceptions::InvalidJaniException,
748 "Constant '" + name +
"' (scope: " + scope.description +
") must have a (single) type-declaration.");
749 auto type = parseType(constantStructure.at(
"type"), name, scope);
750 STORM_LOG_THROW((type.first->isBasicType() || type.first->isBoundedType()), storm::exceptions::InvalidJaniException,
751 "Constant '" + name +
"' (scope: " + scope.description +
") has unexpected type");
753 uint_fast64_t valueCount = constantStructure.count(
"value");
755 STORM_LOG_THROW(valueCount < 2, storm::exceptions::InvalidJaniException,
756 "Value for constant '" + name +
"' (scope: " + scope.description +
") must be given at most once.");
757 if (valueCount == 1) {
760 definingExpression = parseExpression(constantStructure.at(
"value"), scope.refine(
"Value of constant " + name));
764 storm::exceptions::InvalidJaniException,
765 "Type of value for constant '" + name +
"' (scope: " + scope.description +
") does not match the given type '" +
766 type.first->getStringRepresentation() +
".");
772 if (type.first->isBoundedType()) {
773 auto const& bndType = type.first->asBoundedType();
774 if (bndType.hasLowerBound()) {
775 constraintExpression = var.
getExpression() >= bndType.getLowerBound();
776 if (bndType.hasUpperBound()) {
777 constraintExpression = constraintExpression && var.
getExpression() <= bndType.getUpperBound();
779 }
else if (bndType.hasUpperBound()) {
780 constraintExpression = var.
getExpression() <= bndType.getUpperBound();
783 return std::make_shared<storm::jani::Constant>(name, std::move(var), definingExpression, constraintExpression);
786template<
typename ValueType>
788 std::string variableName,
Scope const& scope) {
790 if (typeStructure.is_string()) {
791 if (typeStructure ==
"real") {
793 result.second = expressionManager->getRationalType();
794 }
else if (typeStructure ==
"bool") {
796 result.second = expressionManager->getBooleanType();
797 }
else if (typeStructure ==
"int") {
799 result.second = expressionManager->getIntegerType();
800 }
else if (typeStructure ==
"clock") {
801 result.first = std::make_unique<storm::jani::ClockType>();
802 result.second = expressionManager->getRationalType();
803 }
else if (typeStructure ==
"continuous") {
804 result.first = std::make_unique<storm::jani::ContinuousType>();
805 result.second = expressionManager->getRationalType();
808 "Unsupported type " << typeStructure.dump() <<
" for variable '" << variableName <<
"' (scope: " << scope.
description <<
").");
810 }
else if (typeStructure.is_object()) {
811 STORM_LOG_THROW(typeStructure.count(
"kind") == 1, storm::exceptions::InvalidJaniException,
812 "For complex type as in variable " << variableName <<
"(scope: " << scope.
description <<
") kind must be given");
814 getString<ValueType>(typeStructure.at(
"kind"),
"kind for complex type as in variable " + variableName +
"(scope: " + scope.
description +
") ");
815 if (kind ==
"bounded") {
817 typeStructure.count(
"lower-bound") + typeStructure.count(
"upper-bound") > 0, storm::exceptions::InvalidJaniException,
818 "For bounded type as in variable " << variableName <<
"(scope: " << scope.
description <<
") lower-bound or upper-bound must be given");
820 if (typeStructure.count(
"lower-bound") > 0) {
821 lowerboundExpr = parseExpression(typeStructure.at(
"lower-bound"), scope.
refine(
"Lower bound for variable " + variableName));
824 if (typeStructure.count(
"upper-bound") > 0) {
825 upperboundExpr = parseExpression(typeStructure.at(
"upper-bound"), scope.
refine(
"Upper bound for variable " + variableName));
827 STORM_LOG_THROW(typeStructure.count(
"base") == 1, storm::exceptions::InvalidJaniException,
828 "For bounded type as in variable " << variableName <<
"(scope: " << scope.
description <<
") base must be given");
829 std::string basictype =
830 getString<ValueType>(typeStructure.at(
"base"),
"base for bounded type as in variable " + variableName +
"(scope: " + scope.
description +
") ");
831 if (basictype ==
"int") {
833 "Lower bound for bounded integer variable " << variableName <<
"(scope: " << scope.
description <<
") must be integer-typed");
835 "Upper bound for bounded integer variable " << variableName <<
"(scope: " << scope.
description <<
") must be integer-typed");
839 "Lower bound must not be larger than upper bound for bounded integer variable " << variableName
843 result.second = expressionManager->getIntegerType();
844 }
else if (basictype ==
"real") {
846 "Lower bound for bounded real variable " << variableName <<
"(scope: " << scope.
description <<
") must be numeric");
848 "Upper bound for bounded real variable " << variableName <<
"(scope: " << scope.
description <<
") must be numeric");
851 using SubMap = std::map<storm::expressions::Variable, storm::expressions::Expression>;
855 STORM_LOG_THROW(lowerboundValue <= upperboundValue, storm::exceptions::InvalidJaniException,
856 "Lower bound must not be larger than upper bound for bounded real variable " << variableName
860 result.second = expressionManager->getRationalType();
863 "Unsupported base " << basictype <<
" for bounded variable " << variableName <<
"(scope: " << scope.
description <<
").");
865 }
else if (kind ==
"array") {
866 STORM_LOG_THROW(typeStructure.count(
"base") == 1, storm::exceptions::InvalidJaniException,
867 "For array type as in variable " << variableName <<
"(scope: " << scope.
description <<
") base must be given");
868 auto base = parseType(typeStructure.at(
"base"), variableName, scope);
869 result.first = std::make_unique<storm::jani::ArrayType>(std::move(base.first));
870 result.second = expressionManager->getArrayType(base.second);
873 "Unsupported kind " << kind <<
" for complex type of variable " << variableName <<
"(scope: " << scope.
description <<
").");
879template<
typename ValueType>
881 std::string
const& parameterNamePrefix) {
882 STORM_LOG_THROW(functionDefinitionStructure.count(
"name") == 1, storm::exceptions::InvalidJaniException,
883 "Function definition (scope: " + scope.description +
") must have a name");
884 std::string functionName = getString<ValueType>(functionDefinitionStructure.at(
"name"),
"function-name in " + scope.description);
885 STORM_LOG_THROW(functionDefinitionStructure.count(
"type") == 1, storm::exceptions::InvalidJaniException,
886 "Function definition '" + functionName +
"' (scope: " + scope.description +
") must have a (single) type-declaration.");
887 auto type = parseType(functionDefinitionStructure.at(
"type"), functionName, scope);
889 !(type.first->isClockType() || type.first->isContinuousType()), storm::exceptions::InvalidJaniException,
890 "Function definition '" + functionName +
"' (scope: " + scope.description +
") uses illegal type '" + type.first->getStringRepresentation() +
"'.");
892 std::unordered_map<std::string, storm::expressions::Variable> parameterNameToVariableMap;
893 std::vector<storm::expressions::Variable> parameters;
894 if (!firstPass && functionDefinitionStructure.count(
"parameters") > 0) {
895 STORM_LOG_THROW(functionDefinitionStructure.count(
"parameters") == 1, storm::exceptions::InvalidJaniException,
896 "Function definition '" + functionName +
"' (scope: " + scope.description +
") must have exactly one list of parameters.");
897 for (
auto const& parameterStructure : functionDefinitionStructure.at(
"parameters")) {
898 STORM_LOG_THROW(parameterStructure.count(
"name") == 1, storm::exceptions::InvalidJaniException,
899 "Parameter declaration of parameter " + std::to_string(parameters.size()) +
" of Function definition '" + functionName +
900 "' (scope: " + scope.description +
") must have a name");
901 std::string parameterName =
902 getString<ValueType>(parameterStructure.at(
"name"),
"parameter-name of parameter " + std::to_string(parameters.size()) +
903 " of Function definition '" + functionName +
"' (scope: " + scope.description +
").");
904 STORM_LOG_THROW(parameterStructure.count(
"type") == 1, storm::exceptions::InvalidJaniException,
905 "Parameter declaration of parameter " + std::to_string(parameters.size()) +
" of Function definition '" + functionName +
906 "' (scope: " + scope.description +
") must have exactly one type.");
908 parseType(parameterStructure.at(
"type"), parameterName,
909 scope.refine(
"parameter declaration of parameter " + std::to_string(parameters.size()) +
" of Function definition " + functionName));
910 STORM_LOG_THROW(!(parameterType.first->isClockType() || parameterType.first->isContinuousType()), storm::exceptions::InvalidJaniException,
911 "Type of parameter " + std::to_string(parameters.size()) +
" of function definition '" + functionName +
912 "' (scope: " + scope.description +
") uses illegal type '" + parameterType.first->getStringRepresentation() +
"'.");
914 "Bounds on parameter" + parameterName +
" of function definition " + functionName +
" will be ignored.");
917 parameters.push_back(expressionManager->declareVariable(exprParameterName, parameterType.second));
918 parameterNameToVariableMap.emplace(parameterName, parameters.back());
922 STORM_LOG_THROW(functionDefinitionStructure.count(
"body") == 1, storm::exceptions::InvalidJaniException,
923 "Function definition '" + functionName +
"' (scope: " + scope.description +
") must have a (single) body.");
926 functionBody = parseExpression(functionDefinitionStructure.at(
"body"), scope.refine(
"body of function definition " + functionName),
false,
927 parameterNameToVariableMap);
929 "Type of body of function " + functionName +
"' (scope: " + scope.description +
") has type "
930 << functionBody.
getType() <<
" although the function type is given as " << type.second);
935template<
typename ValueType>
937 STORM_LOG_THROW(variableStructure.count(
"name") == 1, storm::exceptions::InvalidJaniException,
938 "Variable (scope: " + scope.
description +
") must have a name");
939 std::string name = getString<ValueType>(variableStructure.at(
"name"),
"variable-name in " + scope.
description +
"-scope");
942 std::string exprManagerName = namePrefix + name;
943 bool transientVar = defaultVariableTransient;
944 uint_fast64_t tvarcount = variableStructure.count(
"transient");
945 STORM_LOG_THROW(tvarcount <= 1, storm::exceptions::InvalidJaniException,
946 "Multiple definitions of transient not allowed in variable '" + name +
"' (scope: " + scope.
description +
").");
947 if (tvarcount == 1) {
949 getBoolean<ValueType>(variableStructure.at(
"transient"),
"transient-attribute in variable '" + name +
"' (scope: " + scope.
description +
").");
951 STORM_LOG_THROW(variableStructure.count(
"type") == 1, storm::exceptions::InvalidJaniException,
952 "Variable '" + name +
"' (scope: " + scope.
description +
") must have a (single) type-declaration.");
953 auto type = parseType(variableStructure.at(
"type"), name, scope);
955 uint_fast64_t initvalcount = variableStructure.count(
"initial-value");
957 STORM_LOG_THROW(initvalcount == 1, storm::exceptions::InvalidJaniException,
958 "Initial value must be given once for transient variable '" + name +
"' (scope: " + scope.
description +
") " + name +
961 STORM_LOG_THROW(initvalcount <= 1, storm::exceptions::InvalidJaniException,
962 "Initial value can be given at most one for variable " + name +
"' (scope: " + scope.
description +
").");
964 boost::optional<storm::expressions::Expression> initVal;
965 if (initvalcount == 1 && !variableStructure.at(
"initial-value").is_null()) {
966 initVal = parseExpression(variableStructure.at(
"initial-value"), scope.
refine(
"Initial value for variable " + name));
971 assert(!transientVar);
974 if (transientVar && type.first->isBasicType() && type.first->asBasicType().isBooleanType()) {
978 auto expressionVariable = expressionManager->declareVariable(exprManagerName, type.second);
986 STORM_LOG_THROW(expected == actual, storm::exceptions::InvalidJaniException,
987 "Operator " << opstring <<
" expects " << expected <<
" arguments, but got " << actual <<
" in " << errorInfo <<
".");
990template<
typename ValueType>
991std::vector<storm::expressions::Expression> JaniParser<ValueType>::parseUnaryExpressionArguments(
992 Json
const& expressionDecl, std::string
const& opstring, Scope
const& scope,
bool returnNoneInitializedOnUnknownOperator,
993 std::unordered_map<std::string, storm::expressions::Variable>
const& auxiliaryVariables) {
995 parseExpression(expressionDecl.at(
"exp"), scope.refine(
"Argument of operator " + opstring), returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
999template<
typename ValueType>
1000std::vector<storm::expressions::Expression> JaniParser<ValueType>::parseBinaryExpressionArguments(
1001 Json
const& expressionDecl, std::string
const& opstring, Scope
const& scope,
bool returnNoneInitializedOnUnknownOperator,
1002 std::unordered_map<std::string, storm::expressions::Variable>
const& auxiliaryVariables) {
1004 returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1006 returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1007 return {left, right};
1014 "Operator " << opstring <<
" expects argument[" << argNr <<
"]: '" << expr <<
"' to be Boolean in " << errorInfo <<
".");
1022 "Operator " << opstring <<
" expects argument " + std::to_string(argNr) +
" to be numerical in " << errorInfo <<
".");
1030 "Operator " << opstring <<
" expects argument " + std::to_string(argNr) +
" to be numerical in " << errorInfo <<
".");
1038 "Operator " << opstring <<
" expects argument " + std::to_string(argNr) +
" to be of type 'array' in " << errorInfo <<
".");
1041template<
typename ValueType>
1043 if (lValueStructure.is_string()) {
1044 std::string ident = getString<ValueType>(lValueStructure, scope.
description);
1047 auto localVar = scope.
localVars->find(ident);
1048 if (localVar != scope.
localVars->end()) {
1049 var = localVar->second;
1052 if (var ==
nullptr) {
1054 "Unknown identifier '" << ident <<
"' occurs in " << scope.
description);
1055 auto globalVar = scope.
globalVars->find(ident);
1057 "Unknown identifier '" << ident <<
"' occurs in " << scope.
description);
1058 var = globalVar->second;
1062 }
else if (lValueStructure.count(
"op") == 1) {
1066 std::string opstring = getString<ValueType>(lValueStructure.at(
"op"), scope.
description);
1067 STORM_LOG_THROW(opstring ==
"aa", storm::exceptions::InvalidJaniException,
"Unknown operation '" << opstring <<
"' occurs in " << scope.
description);
1068 STORM_LOG_THROW(lValueStructure.count(
"exp") == 1, storm::exceptions::InvalidJaniException,
"Missing 'exp' in array access at " << scope.
description);
1069 auto expLValue = parseLValue(lValueStructure.at(
"exp"), scope.
refine(
"Expression of array access"));
1070 STORM_LOG_THROW(expLValue.isArray(), storm::exceptions::InvalidJaniException,
"Array access considers non-array expression at " << scope.
description);
1071 STORM_LOG_THROW(lValueStructure.count(
"index"), storm::exceptions::InvalidJaniException,
"Missing 'index' in array access at " << scope.
description);
1072 auto indexExpression = parseExpression(lValueStructure.at(
"index"), scope.
refine(
"Index of array access"));
1073 expLValue.addArrayAccessIndex(indexExpression);
1076 STORM_LOG_THROW(
false, storm::exceptions::InvalidJaniException,
"Unknown LValue '" << lValueStructure.dump() <<
"' occurs in " << scope.
description);
1082template<
typename ValueType>
1084 std::string
const& ident, Scope
const& scope, std::unordered_map<std::string, storm::expressions::Variable>
const& auxiliaryVariables) {
1086 auto it = auxiliaryVariables.find(ident);
1087 if (it != auxiliaryVariables.end()) {
1091 if (scope.localVars !=
nullptr) {
1092 auto it = scope.localVars->find(ident);
1093 if (it != scope.localVars->end()) {
1094 return it->second->getExpressionVariable();
1097 if (scope.globalVars !=
nullptr) {
1098 auto it = scope.globalVars->find(ident);
1099 if (it != scope.globalVars->end()) {
1100 return it->second->getExpressionVariable();
1103 if (scope.constants !=
nullptr) {
1104 auto it = scope.constants->find(ident);
1105 if (it != scope.constants->end()) {
1106 return it->second->getExpressionVariable();
1109 STORM_LOG_THROW(
false, storm::exceptions::InvalidJaniException,
"Unknown identifier '" << ident <<
"' occurs in " << scope.description);
1114template<
typename ValueType>
1116 bool returnNoneInitializedOnUnknownOperator,
1117 std::unordered_map<std::string, storm::expressions::Variable>
const& auxiliaryVariables) {
1118 if (expressionStructure.is_boolean()) {
1119 if (expressionStructure.template get<bool>()) {
1120 return expressionManager->boolean(
true);
1122 return expressionManager->boolean(
false);
1124 }
else if (expressionStructure.is_number_integer()) {
1125 return expressionManager->integer(expressionStructure.template get<int64_t>());
1126 }
else if (expressionStructure.is_number_float()) {
1127 return expressionManager->rational(storm::utility::convertNumber<storm::RationalNumber>(expressionStructure.template get<ValueType>()));
1128 }
else if (expressionStructure.is_string()) {
1129 std::string ident = expressionStructure.template get<std::string>();
1131 }
else if (expressionStructure.is_object()) {
1132 if (expressionStructure.count(
"distribution") == 1) {
1134 false, storm::exceptions::InvalidJaniException,
1135 "Distributions are not supported by storm expressions, cannot import " << expressionStructure.dump() <<
" in " << scope.
description <<
".");
1137 if (expressionStructure.count(
"op") == 1) {
1138 std::string opstring = getString<ValueType>(expressionStructure.at(
"op"), scope.
description);
1139 std::vector<storm::expressions::Expression> arguments = {};
1140 if (opstring ==
"ite") {
1141 STORM_LOG_THROW(expressionStructure.count(
"if") == 1, storm::exceptions::InvalidJaniException,
"If operator required");
1142 STORM_LOG_THROW(expressionStructure.count(
"else") == 1, storm::exceptions::InvalidJaniException,
"Else operator required");
1143 STORM_LOG_THROW(expressionStructure.count(
"then") == 1, storm::exceptions::InvalidJaniException,
"Then operator required");
1144 arguments.push_back(
1145 parseExpression(expressionStructure.at(
"if"), scope.
refine(
"if-formula"), returnNoneInitializedOnUnknownOperator, auxiliaryVariables));
1146 arguments.push_back(
1147 parseExpression(expressionStructure.at(
"then"), scope.
refine(
"then-formula"), returnNoneInitializedOnUnknownOperator, auxiliaryVariables));
1148 arguments.push_back(
1149 parseExpression(expressionStructure.at(
"else"), scope.
refine(
"else-formula"), returnNoneInitializedOnUnknownOperator, auxiliaryVariables));
1151 assert(arguments.size() == 3);
1154 }
else if (opstring ==
"∨") {
1155 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1156 assert(arguments.size() == 2);
1157 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1162 return arguments[0] || arguments[1];
1163 }
else if (opstring ==
"∧") {
1164 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1165 assert(arguments.size() == 2);
1166 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1171 return arguments[0] && arguments[1];
1172 }
else if (opstring ==
"⇒") {
1173 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1174 assert(arguments.size() == 2);
1175 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1180 return (!arguments[0]) || arguments[1];
1181 }
else if (opstring ==
"¬") {
1182 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1183 assert(arguments.size() == 1);
1184 if (!arguments[0].isInitialized()) {
1188 return !arguments[0];
1189 }
else if (opstring ==
"=") {
1190 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1191 assert(arguments.size() == 2);
1192 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1195 if (arguments[0].hasBooleanType()) {
1200 return arguments[0] == arguments[1];
1202 }
else if (opstring ==
"≠") {
1203 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1204 assert(arguments.size() == 2);
1205 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1208 if (arguments[0].hasBooleanType()) {
1213 return arguments[0] != arguments[1];
1215 }
else if (opstring ==
"<") {
1216 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1217 assert(arguments.size() == 2);
1218 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1223 return arguments[0] < arguments[1];
1224 }
else if (opstring ==
"≤") {
1225 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1226 assert(arguments.size() == 2);
1227 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1232 return arguments[0] <= arguments[1];
1233 }
else if (opstring ==
">") {
1234 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1235 assert(arguments.size() == 2);
1236 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1241 return arguments[0] > arguments[1];
1242 }
else if (opstring ==
"≥") {
1243 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1244 assert(arguments.size() == 2);
1245 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1250 return arguments[0] >= arguments[1];
1251 }
else if (opstring ==
"+") {
1252 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1253 assert(arguments.size() == 2);
1256 return arguments[0] + arguments[1];
1257 }
else if (opstring ==
"-" && expressionStructure.count(
"left") > 0) {
1258 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1259 assert(arguments.size() == 2);
1262 return arguments[0] - arguments[1];
1263 }
else if (opstring ==
"-") {
1264 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1265 assert(arguments.size() == 1);
1267 return -arguments[0];
1268 }
else if (opstring ==
"*") {
1269 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1270 assert(arguments.size() == 2);
1273 return arguments[0] * arguments[1];
1274 }
else if (opstring ==
"/") {
1275 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1276 assert(arguments.size() == 2);
1279 return arguments[0] / arguments[1];
1280 }
else if (opstring ==
"%") {
1281 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1282 assert(arguments.size() == 2);
1285 return arguments[0] % arguments[1];
1286 }
else if (opstring ==
"max") {
1287 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1288 assert(arguments.size() == 2);
1292 }
else if (opstring ==
"min") {
1293 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1294 assert(arguments.size() == 2);
1298 }
else if (opstring ==
"floor") {
1299 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1300 assert(arguments.size() == 1);
1303 }
else if (opstring ==
"ceil") {
1304 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1305 assert(arguments.size() == 1);
1308 }
else if (opstring ==
"abs") {
1309 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1310 assert(arguments.size() == 1);
1313 }
else if (opstring ==
"sgn") {
1314 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1315 assert(arguments.size() == 1);
1318 }
else if (opstring ==
"trc") {
1319 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1320 assert(arguments.size() == 1);
1323 }
else if (opstring ==
"pow") {
1324 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1325 assert(arguments.size() == 2);
1329 }
else if (opstring ==
"exp") {
1330 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1331 assert(arguments.size() == 2);
1335 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"exp operation is not yet implemented");
1336 }
else if (opstring ==
"log") {
1337 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1338 assert(arguments.size() == 2);
1342 }
else if (opstring ==
"cos") {
1343 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1344 assert(arguments.size() == 1);
1347 }
else if (opstring ==
"sin") {
1348 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1349 assert(arguments.size() == 1);
1352 }
else if (opstring ==
"aa") {
1353 STORM_LOG_THROW(expressionStructure.count(
"exp") == 1, storm::exceptions::InvalidJaniException,
1354 "Array access operator requires exactly one exp (at " + scope.
description +
").");
1356 returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1357 STORM_LOG_THROW(expressionStructure.count(
"index") == 1, storm::exceptions::InvalidJaniException,
1358 "Array access operator requires exactly one index (at " + scope.
description +
").");
1360 returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1366 }
else if (opstring ==
"av") {
1367 STORM_LOG_THROW(expressionStructure.count(
"elements") == 1, storm::exceptions::InvalidJaniException,
1368 "Array value operator requires exactly one 'elements' (at " + scope.
description +
").");
1369 std::vector<std::shared_ptr<storm::expressions::BaseExpression const>> elements;
1372 for (
auto const& element : expressionStructure.at(
"elements")) {
1373 elements.push_back(parseExpression(element, scope.
refine(
"element " + std::to_string(elements.size()) +
" of array value expression"),
1374 returnNoneInitializedOnUnknownOperator, auxiliaryVariables)
1375 .getBaseExpressionPointer());
1377 commonType = elements.back()->getType();
1379 }
else if (!(commonType == elements.back()->getType())) {
1380 if (commonType.
isIntegerType() && elements.back()->getType().isRationalType()) {
1381 commonType = elements.back()->getType();
1384 "Incompatible element types " << commonType <<
" and " << elements.back()->getType()
1385 <<
" of array value expression at " << scope.
description);
1389 return std::make_shared<storm::expressions::ValueArrayExpression>(*expressionManager, expressionManager->getArrayType(commonType), elements)
1391 }
else if (opstring ==
"ac") {
1392 STORM_LOG_THROW(expressionStructure.count(
"length") == 1, storm::exceptions::InvalidJaniException,
1393 "Array access operator requires exactly one length (at " + scope.
description +
").");
1395 returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1397 STORM_LOG_THROW(expressionStructure.count(
"var") == 1, storm::exceptions::InvalidJaniException,
1398 "Array access operator requires exactly one var (at " + scope.
description +
").");
1399 std::string indexVarName =
1400 getString<ValueType>(expressionStructure.at(
"var"),
"Field 'var' of Array access operator (at " + scope.
description +
").");
1401 STORM_LOG_THROW(auxiliaryVariables.find(indexVarName) == auxiliaryVariables.end(), storm::exceptions::InvalidJaniException,
1402 "Index variable " << indexVarName <<
" is already defined as an auxiliary variable (at " + scope.
description +
").");
1403 auto newAuxVars = auxiliaryVariables;
1405 newAuxVars.emplace(indexVarName, indexVar);
1406 STORM_LOG_THROW(expressionStructure.count(
"exp") == 1, storm::exceptions::InvalidJaniException,
1407 "Array constructor operator requires exactly one exp (at " + scope.
description +
").");
1409 returnNoneInitializedOnUnknownOperator, newAuxVars);
1410 return std::make_shared<storm::expressions::ConstructorArrayExpression>(*expressionManager, expressionManager->getArrayType(exp.
getType()),
1414 }
else if (opstring ==
"call") {
1415 STORM_LOG_THROW(expressionStructure.count(
"function") == 1, storm::exceptions::InvalidJaniException,
1416 "Function call operator requires exactly one function (at " + scope.
description +
").");
1417 std::string functionName =
1418 getString<ValueType>(expressionStructure.at(
"function"),
"in function call operator (at " + scope.
description +
").");
1426 "Function call operator calls unknown function '" + functionName +
"' (at " + scope.
description +
").");
1428 STORM_LOG_THROW(expressionStructure.count(
"args") == 1, storm::exceptions::InvalidJaniException,
1429 "Function call operator requires exactly one args (at " + scope.
description +
").");
1430 std::vector<std::shared_ptr<storm::expressions::BaseExpression const>> args;
1431 if (expressionStructure.count(
"args") > 0) {
1432 STORM_LOG_THROW(expressionStructure.count(
"args") == 1, storm::exceptions::InvalidJaniException,
1433 "Function call operator requires exactly one args (at " + scope.
description +
").");
1434 for (
auto const& arg : expressionStructure.at(
"args")) {
1435 args.push_back(parseExpression(arg, scope.
refine(
"argument " + std::to_string(args.size()) +
" of function call expression"),
1436 returnNoneInitializedOnUnknownOperator, auxiliaryVariables)
1437 .getBaseExpressionPointer());
1440 return std::make_shared<storm::expressions::FunctionCallExpression>(*expressionManager, functionDefinition->
getType(), functionName, args)
1442 }
else if (unsupportedOpstrings.count(opstring) > 0) {
1443 STORM_LOG_THROW(
false, storm::exceptions::InvalidJaniException,
"Opstring " + opstring +
" is not supported by storm");
1445 if (returnNoneInitializedOnUnknownOperator) {
1448 STORM_LOG_THROW(
false, storm::exceptions::InvalidJaniException,
"Unknown operator " << opstring <<
" in " << scope.
description <<
".");
1451 if (expressionStructure.count(
"constant") == 1) {
1453 const std::string constantStr = getString<ValueType>(expressionStructure.at(
"constant"), scope.
description);
1454 if (constantStr ==
"Ï€") {
1455 return std::make_shared<storm::expressions::TranscendentalNumberLiteralExpression>(
1459 if (constantStr ==
"e") {
1460 return std::make_shared<storm::expressions::TranscendentalNumberLiteralExpression>(
1466 false, storm::exceptions::InvalidJaniException,
1467 "No supported operator declaration found for complex expressions as " << expressionStructure.dump() <<
" in " << scope.
description <<
".");
1470 "No supported expression found at " << expressionStructure.dump() <<
" in " << scope.
description <<
".");
1475template<
typename ValueType>
1477 std::set<std::string> actionNames;
1478 for (
auto const& actionEntry : actionStructure) {
1479 STORM_LOG_THROW(actionEntry.count(
"name") == 1, storm::exceptions::InvalidJaniException,
"Actions must have exactly one name.");
1480 std::string actionName = getString<ValueType>(actionEntry.at(
"name"),
"name of action");
1481 STORM_LOG_THROW(actionNames.count(actionName) == 0, storm::exceptions::InvalidJaniException,
"Action with name " + actionName +
" already exists.");
1483 actionNames.emplace(actionName);
1487template<
typename ValueType>
1489 STORM_LOG_THROW(automatonStructure.count(
"name") == 1, storm::exceptions::InvalidJaniException,
"Each automaton must have a name");
1490 std::string name = getString<ValueType>(automatonStructure.at(
"name"),
" the name field for automaton");
1494 uint64_t varDeclCount = automatonStructure.count(
"variables");
1495 STORM_LOG_THROW(varDeclCount < 2, storm::exceptions::InvalidJaniException,
"Automaton '" << name <<
"' has more than one list of variables");
1498 if (varDeclCount > 0) {
1499 for (
auto const& varStructure : automatonStructure.at(
"variables")) {
1500 std::shared_ptr<storm::jani::Variable> var = parseVariable(
1502 assert(localVars.count(var->getName()) == 0);
1503 localVars.emplace(var->getName(), &automaton.
addVariable(*var));
1507 uint64_t funDeclCount = automatonStructure.count(
"functions");
1508 STORM_LOG_THROW(funDeclCount < 2, storm::exceptions::InvalidJaniException,
"Automaton '" << name <<
"' has more than one list of functions");
1511 if (funDeclCount > 0) {
1513 std::vector<storm::jani::FunctionDefinition> dummyFunctionDefinitions;
1514 for (
auto const& funStructure : automatonStructure.at(
"functions")) {
1516 dummyFunctionDefinitions.push_back(
1517 parseFunctionDefinition(funStructure, scope.
refine(
"functions[" + std::to_string(localFuns.size()) +
"] of automaton " + name),
true));
1521 for (
auto const& funDef : dummyFunctionDefinitions) {
1522 bool unused = localFuns.emplace(funDef.getName(), &funDef).second;
1524 "Multiple definitions of functions with the name " << funDef.getName() <<
" in " << scope.
description);
1526 for (
auto const& funStructure : automatonStructure.at(
"functions")) {
1529 parseFunctionDefinition(funStructure, scope.
refine(
"functions[" + std::to_string(localFuns.size()) +
"] of automaton " + name),
false,
1531 assert(localFuns.count(funDef.
getName()) == 1);
1536 STORM_LOG_THROW(automatonStructure.count(
"locations") > 0, storm::exceptions::InvalidJaniException,
"Automaton '" << name <<
"' does not have locations.");
1537 std::unordered_map<std::string, uint64_t> locIds;
1538 for (
auto const& locEntry : automatonStructure.at(
"locations")) {
1539 STORM_LOG_THROW(locEntry.count(
"name") == 1, storm::exceptions::InvalidJaniException,
1540 "Locations for automaton '" << name <<
"' must have exactly one name");
1541 std::string locName = getString<ValueType>(locEntry.at(
"name"),
"location of automaton " + name);
1542 STORM_LOG_THROW(locIds.count(locName) == 0, storm::exceptions::InvalidJaniException,
1543 "Location with name '" + locName +
"' already exists in automaton '" + name +
"'");
1544 STORM_LOG_THROW(locEntry.count(
"invariant") == 0, storm::exceptions::InvalidJaniException,
1545 "Invariants in locations as in '" + locName +
"' in automaton '" + name +
"' are not supported");
1548 std::vector<storm::jani::Assignment> transientAssignments;
1549 if (locEntry.count(
"transient-values") > 0) {
1550 for (
auto const& transientValueEntry : locEntry.at(
"transient-values")) {
1551 STORM_LOG_THROW(transientValueEntry.count(
"ref") == 1, storm::exceptions::InvalidJaniException,
1552 "Transient values in location " << locName <<
" need exactly one ref that is assigned to");
1553 STORM_LOG_THROW(transientValueEntry.count(
"value") == 1, storm::exceptions::InvalidJaniException,
1554 "Transient values in location " << locName <<
" need exactly one assigned value");
1555 storm::jani::LValue lValue = parseLValue(transientValueEntry.at(
"ref"), scope.
refine(
"LHS of assignment in location " + locName));
1557 "Assigned non-transient variable " << lValue <<
" in location " + locName +
" (automaton: '" + name +
"').");
1559 parseExpression(transientValueEntry.at(
"value"), scope.
refine(
"Assignment of lValue in location " + locName));
1560 transientAssignments.emplace_back(lValue, rhs);
1564 locIds.emplace(locName,
id);
1566 STORM_LOG_THROW(automatonStructure.count(
"initial-locations") == 1, storm::exceptions::InvalidJaniException,
1567 "Automaton '" << name <<
"' does not have initial locations.");
1568 for (
Json const& initLocStruct : automatonStructure.at(
"initial-locations")) {
1569 automaton.
addInitialLocation(getString<ValueType>(initLocStruct,
"Initial locations for automaton '" + name +
"'."));
1571 STORM_LOG_THROW(automatonStructure.count(
"restrict-initial") < 2, storm::exceptions::InvalidJaniException,
1572 "Automaton '" << name <<
"' has multiple initial value restrictions");
1574 if (automatonStructure.count(
"restrict-initial") > 0) {
1575 STORM_LOG_THROW(automatonStructure.at(
"restrict-initial").count(
"exp") == 1, storm::exceptions::InvalidJaniException,
1576 "Automaton '" << name <<
"' needs an expression inside the initial restricion");
1577 initialValueRestriction = parseExpression(automatonStructure.at(
"restrict-initial").at(
"exp"), scope.
refine(
"Initial value restriction"));
1581 STORM_LOG_THROW(automatonStructure.count(
"edges") > 0, storm::exceptions::InvalidJaniException,
"Automaton '" << name <<
"' must have a list of edges");
1582 for (
auto const& edgeEntry : automatonStructure.at(
"edges")) {
1584 STORM_LOG_THROW(edgeEntry.count(
"location") == 1, storm::exceptions::InvalidJaniException,
1585 "Each edge in automaton '" << name <<
"' must have a source");
1586 std::string sourceLoc = getString<ValueType>(edgeEntry.at(
"location"),
"source location for edge in automaton '" + name +
"'");
1587 STORM_LOG_THROW(locIds.count(sourceLoc) == 1, storm::exceptions::InvalidJaniException,
1588 "Source of edge has unknown location '" << sourceLoc <<
"' in automaton '" << name <<
"'.");
1590 STORM_LOG_THROW(edgeEntry.count(
"action") < 2, storm::exceptions::InvalidJaniException,
1591 "Edge from " << sourceLoc <<
" in automaton " << name <<
" has multiple actions");
1593 if (edgeEntry.count(
"action") > 0) {
1594 action = getString<ValueType>(edgeEntry.at(
"action"),
"action name in edge from '" + sourceLoc +
"' in automaton '" + name +
"'");
1596 assert(action !=
"");
1599 STORM_LOG_THROW(edgeEntry.count(
"rate") < 2, storm::exceptions::InvalidJaniException,
1600 "Edge from '" << sourceLoc <<
"' in automaton '" << name <<
"' has multiple rates");
1602 if (edgeEntry.count(
"rate") > 0) {
1603 STORM_LOG_THROW(edgeEntry.at(
"rate").count(
"exp") == 1, storm::exceptions::InvalidJaniException,
1604 "Rate in edge from '" << sourceLoc <<
"' in automaton '" << name <<
"' must have a defing expression.");
1605 rateExpr = parseExpression(edgeEntry.at(
"rate").at(
"exp"), scope.
refine(
"rate expression in edge from '" + sourceLoc));
1608 storm::exceptions::InvalidJaniException,
"Only positive rates are allowed but rate '" << rateExpr <<
" was found.");
1611 STORM_LOG_THROW(edgeEntry.count(
"guard") <= 1, storm::exceptions::InvalidJaniException,
1612 "Guard can be given at most once in edge from '" << sourceLoc <<
"' in automaton '" << name <<
"'");
1614 if (edgeEntry.count(
"guard") == 1) {
1615 STORM_LOG_THROW(edgeEntry.at(
"guard").count(
"exp") == 1, storm::exceptions::InvalidJaniException,
1616 "Guard in edge from '" + sourceLoc +
"' in automaton '" + name +
"' must have one expression");
1617 guardExpr = parseExpression(edgeEntry.at(
"guard").at(
"exp"), scope.
refine(
"guard expression in edge from '" + sourceLoc));
1618 STORM_LOG_THROW(guardExpr.
hasBooleanType(), storm::exceptions::InvalidJaniException,
"Guard " << guardExpr <<
" does not have Boolean type.");
1621 std::shared_ptr<storm::jani::TemplateEdge> templateEdge = std::make_shared<storm::jani::TemplateEdge>(guardExpr);
1624 if (edgeEntry.count(
"assignments") > 0) {
1625 STORM_LOG_THROW(edgeEntry.count(
"assignments") == 1, storm::exceptions::InvalidJaniException,
1626 "Multiple edge assignments in edge from '" + sourceLoc +
"' in automaton '" + name +
"'.");
1627 for (
auto const& assignmentEntry : edgeEntry.at(
"assignments")) {
1629 STORM_LOG_THROW(assignmentEntry.count(
"ref") == 1, storm::exceptions::InvalidJaniException,
1630 "Assignment in edge from '" << sourceLoc <<
"' in automaton '" << name <<
"'must have one ref field");
1632 parseLValue(assignmentEntry.at(
"ref"), scope.
refine(
"Assignment variable in edge from '" + sourceLoc +
"' in automaton '" + name +
"'"));
1634 STORM_LOG_THROW(assignmentEntry.count(
"value") == 1, storm::exceptions::InvalidJaniException,
1635 "Assignment in edge from '" << sourceLoc <<
"' in automaton '" << name <<
"' must have one value field");
1637 parseExpression(assignmentEntry.at(
"value"), scope.
refine(
"assignment in edge from '" + sourceLoc +
"' in automaton '" + name +
"'"));
1640 int64_t assignmentIndex = 0;
1641 if (assignmentEntry.count(
"index") > 0) {
1643 getSignedInt<ValueType>(assignmentEntry.at(
"index"),
"assignment index in edge from '" + sourceLoc +
"' in automaton '" + name +
"'");
1650 STORM_LOG_THROW(edgeEntry.count(
"destinations") == 1, storm::exceptions::InvalidJaniException,
1651 "A single list of destinations must be given in edge from '" << sourceLoc <<
"' in automaton '" << name <<
"'");
1652 std::vector<std::pair<uint64_t, storm::expressions::Expression>> destinationLocationsAndProbabilities;
1653 for (
auto const& destEntry : edgeEntry.at(
"destinations")) {
1655 STORM_LOG_THROW(destEntry.count(
"location") == 1, storm::exceptions::InvalidJaniException,
1656 "Each destination in edge from '" << sourceLoc <<
"' in automaton '" << name <<
"' must have a target location");
1657 std::string targetLoc =
1658 getString<ValueType>(destEntry.at(
"location"),
"target location for edge from '" + sourceLoc +
"' in automaton '" + name +
"'");
1659 STORM_LOG_THROW(locIds.count(targetLoc) == 1, storm::exceptions::InvalidJaniException,
1660 "Target of edge has unknown location '" << targetLoc <<
"' in automaton '" << name <<
"'.");
1663 unsigned probDeclCount = destEntry.count(
"probability");
1664 STORM_LOG_THROW(probDeclCount < 2, storm::exceptions::InvalidJaniException,
1665 "Destination in edge from '" << sourceLoc <<
"' to '" << targetLoc <<
"' in automaton '" << name <<
"' has multiple probabilites");
1666 if (probDeclCount == 0) {
1667 probExpr = expressionManager->rational(1.0);
1669 STORM_LOG_THROW(destEntry.at(
"probability").count(
"exp") == 1, storm::exceptions::InvalidJaniException,
1670 "Destination in edge from '" << sourceLoc <<
"' to '" << targetLoc <<
"' in automaton '" << name
1671 <<
"' must have a probability expression.");
1672 probExpr = parseExpression(destEntry.at(
"probability").at(
"exp"), scope.
refine(
"probability expression in edge from '" + sourceLoc +
"' to '" +
1673 targetLoc +
"' in automaton '" + name +
"'"));
1677 "Probability expression " << probExpr <<
" does not have a numerical type.");
1679 std::vector<storm::jani::Assignment> assignments;
1680 unsigned assignmentDeclCount = destEntry.count(
"assignments");
1682 assignmentDeclCount < 2, storm::exceptions::InvalidJaniException,
1683 "Destination in edge from '" << sourceLoc <<
"' to '" << targetLoc <<
"' in automaton '" << name <<
"' has multiple assignment lists");
1684 if (assignmentDeclCount > 0) {
1685 for (
auto const& assignmentEntry : destEntry.at(
"assignments")) {
1688 assignmentEntry.count(
"ref") == 1, storm::exceptions::InvalidJaniException,
1689 "Assignment in edge from '" << sourceLoc <<
"' to '" << targetLoc <<
"' in automaton '" << name <<
"' must have one ref field");
1690 storm::jani::LValue lValue = parseLValue(assignmentEntry.at(
"ref"), scope.
refine(
"Assignment variable in edge from '" + sourceLoc +
1691 "' to '" + targetLoc +
"' in automaton '" + name +
"'"));
1694 assignmentEntry.count(
"value") == 1, storm::exceptions::InvalidJaniException,
1695 "Assignment in edge from '" << sourceLoc <<
"' to '" << targetLoc <<
"' in automaton '" << name <<
"' must have one value field");
1697 parseExpression(assignmentEntry.at(
"value"),
1698 scope.
refine(
"assignment in edge from '" + sourceLoc +
"' to '" + targetLoc +
"' in automaton '" + name +
"'"));
1701 int64_t assignmentIndex = 0;
1702 if (assignmentEntry.count(
"index") > 0) {
1703 assignmentIndex = getSignedInt<ValueType>(assignmentEntry.at(
"index"),
"assignment index in edge from '" + sourceLoc +
"' to '" +
1704 targetLoc +
"' in automaton '" + name +
"'");
1706 assignments.emplace_back(lValue, assignmentExpr, assignmentIndex);
1709 destinationLocationsAndProbabilities.emplace_back(locIds.at(targetLoc), probExpr);
1713 rateExpr.
isInitialized() ? boost::optional<storm::expressions::Expression>(rateExpr) : boost::none, templateEdge,
1714 destinationLocationsAndProbabilities));
1720template<
typename ValueType>
1722 std::vector<storm::jani::SynchronizationVector> syncVectors;
1724 for (
auto const& syncEntry : syncVectorStructure) {
1725 std::vector<std::string> inputs;
1726 for (
auto const& syncInput : syncEntry.at(
"synchronise")) {
1727 if (syncInput.is_null()) {
1730 inputs.push_back(syncInput);
1733 std::string syncResult;
1734 if (syncEntry.count(
"result")) {
1735 syncResult = syncEntry.at(
"result");
1739 syncVectors.emplace_back(inputs, syncResult);
1744template<
typename ValueType>
1745std::shared_ptr<storm::jani::Composition> JaniParser<ValueType>::parseComposition(Json
const& compositionStructure) {
1746 if (compositionStructure.count(
"automaton")) {
1747 std::set<std::string> inputEnabledActions;
1748 if (compositionStructure.count(
"input-enable")) {
1749 for (
auto const& actionDecl : compositionStructure.at(
"input-enable")) {
1750 inputEnabledActions.insert(actionDecl.template get<std::string>());
1753 return std::shared_ptr<storm::jani::AutomatonComposition>(
1757 STORM_LOG_THROW(compositionStructure.count(
"elements") == 1, storm::exceptions::InvalidJaniException,
1758 "Elements of a composition must be given, got " << compositionStructure.dump());
1760 if (compositionStructure.at(
"elements").size() == 1 && compositionStructure.count(
"syncs") == 0) {
1762 STORM_LOG_THROW(compositionStructure.at(
"elements").back().count(
"automaton") == 1, storm::exceptions::InvalidJaniException,
1763 "Automaton must be given in composition");
1764 if (compositionStructure.at(
"elements").back().at(
"automaton").is_string()) {
1765 std::string name = compositionStructure.at(
"elements").back().at(
"automaton");
1769 STORM_LOG_THROW(
false, storm::exceptions::InvalidJaniException,
"Trivial nesting parallel composition is not yet supported");
1772 std::vector<std::shared_ptr<storm::jani::Composition>> compositions;
1773 for (
auto const& elemDecl : compositionStructure.at(
"elements")) {
1774 if (!allowRecursion) {
1775 STORM_LOG_THROW(elemDecl.count(
"automaton") == 1, storm::exceptions::InvalidJaniException,
"Automaton must be given in the element");
1777 compositions.push_back(parseComposition(elemDecl));
1780 STORM_LOG_THROW(compositionStructure.count(
"syncs") < 2, storm::exceptions::InvalidJaniException,
"Sync vectors can be given at most once");
1781 std::vector<storm::jani::SynchronizationVector> syncVectors;
1782 if (compositionStructure.count(
"syncs") > 0) {
1783 syncVectors = parseSyncVectors<ValueType>(compositionStructure.at(
"syncs"));
1789template class JaniParser<double>;
1790template class JaniParser<storm::RationalNumber>;
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.
bool isIntegerType() const
Checks whether this type is an integral type.
bool isNumericalType() const
Checks whether this type is a numerical type.
bool isArrayType() const
Checks whether this type is an array type.
bool isRationalType() const
Checks whether this type is a rational type.
storm::expressions::Expression getExpression() const
Retrieves an expression that represents the variable.
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.
FunctionDefinition const & addFunctionDefinition(FunctionDefinition const &functionDefinition)
Adds the given function definition.
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.
ModelFeatures & add(ModelFeature const &modelFeature)
void setInitialStatesRestriction(storm::expressions::Expression const &initialStatesRestriction)
Sets the expression restricting the legal initial values of the global variables.
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,...
void setSystemComposition(std::shared_ptr< Composition > const &composition)
Sets the system composition expression of the JANI model.
std::vector< Constant > const & getConstants() const
Retrieves the constants of the model.
void addConstant(Constant const &constant)
Adds the given constant to the model.
uint64_t getJaniVersion() const
Retrieves the JANI-version of the model.
Variable const & addVariable(Variable const &variable)
Adds the given variable to this model.
FunctionDefinition const & addFunctionDefinition(FunctionDefinition const &functionDefinition)
Adds the given function definition.
static const std::string SILENT_ACTION_NAME
The name of the silent action.
uint64_t addAction(Action const &action)
Adds an action to the model.
std::size_t getNumberOfAutomata() const
Retrieves the number of automata in this model.
bool isDiscreteTimeModel() const
Determines whether this model is a discrete-time model.
ModelFeatures const & getModelFeatures() const
Retrieves the enabled model features.
uint64_t addAutomaton(Automaton const &automaton)
Adds the given automaton to the automata of this model.
uint64_t getActionIndex(std::string const &name) const
Get the index of the action.
void finalize()
After adding all components to the model, this method has to be called.
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...
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
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
std::unordered_map< std::string, storm::jani::Constant const * > ConstantsMap
std::unordered_map< std::string, storm::jani::FunctionDefinition const * > FunctionsMap
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)
#define STORM_LOG_TRACE(message)
#define STORM_LOG_WARN_COND(cond, message)
#define STORM_LOG_THROW(cond, exception, message)
Expression maximum(Expression const &first, Expression const &second)
Expression ceil(Expression const &first)
Expression ite(Expression const &condition, Expression const &thenExpression, Expression const &elseExpression)
Expression iff(Expression const &first, Expression const &second)
Expression abs(Expression const &first)
Expression minimum(Expression const &first, Expression const &second)
Expression floor(Expression const &first)
Expression sin(Expression const &first)
Expression sign(Expression const &first)
Expression truncate(Expression const &first)
Expression pow(Expression const &base, Expression const &exponent, bool allowIntegerType)
The type of the resulting expression is.
Expression xclusiveor(Expression const &first, Expression const &second)
Expression logarithm(Expression const &first, Expression const &second)
Expression cos(Expression const &first)
void closeFile(std::ofstream &stream)
Close the given file after writing.
void openFile(std::string const &filepath, std::ofstream &filestream, bool append=false, bool silent=false)
Open the given file for writing.
std::string toString(ModelFeature const &modelFeature)
ModelType getModelType(std::string const &input)
ModelFeatures getAllKnownModelFeatures()
BinaryBooleanOperatorType
void ensureNumberOfArguments(uint64_t expected, uint64_t actual, std::string const &opstring, std::string const &errorInfo)
Helper for parse expression.
void insertLowerUpperTimeBounds(std::vector< boost::optional< storm::logic::TimeBound > > &lowerBounds, std::vector< boost::optional< storm::logic::TimeBound > > &upperBounds, storm::jani::PropertyInterval const &pi)
std::string getString(typename JaniParser< ValueType >::Json const &structure, std::string const &errorInfo)
std::vector< storm::jani::SynchronizationVector > parseSyncVectors(typename JaniParser< ValueType >::Json const &syncVectorStructure)
int64_t getSignedInt(typename JaniParser< ValueType >::Json const &structure, std::string const &errorInfo)
bool getBoolean(typename JaniParser< ValueType >::Json const &structure, std::string const &errorInfo)
void ensureArrayType(storm::expressions::Expression const &expr, std::string const &opstring, unsigned argNr, std::string const &errorInfo)
Helper for parse expression.
void ensureBooleanType(storm::expressions::Expression const &expr, std::string const &opstring, unsigned argNr, std::string const &errorInfo)
Helper for parse expression.
void ensureIntegerType(storm::expressions::Expression const &expr, std::string const &opstring, unsigned argNr, std::string const &errorInfo)
Helper for parse expression.
const std::string VARIABLE_AUTOMATON_DELIMITER
uint64_t getUnsignedInt(typename JaniParser< ValueType >::Json const &structure, std::string const &errorInfo)
void ensureNumericalType(storm::expressions::Expression const &expr, std::string const &opstring, unsigned argNr, std::string const &errorInfo)
Helper for parse expression.
bool isOne(ValueType const &a)
bool isZero(ValueType const &a)
Property intervals as per Jani Specification.
bool hasUpperBound() const
bool hasLowerBound() const
storm::expressions::Expression upperBound
storm::expressions::Expression lowerBound
FunctionsMap const * globalFunctions
VariablesMap const * localVars
FunctionsMap const * localFunctions
VariablesMap const * globalVars
Scope refine(std::string const &prependedDescription="") const
ConstantsMap const * constants