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.");
657 }
else if (opString ==
"Multi") {
660 <<
" not enabled but model contains multi-objective property in " << scope.description
661 <<
". Continuing with that property anyways");
662 assert(bound == boost::none);
663 STORM_LOG_THROW(propertyStructure.count(
"properties") == 1, storm::exceptions::InvalidJaniException,
664 "Expecting properties for multi-objective operator in " << scope.description);
665 std::vector<std::shared_ptr<storm::logic::Formula const>> subformulas;
667 for (
auto const& subPropStructure : propertyStructure.at(
"properties")) {
668 STORM_LOG_THROW(subPropStructure.count(
"exp") == 1, storm::exceptions::InvalidJaniException,
669 "Expecting property expression in subproperty #" << i <<
" in " << scope.description);
670 subformulas.push_back(parseFormula(model, subPropStructure[
"exp"], formulaContext,
671 scope.refine(
"Subproperty #" + std::to_string(i) +
" of multi-objective operator")));
672 if (subPropStructure.count(
"opt") == 1) {
673 STORM_LOG_THROW(subformulas.back()->hasQuantitativeResult(), storm::exceptions::InvalidJaniException,
674 "Subformula #" << i <<
" has an optimization direction but is not numeric in " << scope.description);
675 STORM_LOG_THROW(subformulas.back()->isOperatorFormula(), storm::exceptions::NotSupportedException,
676 "Subformula #" << i <<
" is not an operator formula in " << scope.description);
677 std::string
const optString =
678 getString<ValueType>(subPropStructure.at(
"opt"),
"optimization direction for subproperty #" + std::to_string(i));
679 STORM_LOG_THROW(optString ==
"min" || optString ==
"max", storm::exceptions::InvalidJaniException,
680 "Unknown optimization direction " << optString <<
" for subproperty #" << i <<
" in " << scope.description);
681 auto const opt = optString ==
"min" ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize;
682 auto newFormula = subformulas.back()->clone();
683 newFormula->asOperatorFormula().setOptimalityType(opt);
684 subformulas.back() = newFormula;
686 STORM_LOG_THROW(subformulas.back()->hasQualitativeResult(), storm::exceptions::InvalidJaniException,
687 "Subformula #" << i <<
" has non-Boolean result but no optimization direction in " << scope.description);
691 STORM_LOG_THROW(propertyStructure.count(
"type") == 1, storm::exceptions::InvalidJaniException,
692 "Expecting type for multi-objective operator in " << scope.description);
693 std::string
const typeString = getString<ValueType>(propertyStructure.at(
"type"),
"type of multi-objective operator");
694 STORM_LOG_THROW(typeString ==
"tradeoff" || typeString ==
"lexicographic", storm::exceptions::InvalidJaniException,
695 "Unknown type " << typeString <<
" for multi-objective operator in " << scope.description);
698 return std::make_shared<storm::logic::MultiObjectiveFormula const>(subformulas, type);
701 "Non-trivial Expression '" << expr <<
"' contains a boolean transient variable. Can not translate to PRCTL-like formula at "
702 << scope.description <<
".");
704 STORM_LOG_THROW(
false, storm::exceptions::InvalidJaniException,
"Unknown operator " << opString);
708 "Looking for operator for formula " << propertyStructure.dump() <<
", but did not find one");
712template<
typename ValueType>
714 STORM_LOG_THROW(propertyStructure.count(
"name") == 1, storm::exceptions::InvalidJaniException,
"Property must have a name");
716 std::string name = getString<ValueType>(propertyStructure.at(
"name"),
"property-name");
718 std::string comment =
"";
719 if (propertyStructure.count(
"comment") > 0) {
720 comment = getString<ValueType>(propertyStructure.at(
"comment"),
"comment for property named '" + name +
"'.");
722 STORM_LOG_THROW(propertyStructure.count(
"expression") == 1, storm::exceptions::InvalidJaniException,
"Property must have an expression");
724 Json const& expressionStructure = propertyStructure.at(
"expression");
726 STORM_LOG_THROW(expressionStructure.count(
"op") == 1, storm::exceptions::InvalidJaniException,
"Expression in property must have an operation description");
727 STORM_LOG_THROW(expressionStructure.at(
"op") ==
"filter", storm::exceptions::InvalidJaniException,
"Top level operation of a property must be a filter");
728 STORM_LOG_THROW(expressionStructure.count(
"fun") == 1, storm::exceptions::InvalidJaniException,
"Filter must have a function descritpion");
729 std::string funDescr = getString<ValueType>(expressionStructure.at(
"fun"),
"Filter function in property named " + name);
731 if (funDescr ==
"min") {
733 }
else if (funDescr ==
"max") {
735 }
else if (funDescr ==
"sum") {
737 }
else if (funDescr ==
"avg") {
739 }
else if (funDescr ==
"count") {
741 }
else if (funDescr ==
"∀") {
743 }
else if (funDescr ==
"∃") {
745 }
else if (funDescr ==
"argmin") {
747 }
else if (funDescr ==
"argmax") {
749 }
else if (funDescr ==
"values") {
752 STORM_LOG_THROW(
false, storm::exceptions::InvalidJaniException,
"Unknown filter description " << funDescr <<
" in property named " << name);
755 STORM_LOG_THROW(expressionStructure.count(
"states") == 1, storm::exceptions::InvalidJaniException,
"Filter must have a states description");
756 std::shared_ptr<storm::logic::Formula const> statesFormula;
757 if (expressionStructure.at(
"states").count(
"op") > 0) {
758 std::string statesDescr = getString<ValueType>(expressionStructure.at(
"states").at(
"op"),
"Filtered states in property named " + name);
759 if (statesDescr ==
"initial") {
760 statesFormula = std::make_shared<storm::logic::AtomicLabelFormula>(
"init");
763 if (!statesFormula) {
768 }
catch (storm::exceptions::NotSupportedException
const& ex) {
770 }
catch (storm::exceptions::NotImplementedException
const& ex) {
774 STORM_LOG_THROW(statesFormula, storm::exceptions::NotImplementedException,
"Could not derive states formula.");
775 STORM_LOG_THROW(expressionStructure.count(
"values") == 1, storm::exceptions::InvalidJaniException,
"Values as input for a filter must be given");
780template<
typename ValueType>
782 STORM_LOG_THROW(constantStructure.count(
"name") == 1, storm::exceptions::InvalidJaniException,
783 "Variable (scope: " + scope.description +
") must have a name");
784 std::string name = getString<ValueType>(constantStructure.at(
"name"),
"variable-name in " + scope.description +
"-scope");
787 std::string exprManagerName = name;
789 STORM_LOG_THROW(constantStructure.count(
"type") == 1, storm::exceptions::InvalidJaniException,
790 "Constant '" + name +
"' (scope: " + scope.description +
") must have a (single) type-declaration.");
791 auto type = parseType(constantStructure.at(
"type"), name, scope);
792 STORM_LOG_THROW((type.first->isBasicType() || type.first->isBoundedType()), storm::exceptions::InvalidJaniException,
793 "Constant '" + name +
"' (scope: " + scope.description +
") has unexpected type");
795 uint_fast64_t valueCount = constantStructure.count(
"value");
797 STORM_LOG_THROW(valueCount < 2, storm::exceptions::InvalidJaniException,
798 "Value for constant '" + name +
"' (scope: " + scope.description +
") must be given at most once.");
799 if (valueCount == 1) {
802 definingExpression = parseExpression(constantStructure.at(
"value"), scope.refine(
"Value of constant " + name));
806 storm::exceptions::InvalidJaniException,
807 "Type of value for constant '" + name +
"' (scope: " + scope.description +
") does not match the given type '" +
808 type.first->getStringRepresentation() +
".");
814 if (type.first->isBoundedType()) {
815 auto const& bndType = type.first->asBoundedType();
816 if (bndType.hasLowerBound()) {
817 constraintExpression = var.
getExpression() >= bndType.getLowerBound();
818 if (bndType.hasUpperBound()) {
819 constraintExpression = constraintExpression && var.
getExpression() <= bndType.getUpperBound();
821 }
else if (bndType.hasUpperBound()) {
822 constraintExpression = var.
getExpression() <= bndType.getUpperBound();
825 return std::make_shared<storm::jani::Constant>(name, std::move(var), definingExpression, constraintExpression);
828template<
typename ValueType>
830 std::string variableName,
Scope const& scope) {
832 if (typeStructure.is_string()) {
833 if (typeStructure ==
"real") {
835 result.second = expressionManager->getRationalType();
836 }
else if (typeStructure ==
"bool") {
838 result.second = expressionManager->getBooleanType();
839 }
else if (typeStructure ==
"int") {
841 result.second = expressionManager->getIntegerType();
842 }
else if (typeStructure ==
"clock") {
843 result.first = std::make_unique<storm::jani::ClockType>();
844 result.second = expressionManager->getRationalType();
845 }
else if (typeStructure ==
"continuous") {
846 result.first = std::make_unique<storm::jani::ContinuousType>();
847 result.second = expressionManager->getRationalType();
850 "Unsupported type " << typeStructure.dump() <<
" for variable '" << variableName <<
"' (scope: " << scope.
description <<
").");
852 }
else if (typeStructure.is_object()) {
853 STORM_LOG_THROW(typeStructure.count(
"kind") == 1, storm::exceptions::InvalidJaniException,
854 "For complex type as in variable " << variableName <<
"(scope: " << scope.
description <<
") kind must be given");
856 getString<ValueType>(typeStructure.at(
"kind"),
"kind for complex type as in variable " + variableName +
"(scope: " + scope.
description +
") ");
857 if (kind ==
"bounded") {
859 typeStructure.count(
"lower-bound") + typeStructure.count(
"upper-bound") > 0, storm::exceptions::InvalidJaniException,
860 "For bounded type as in variable " << variableName <<
"(scope: " << scope.
description <<
") lower-bound or upper-bound must be given");
862 if (typeStructure.count(
"lower-bound") > 0) {
863 lowerboundExpr = parseExpression(typeStructure.at(
"lower-bound"), scope.
refine(
"Lower bound for variable " + variableName));
866 if (typeStructure.count(
"upper-bound") > 0) {
867 upperboundExpr = parseExpression(typeStructure.at(
"upper-bound"), scope.
refine(
"Upper bound for variable " + variableName));
869 STORM_LOG_THROW(typeStructure.count(
"base") == 1, storm::exceptions::InvalidJaniException,
870 "For bounded type as in variable " << variableName <<
"(scope: " << scope.
description <<
") base must be given");
871 std::string basictype =
872 getString<ValueType>(typeStructure.at(
"base"),
"base for bounded type as in variable " + variableName +
"(scope: " + scope.
description +
") ");
873 if (basictype ==
"int") {
875 "Lower bound for bounded integer variable " << variableName <<
"(scope: " << scope.
description <<
") must be integer-typed");
877 "Upper bound for bounded integer variable " << variableName <<
"(scope: " << scope.
description <<
") must be integer-typed");
881 "Lower bound must not be larger than upper bound for bounded integer variable " << variableName
885 result.second = expressionManager->getIntegerType();
886 }
else if (basictype ==
"real") {
888 "Lower bound for bounded real variable " << variableName <<
"(scope: " << scope.
description <<
") must be numeric");
890 "Upper bound for bounded real variable " << variableName <<
"(scope: " << scope.
description <<
") must be numeric");
893 using SubMap = std::map<storm::expressions::Variable, storm::expressions::Expression>;
897 STORM_LOG_THROW(lowerboundValue <= upperboundValue, storm::exceptions::InvalidJaniException,
898 "Lower bound must not be larger than upper bound for bounded real variable " << variableName
902 result.second = expressionManager->getRationalType();
905 "Unsupported base " << basictype <<
" for bounded variable " << variableName <<
"(scope: " << scope.
description <<
").");
907 }
else if (kind ==
"array") {
908 STORM_LOG_THROW(typeStructure.count(
"base") == 1, storm::exceptions::InvalidJaniException,
909 "For array type as in variable " << variableName <<
"(scope: " << scope.
description <<
") base must be given");
910 auto base = parseType(typeStructure.at(
"base"), variableName, scope);
911 result.first = std::make_unique<storm::jani::ArrayType>(std::move(base.first));
912 result.second = expressionManager->getArrayType(base.second);
915 "Unsupported kind " << kind <<
" for complex type of variable " << variableName <<
"(scope: " << scope.
description <<
").");
921template<
typename ValueType>
923 std::string
const& parameterNamePrefix) {
924 STORM_LOG_THROW(functionDefinitionStructure.count(
"name") == 1, storm::exceptions::InvalidJaniException,
925 "Function definition (scope: " + scope.description +
") must have a name");
926 std::string functionName = getString<ValueType>(functionDefinitionStructure.at(
"name"),
"function-name in " + scope.description);
927 STORM_LOG_THROW(functionDefinitionStructure.count(
"type") == 1, storm::exceptions::InvalidJaniException,
928 "Function definition '" + functionName +
"' (scope: " + scope.description +
") must have a (single) type-declaration.");
929 auto type = parseType(functionDefinitionStructure.at(
"type"), functionName, scope);
931 !(type.first->isClockType() || type.first->isContinuousType()), storm::exceptions::InvalidJaniException,
932 "Function definition '" + functionName +
"' (scope: " + scope.description +
") uses illegal type '" + type.first->getStringRepresentation() +
"'.");
934 std::unordered_map<std::string, storm::expressions::Variable> parameterNameToVariableMap;
935 std::vector<storm::expressions::Variable> parameters;
936 if (!firstPass && functionDefinitionStructure.count(
"parameters") > 0) {
937 STORM_LOG_THROW(functionDefinitionStructure.count(
"parameters") == 1, storm::exceptions::InvalidJaniException,
938 "Function definition '" + functionName +
"' (scope: " + scope.description +
") must have exactly one list of parameters.");
939 for (
auto const& parameterStructure : functionDefinitionStructure.at(
"parameters")) {
940 STORM_LOG_THROW(parameterStructure.count(
"name") == 1, storm::exceptions::InvalidJaniException,
941 "Parameter declaration of parameter " + std::to_string(parameters.size()) +
" of Function definition '" + functionName +
942 "' (scope: " + scope.description +
") must have a name");
943 std::string parameterName =
944 getString<ValueType>(parameterStructure.at(
"name"),
"parameter-name of parameter " + std::to_string(parameters.size()) +
945 " of Function definition '" + functionName +
"' (scope: " + scope.description +
").");
946 STORM_LOG_THROW(parameterStructure.count(
"type") == 1, storm::exceptions::InvalidJaniException,
947 "Parameter declaration of parameter " + std::to_string(parameters.size()) +
" of Function definition '" + functionName +
948 "' (scope: " + scope.description +
") must have exactly one type.");
950 parseType(parameterStructure.at(
"type"), parameterName,
951 scope.refine(
"parameter declaration of parameter " + std::to_string(parameters.size()) +
" of Function definition " + functionName));
952 STORM_LOG_THROW(!(parameterType.first->isClockType() || parameterType.first->isContinuousType()), storm::exceptions::InvalidJaniException,
953 "Type of parameter " + std::to_string(parameters.size()) +
" of function definition '" + functionName +
954 "' (scope: " + scope.description +
") uses illegal type '" + parameterType.first->getStringRepresentation() +
"'.");
956 "Bounds on parameter" + parameterName +
" of function definition " + functionName +
" will be ignored.");
959 parameters.push_back(expressionManager->declareVariable(exprParameterName, parameterType.second));
960 parameterNameToVariableMap.emplace(parameterName, parameters.back());
964 STORM_LOG_THROW(functionDefinitionStructure.count(
"body") == 1, storm::exceptions::InvalidJaniException,
965 "Function definition '" + functionName +
"' (scope: " + scope.description +
") must have a (single) body.");
968 functionBody = parseExpression(functionDefinitionStructure.at(
"body"), scope.refine(
"body of function definition " + functionName),
false,
969 parameterNameToVariableMap);
971 "Type of body of function " + functionName +
"' (scope: " + scope.description +
") has type "
972 << functionBody.
getType() <<
" although the function type is given as " << type.second);
977template<
typename ValueType>
979 STORM_LOG_THROW(variableStructure.count(
"name") == 1, storm::exceptions::InvalidJaniException,
980 "Variable (scope: " + scope.
description +
") must have a name");
981 std::string name = getString<ValueType>(variableStructure.at(
"name"),
"variable-name in " + scope.
description +
"-scope");
984 std::string exprManagerName = namePrefix + name;
985 bool transientVar = defaultVariableTransient;
986 uint_fast64_t tvarcount = variableStructure.count(
"transient");
987 STORM_LOG_THROW(tvarcount <= 1, storm::exceptions::InvalidJaniException,
988 "Multiple definitions of transient not allowed in variable '" + name +
"' (scope: " + scope.
description +
").");
989 if (tvarcount == 1) {
991 getBoolean<ValueType>(variableStructure.at(
"transient"),
"transient-attribute in variable '" + name +
"' (scope: " + scope.
description +
").");
993 STORM_LOG_THROW(variableStructure.count(
"type") == 1, storm::exceptions::InvalidJaniException,
994 "Variable '" + name +
"' (scope: " + scope.
description +
") must have a (single) type-declaration.");
995 auto type = parseType(variableStructure.at(
"type"), name, scope);
997 uint_fast64_t initvalcount = variableStructure.count(
"initial-value");
999 STORM_LOG_THROW(initvalcount == 1, storm::exceptions::InvalidJaniException,
1000 "Initial value must be given once for transient variable '" + name +
"' (scope: " + scope.
description +
") " + name +
1003 STORM_LOG_THROW(initvalcount <= 1, storm::exceptions::InvalidJaniException,
1004 "Initial value can be given at most one for variable " + name +
"' (scope: " + scope.
description +
").");
1006 boost::optional<storm::expressions::Expression> initVal;
1007 if (initvalcount == 1 && !variableStructure.at(
"initial-value").is_null()) {
1008 initVal = parseExpression(variableStructure.at(
"initial-value"), scope.
refine(
"Initial value for variable " + name));
1013 assert(!transientVar);
1016 if (transientVar && type.first->isBasicType() && type.first->asBasicType().isBooleanType()) {
1017 labels.insert(name);
1020 auto expressionVariable = expressionManager->declareVariable(exprManagerName, type.second);
1028 STORM_LOG_THROW(expected == actual, storm::exceptions::InvalidJaniException,
1029 "Operator " << opstring <<
" expects " << expected <<
" arguments, but got " << actual <<
" in " << errorInfo <<
".");
1032template<
typename ValueType>
1033std::vector<storm::expressions::Expression> JaniParser<ValueType>::parseUnaryExpressionArguments(
1034 Json
const& expressionDecl, std::string
const& opstring, Scope
const& scope,
bool returnNoneInitializedOnUnknownOperator,
1035 std::unordered_map<std::string, storm::expressions::Variable>
const& auxiliaryVariables) {
1037 parseExpression(expressionDecl.at(
"exp"), scope.refine(
"Argument of operator " + opstring), returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1041template<
typename ValueType>
1042std::vector<storm::expressions::Expression> JaniParser<ValueType>::parseBinaryExpressionArguments(
1043 Json
const& expressionDecl, std::string
const& opstring, Scope
const& scope,
bool returnNoneInitializedOnUnknownOperator,
1044 std::unordered_map<std::string, storm::expressions::Variable>
const& auxiliaryVariables) {
1046 returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1048 returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1049 return {left, right};
1056 "Operator " << opstring <<
" expects argument[" << argNr <<
"]: '" << expr <<
"' to be Boolean in " << errorInfo <<
".");
1064 "Operator " << opstring <<
" expects argument " + std::to_string(argNr) +
" to be numerical in " << errorInfo <<
".");
1072 "Operator " << opstring <<
" expects argument " + std::to_string(argNr) +
" to be numerical in " << errorInfo <<
".");
1080 "Operator " << opstring <<
" expects argument " + std::to_string(argNr) +
" to be of type 'array' in " << errorInfo <<
".");
1083template<
typename ValueType>
1085 if (lValueStructure.is_string()) {
1086 std::string ident = getString<ValueType>(lValueStructure, scope.
description);
1089 auto localVar = scope.
localVars->find(ident);
1090 if (localVar != scope.
localVars->end()) {
1091 var = localVar->second;
1094 if (var ==
nullptr) {
1096 "Unknown identifier '" << ident <<
"' occurs in " << scope.
description);
1097 auto globalVar = scope.
globalVars->find(ident);
1099 "Unknown identifier '" << ident <<
"' occurs in " << scope.
description);
1100 var = globalVar->second;
1104 }
else if (lValueStructure.count(
"op") == 1) {
1108 std::string opstring = getString<ValueType>(lValueStructure.at(
"op"), scope.
description);
1109 STORM_LOG_THROW(opstring ==
"aa", storm::exceptions::InvalidJaniException,
"Unknown operation '" << opstring <<
"' occurs in " << scope.
description);
1110 STORM_LOG_THROW(lValueStructure.count(
"exp") == 1, storm::exceptions::InvalidJaniException,
"Missing 'exp' in array access at " << scope.
description);
1111 auto expLValue = parseLValue(lValueStructure.at(
"exp"), scope.
refine(
"Expression of array access"));
1112 STORM_LOG_THROW(expLValue.isArray(), storm::exceptions::InvalidJaniException,
"Array access considers non-array expression at " << scope.
description);
1113 STORM_LOG_THROW(lValueStructure.count(
"index"), storm::exceptions::InvalidJaniException,
"Missing 'index' in array access at " << scope.
description);
1114 auto indexExpression = parseExpression(lValueStructure.at(
"index"), scope.
refine(
"Index of array access"));
1115 expLValue.addArrayAccessIndex(indexExpression);
1118 STORM_LOG_THROW(
false, storm::exceptions::InvalidJaniException,
"Unknown LValue '" << lValueStructure.dump() <<
"' occurs in " << scope.
description);
1124template<
typename ValueType>
1126 std::string
const& ident, Scope
const& scope, std::unordered_map<std::string, storm::expressions::Variable>
const& auxiliaryVariables) {
1128 auto it = auxiliaryVariables.find(ident);
1129 if (it != auxiliaryVariables.end()) {
1133 if (scope.localVars !=
nullptr) {
1134 auto it = scope.localVars->find(ident);
1135 if (it != scope.localVars->end()) {
1136 return it->second->getExpressionVariable();
1139 if (scope.globalVars !=
nullptr) {
1140 auto it = scope.globalVars->find(ident);
1141 if (it != scope.globalVars->end()) {
1142 return it->second->getExpressionVariable();
1145 if (scope.constants !=
nullptr) {
1146 auto it = scope.constants->find(ident);
1147 if (it != scope.constants->end()) {
1148 return it->second->getExpressionVariable();
1151 STORM_LOG_THROW(
false, storm::exceptions::InvalidJaniException,
"Unknown identifier '" << ident <<
"' occurs in " << scope.description);
1156template<
typename ValueType>
1158 bool returnNoneInitializedOnUnknownOperator,
1159 std::unordered_map<std::string, storm::expressions::Variable>
const& auxiliaryVariables) {
1160 if (expressionStructure.is_boolean()) {
1161 if (expressionStructure.template get<bool>()) {
1162 return expressionManager->boolean(
true);
1164 return expressionManager->boolean(
false);
1166 }
else if (expressionStructure.is_number_integer()) {
1167 return expressionManager->integer(expressionStructure.template get<int64_t>());
1168 }
else if (expressionStructure.is_number_float()) {
1169 return expressionManager->rational(storm::utility::convertNumber<storm::RationalNumber>(expressionStructure.template get<ValueType>()));
1170 }
else if (expressionStructure.is_string()) {
1171 std::string ident = expressionStructure.template get<std::string>();
1173 }
else if (expressionStructure.is_object()) {
1174 if (expressionStructure.count(
"distribution") == 1) {
1176 false, storm::exceptions::InvalidJaniException,
1177 "Distributions are not supported by storm expressions, cannot import " << expressionStructure.dump() <<
" in " << scope.
description <<
".");
1179 if (expressionStructure.count(
"op") == 1) {
1180 std::string opstring = getString<ValueType>(expressionStructure.at(
"op"), scope.
description);
1181 std::vector<storm::expressions::Expression> arguments = {};
1182 if (opstring ==
"ite") {
1183 STORM_LOG_THROW(expressionStructure.count(
"if") == 1, storm::exceptions::InvalidJaniException,
"If operator required");
1184 STORM_LOG_THROW(expressionStructure.count(
"else") == 1, storm::exceptions::InvalidJaniException,
"Else operator required");
1185 STORM_LOG_THROW(expressionStructure.count(
"then") == 1, storm::exceptions::InvalidJaniException,
"Then operator required");
1186 arguments.push_back(
1187 parseExpression(expressionStructure.at(
"if"), scope.
refine(
"if-formula"), returnNoneInitializedOnUnknownOperator, auxiliaryVariables));
1188 arguments.push_back(
1189 parseExpression(expressionStructure.at(
"then"), scope.
refine(
"then-formula"), returnNoneInitializedOnUnknownOperator, auxiliaryVariables));
1190 arguments.push_back(
1191 parseExpression(expressionStructure.at(
"else"), scope.
refine(
"else-formula"), returnNoneInitializedOnUnknownOperator, auxiliaryVariables));
1193 assert(arguments.size() == 3);
1196 }
else if (opstring ==
"∨") {
1197 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1198 assert(arguments.size() == 2);
1199 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1204 return arguments[0] || arguments[1];
1205 }
else if (opstring ==
"∧") {
1206 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1207 assert(arguments.size() == 2);
1208 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1213 return arguments[0] && arguments[1];
1214 }
else if (opstring ==
"⇒") {
1215 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1216 assert(arguments.size() == 2);
1217 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1222 return (!arguments[0]) || arguments[1];
1223 }
else if (opstring ==
"¬") {
1224 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1225 assert(arguments.size() == 1);
1226 if (!arguments[0].isInitialized()) {
1230 return !arguments[0];
1231 }
else if (opstring ==
"=") {
1232 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1233 assert(arguments.size() == 2);
1234 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1237 if (arguments[0].hasBooleanType()) {
1242 return arguments[0] == arguments[1];
1244 }
else if (opstring ==
"≠") {
1245 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1246 assert(arguments.size() == 2);
1247 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1250 if (arguments[0].hasBooleanType()) {
1255 return arguments[0] != arguments[1];
1257 }
else if (opstring ==
"<") {
1258 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1259 assert(arguments.size() == 2);
1260 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1265 return arguments[0] < arguments[1];
1266 }
else if (opstring ==
"≤") {
1267 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1268 assert(arguments.size() == 2);
1269 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1274 return arguments[0] <= arguments[1];
1275 }
else if (opstring ==
">") {
1276 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1277 assert(arguments.size() == 2);
1278 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1283 return arguments[0] > arguments[1];
1284 }
else if (opstring ==
"≥") {
1285 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1286 assert(arguments.size() == 2);
1287 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1292 return arguments[0] >= arguments[1];
1293 }
else if (opstring ==
"+") {
1294 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1295 assert(arguments.size() == 2);
1298 return arguments[0] + arguments[1];
1299 }
else if (opstring ==
"-" && expressionStructure.count(
"left") > 0) {
1300 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1301 assert(arguments.size() == 2);
1304 return arguments[0] - arguments[1];
1305 }
else if (opstring ==
"-") {
1306 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1307 assert(arguments.size() == 1);
1309 return -arguments[0];
1310 }
else if (opstring ==
"*") {
1311 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1312 assert(arguments.size() == 2);
1315 return arguments[0] * arguments[1];
1316 }
else if (opstring ==
"/") {
1317 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1318 assert(arguments.size() == 2);
1321 return arguments[0] / arguments[1];
1322 }
else if (opstring ==
"%") {
1323 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1324 assert(arguments.size() == 2);
1327 return arguments[0] % arguments[1];
1328 }
else if (opstring ==
"max") {
1329 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1330 assert(arguments.size() == 2);
1334 }
else if (opstring ==
"min") {
1335 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1336 assert(arguments.size() == 2);
1340 }
else if (opstring ==
"floor") {
1341 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1342 assert(arguments.size() == 1);
1345 }
else if (opstring ==
"ceil") {
1346 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1347 assert(arguments.size() == 1);
1350 }
else if (opstring ==
"abs") {
1351 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1352 assert(arguments.size() == 1);
1355 }
else if (opstring ==
"sgn") {
1356 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1357 assert(arguments.size() == 1);
1360 }
else if (opstring ==
"trc") {
1361 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1362 assert(arguments.size() == 1);
1365 }
else if (opstring ==
"pow") {
1366 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1367 assert(arguments.size() == 2);
1371 }
else if (opstring ==
"exp") {
1372 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1373 assert(arguments.size() == 2);
1377 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"exp operation is not yet implemented");
1378 }
else if (opstring ==
"log") {
1379 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1380 assert(arguments.size() == 2);
1384 }
else if (opstring ==
"cos") {
1385 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1386 assert(arguments.size() == 1);
1389 }
else if (opstring ==
"sin") {
1390 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1391 assert(arguments.size() == 1);
1394 }
else if (opstring ==
"aa") {
1395 STORM_LOG_THROW(expressionStructure.count(
"exp") == 1, storm::exceptions::InvalidJaniException,
1396 "Array access operator requires exactly one exp (at " + scope.
description +
").");
1398 returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1399 STORM_LOG_THROW(expressionStructure.count(
"index") == 1, storm::exceptions::InvalidJaniException,
1400 "Array access operator requires exactly one index (at " + scope.
description +
").");
1402 returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1408 }
else if (opstring ==
"av") {
1409 STORM_LOG_THROW(expressionStructure.count(
"elements") == 1, storm::exceptions::InvalidJaniException,
1410 "Array value operator requires exactly one 'elements' (at " + scope.
description +
").");
1411 std::vector<std::shared_ptr<storm::expressions::BaseExpression const>> elements;
1414 for (
auto const& element : expressionStructure.at(
"elements")) {
1415 elements.push_back(parseExpression(element, scope.
refine(
"element " + std::to_string(elements.size()) +
" of array value expression"),
1416 returnNoneInitializedOnUnknownOperator, auxiliaryVariables)
1417 .getBaseExpressionPointer());
1419 commonType = elements.back()->getType();
1421 }
else if (!(commonType == elements.back()->getType())) {
1422 if (commonType.
isIntegerType() && elements.back()->getType().isRationalType()) {
1423 commonType = elements.back()->getType();
1426 "Incompatible element types " << commonType <<
" and " << elements.back()->getType()
1427 <<
" of array value expression at " << scope.
description);
1431 return std::make_shared<storm::expressions::ValueArrayExpression>(*expressionManager, expressionManager->getArrayType(commonType), elements)
1433 }
else if (opstring ==
"ac") {
1434 STORM_LOG_THROW(expressionStructure.count(
"length") == 1, storm::exceptions::InvalidJaniException,
1435 "Array access operator requires exactly one length (at " + scope.
description +
").");
1437 returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1439 STORM_LOG_THROW(expressionStructure.count(
"var") == 1, storm::exceptions::InvalidJaniException,
1440 "Array access operator requires exactly one var (at " + scope.
description +
").");
1441 std::string indexVarName =
1442 getString<ValueType>(expressionStructure.at(
"var"),
"Field 'var' of Array access operator (at " + scope.
description +
").");
1443 STORM_LOG_THROW(auxiliaryVariables.find(indexVarName) == auxiliaryVariables.end(), storm::exceptions::InvalidJaniException,
1444 "Index variable " << indexVarName <<
" is already defined as an auxiliary variable (at " + scope.
description +
").");
1445 auto newAuxVars = auxiliaryVariables;
1447 newAuxVars.emplace(indexVarName, indexVar);
1448 STORM_LOG_THROW(expressionStructure.count(
"exp") == 1, storm::exceptions::InvalidJaniException,
1449 "Array constructor operator requires exactly one exp (at " + scope.
description +
").");
1451 returnNoneInitializedOnUnknownOperator, newAuxVars);
1452 return std::make_shared<storm::expressions::ConstructorArrayExpression>(*expressionManager, expressionManager->getArrayType(exp.
getType()),
1456 }
else if (opstring ==
"call") {
1457 STORM_LOG_THROW(expressionStructure.count(
"function") == 1, storm::exceptions::InvalidJaniException,
1458 "Function call operator requires exactly one function (at " + scope.
description +
").");
1459 std::string functionName =
1460 getString<ValueType>(expressionStructure.at(
"function"),
"in function call operator (at " + scope.
description +
").");
1468 "Function call operator calls unknown function '" + functionName +
"' (at " + scope.
description +
").");
1470 STORM_LOG_THROW(expressionStructure.count(
"args") == 1, storm::exceptions::InvalidJaniException,
1471 "Function call operator requires exactly one args (at " + scope.
description +
").");
1472 std::vector<std::shared_ptr<storm::expressions::BaseExpression const>> args;
1473 if (expressionStructure.count(
"args") > 0) {
1474 STORM_LOG_THROW(expressionStructure.count(
"args") == 1, storm::exceptions::InvalidJaniException,
1475 "Function call operator requires exactly one args (at " + scope.
description +
").");
1476 for (
auto const& arg : expressionStructure.at(
"args")) {
1477 args.push_back(parseExpression(arg, scope.
refine(
"argument " + std::to_string(args.size()) +
" of function call expression"),
1478 returnNoneInitializedOnUnknownOperator, auxiliaryVariables)
1479 .getBaseExpressionPointer());
1482 return std::make_shared<storm::expressions::FunctionCallExpression>(*expressionManager, functionDefinition->
getType(), functionName, args)
1484 }
else if (unsupportedOpstrings.count(opstring) > 0) {
1485 STORM_LOG_THROW(
false, storm::exceptions::InvalidJaniException,
"Opstring " + opstring +
" is not supported by storm");
1487 if (returnNoneInitializedOnUnknownOperator) {
1490 STORM_LOG_THROW(
false, storm::exceptions::InvalidJaniException,
"Unknown operator " << opstring <<
" in " << scope.
description <<
".");
1493 if (expressionStructure.count(
"constant") == 1) {
1495 const std::string constantStr = getString<ValueType>(expressionStructure.at(
"constant"), scope.
description);
1496 if (constantStr ==
"Ï€") {
1497 return std::make_shared<storm::expressions::TranscendentalNumberLiteralExpression>(
1501 if (constantStr ==
"e") {
1502 return std::make_shared<storm::expressions::TranscendentalNumberLiteralExpression>(
1508 false, storm::exceptions::InvalidJaniException,
1509 "No supported operator declaration found for complex expressions as " << expressionStructure.dump() <<
" in " << scope.
description <<
".");
1512 "No supported expression found at " << expressionStructure.dump() <<
" in " << scope.
description <<
".");
1517template<
typename ValueType>
1519 std::set<std::string> actionNames;
1520 for (
auto const& actionEntry : actionStructure) {
1521 STORM_LOG_THROW(actionEntry.count(
"name") == 1, storm::exceptions::InvalidJaniException,
"Actions must have exactly one name.");
1522 std::string actionName = getString<ValueType>(actionEntry.at(
"name"),
"name of action");
1523 STORM_LOG_THROW(actionNames.count(actionName) == 0, storm::exceptions::InvalidJaniException,
"Action with name " + actionName +
" already exists.");
1525 actionNames.emplace(actionName);
1529template<
typename ValueType>
1531 STORM_LOG_THROW(automatonStructure.count(
"name") == 1, storm::exceptions::InvalidJaniException,
"Each automaton must have a name");
1532 std::string name = getString<ValueType>(automatonStructure.at(
"name"),
" the name field for automaton");
1536 uint64_t varDeclCount = automatonStructure.count(
"variables");
1537 STORM_LOG_THROW(varDeclCount < 2, storm::exceptions::InvalidJaniException,
"Automaton '" << name <<
"' has more than one list of variables");
1540 if (varDeclCount > 0) {
1541 for (
auto const& varStructure : automatonStructure.at(
"variables")) {
1542 std::shared_ptr<storm::jani::Variable> var = parseVariable(
1544 assert(localVars.count(var->getName()) == 0);
1545 localVars.emplace(var->getName(), &automaton.
addVariable(*var));
1549 uint64_t funDeclCount = automatonStructure.count(
"functions");
1550 STORM_LOG_THROW(funDeclCount < 2, storm::exceptions::InvalidJaniException,
"Automaton '" << name <<
"' has more than one list of functions");
1553 if (funDeclCount > 0) {
1555 std::vector<storm::jani::FunctionDefinition> dummyFunctionDefinitions;
1556 for (
auto const& funStructure : automatonStructure.at(
"functions")) {
1558 dummyFunctionDefinitions.push_back(
1559 parseFunctionDefinition(funStructure, scope.
refine(
"functions[" + std::to_string(localFuns.size()) +
"] of automaton " + name),
true));
1563 for (
auto const& funDef : dummyFunctionDefinitions) {
1564 bool unused = localFuns.emplace(funDef.getName(), &funDef).second;
1566 "Multiple definitions of functions with the name " << funDef.getName() <<
" in " << scope.
description);
1568 for (
auto const& funStructure : automatonStructure.at(
"functions")) {
1571 parseFunctionDefinition(funStructure, scope.
refine(
"functions[" + std::to_string(localFuns.size()) +
"] of automaton " + name),
false,
1573 assert(localFuns.count(funDef.
getName()) == 1);
1578 STORM_LOG_THROW(automatonStructure.count(
"locations") > 0, storm::exceptions::InvalidJaniException,
"Automaton '" << name <<
"' does not have locations.");
1579 std::unordered_map<std::string, uint64_t> locIds;
1580 for (
auto const& locEntry : automatonStructure.at(
"locations")) {
1581 STORM_LOG_THROW(locEntry.count(
"name") == 1, storm::exceptions::InvalidJaniException,
1582 "Locations for automaton '" << name <<
"' must have exactly one name");
1583 std::string locName = getString<ValueType>(locEntry.at(
"name"),
"location of automaton " + name);
1584 STORM_LOG_THROW(locIds.count(locName) == 0, storm::exceptions::InvalidJaniException,
1585 "Location with name '" + locName +
"' already exists in automaton '" + name +
"'");
1586 STORM_LOG_THROW(locEntry.count(
"invariant") == 0, storm::exceptions::InvalidJaniException,
1587 "Invariants in locations as in '" + locName +
"' in automaton '" + name +
"' are not supported");
1590 std::vector<storm::jani::Assignment> transientAssignments;
1591 if (locEntry.count(
"transient-values") > 0) {
1592 for (
auto const& transientValueEntry : locEntry.at(
"transient-values")) {
1593 STORM_LOG_THROW(transientValueEntry.count(
"ref") == 1, storm::exceptions::InvalidJaniException,
1594 "Transient values in location " << locName <<
" need exactly one ref that is assigned to");
1595 STORM_LOG_THROW(transientValueEntry.count(
"value") == 1, storm::exceptions::InvalidJaniException,
1596 "Transient values in location " << locName <<
" need exactly one assigned value");
1597 storm::jani::LValue lValue = parseLValue(transientValueEntry.at(
"ref"), scope.
refine(
"LHS of assignment in location " + locName));
1599 "Assigned non-transient variable " << lValue <<
" in location " + locName +
" (automaton: '" + name +
"').");
1601 parseExpression(transientValueEntry.at(
"value"), scope.
refine(
"Assignment of lValue in location " + locName));
1602 transientAssignments.emplace_back(lValue, rhs);
1606 locIds.emplace(locName,
id);
1608 STORM_LOG_THROW(automatonStructure.count(
"initial-locations") == 1, storm::exceptions::InvalidJaniException,
1609 "Automaton '" << name <<
"' does not have initial locations.");
1610 for (
Json const& initLocStruct : automatonStructure.at(
"initial-locations")) {
1611 automaton.
addInitialLocation(getString<ValueType>(initLocStruct,
"Initial locations for automaton '" + name +
"'."));
1613 STORM_LOG_THROW(automatonStructure.count(
"restrict-initial") < 2, storm::exceptions::InvalidJaniException,
1614 "Automaton '" << name <<
"' has multiple initial value restrictions");
1616 if (automatonStructure.count(
"restrict-initial") > 0) {
1617 STORM_LOG_THROW(automatonStructure.at(
"restrict-initial").count(
"exp") == 1, storm::exceptions::InvalidJaniException,
1618 "Automaton '" << name <<
"' needs an expression inside the initial restricion");
1619 initialValueRestriction = parseExpression(automatonStructure.at(
"restrict-initial").at(
"exp"), scope.
refine(
"Initial value restriction"));
1623 STORM_LOG_THROW(automatonStructure.count(
"edges") > 0, storm::exceptions::InvalidJaniException,
"Automaton '" << name <<
"' must have a list of edges");
1624 for (
auto const& edgeEntry : automatonStructure.at(
"edges")) {
1626 STORM_LOG_THROW(edgeEntry.count(
"location") == 1, storm::exceptions::InvalidJaniException,
1627 "Each edge in automaton '" << name <<
"' must have a source");
1628 std::string sourceLoc = getString<ValueType>(edgeEntry.at(
"location"),
"source location for edge in automaton '" + name +
"'");
1629 STORM_LOG_THROW(locIds.count(sourceLoc) == 1, storm::exceptions::InvalidJaniException,
1630 "Source of edge has unknown location '" << sourceLoc <<
"' in automaton '" << name <<
"'.");
1632 STORM_LOG_THROW(edgeEntry.count(
"action") < 2, storm::exceptions::InvalidJaniException,
1633 "Edge from " << sourceLoc <<
" in automaton " << name <<
" has multiple actions");
1635 if (edgeEntry.count(
"action") > 0) {
1636 action = getString<ValueType>(edgeEntry.at(
"action"),
"action name in edge from '" + sourceLoc +
"' in automaton '" + name +
"'");
1638 assert(action !=
"");
1641 STORM_LOG_THROW(edgeEntry.count(
"rate") < 2, storm::exceptions::InvalidJaniException,
1642 "Edge from '" << sourceLoc <<
"' in automaton '" << name <<
"' has multiple rates");
1644 if (edgeEntry.count(
"rate") > 0) {
1645 STORM_LOG_THROW(edgeEntry.at(
"rate").count(
"exp") == 1, storm::exceptions::InvalidJaniException,
1646 "Rate in edge from '" << sourceLoc <<
"' in automaton '" << name <<
"' must have a defing expression.");
1647 rateExpr = parseExpression(edgeEntry.at(
"rate").at(
"exp"), scope.
refine(
"rate expression in edge from '" + sourceLoc));
1650 storm::exceptions::InvalidJaniException,
"Only positive rates are allowed but rate '" << rateExpr <<
" was found.");
1653 STORM_LOG_THROW(edgeEntry.count(
"guard") <= 1, storm::exceptions::InvalidJaniException,
1654 "Guard can be given at most once in edge from '" << sourceLoc <<
"' in automaton '" << name <<
"'");
1656 if (edgeEntry.count(
"guard") == 1) {
1657 STORM_LOG_THROW(edgeEntry.at(
"guard").count(
"exp") == 1, storm::exceptions::InvalidJaniException,
1658 "Guard in edge from '" + sourceLoc +
"' in automaton '" + name +
"' must have one expression");
1659 guardExpr = parseExpression(edgeEntry.at(
"guard").at(
"exp"), scope.
refine(
"guard expression in edge from '" + sourceLoc));
1660 STORM_LOG_THROW(guardExpr.
hasBooleanType(), storm::exceptions::InvalidJaniException,
"Guard " << guardExpr <<
" does not have Boolean type.");
1663 std::shared_ptr<storm::jani::TemplateEdge> templateEdge = std::make_shared<storm::jani::TemplateEdge>(guardExpr);
1666 if (edgeEntry.count(
"assignments") > 0) {
1667 STORM_LOG_THROW(edgeEntry.count(
"assignments") == 1, storm::exceptions::InvalidJaniException,
1668 "Multiple edge assignments in edge from '" + sourceLoc +
"' in automaton '" + name +
"'.");
1669 for (
auto const& assignmentEntry : edgeEntry.at(
"assignments")) {
1671 STORM_LOG_THROW(assignmentEntry.count(
"ref") == 1, storm::exceptions::InvalidJaniException,
1672 "Assignment in edge from '" << sourceLoc <<
"' in automaton '" << name <<
"'must have one ref field");
1674 parseLValue(assignmentEntry.at(
"ref"), scope.
refine(
"Assignment variable in edge from '" + sourceLoc +
"' in automaton '" + name +
"'"));
1676 STORM_LOG_THROW(assignmentEntry.count(
"value") == 1, storm::exceptions::InvalidJaniException,
1677 "Assignment in edge from '" << sourceLoc <<
"' in automaton '" << name <<
"' must have one value field");
1679 parseExpression(assignmentEntry.at(
"value"), scope.
refine(
"assignment in edge from '" + sourceLoc +
"' in automaton '" + name +
"'"));
1682 int64_t assignmentIndex = 0;
1683 if (assignmentEntry.count(
"index") > 0) {
1685 getSignedInt<ValueType>(assignmentEntry.at(
"index"),
"assignment index in edge from '" + sourceLoc +
"' in automaton '" + name +
"'");
1692 STORM_LOG_THROW(edgeEntry.count(
"destinations") == 1, storm::exceptions::InvalidJaniException,
1693 "A single list of destinations must be given in edge from '" << sourceLoc <<
"' in automaton '" << name <<
"'");
1694 std::vector<std::pair<uint64_t, storm::expressions::Expression>> destinationLocationsAndProbabilities;
1695 for (
auto const& destEntry : edgeEntry.at(
"destinations")) {
1697 STORM_LOG_THROW(destEntry.count(
"location") == 1, storm::exceptions::InvalidJaniException,
1698 "Each destination in edge from '" << sourceLoc <<
"' in automaton '" << name <<
"' must have a target location");
1699 std::string targetLoc =
1700 getString<ValueType>(destEntry.at(
"location"),
"target location for edge from '" + sourceLoc +
"' in automaton '" + name +
"'");
1701 STORM_LOG_THROW(locIds.count(targetLoc) == 1, storm::exceptions::InvalidJaniException,
1702 "Target of edge has unknown location '" << targetLoc <<
"' in automaton '" << name <<
"'.");
1705 unsigned probDeclCount = destEntry.count(
"probability");
1706 STORM_LOG_THROW(probDeclCount < 2, storm::exceptions::InvalidJaniException,
1707 "Destination in edge from '" << sourceLoc <<
"' to '" << targetLoc <<
"' in automaton '" << name <<
"' has multiple probabilites");
1708 if (probDeclCount == 0) {
1709 probExpr = expressionManager->rational(1.0);
1711 STORM_LOG_THROW(destEntry.at(
"probability").count(
"exp") == 1, storm::exceptions::InvalidJaniException,
1712 "Destination in edge from '" << sourceLoc <<
"' to '" << targetLoc <<
"' in automaton '" << name
1713 <<
"' must have a probability expression.");
1714 probExpr = parseExpression(destEntry.at(
"probability").at(
"exp"), scope.
refine(
"probability expression in edge from '" + sourceLoc +
"' to '" +
1715 targetLoc +
"' in automaton '" + name +
"'"));
1719 "Probability expression " << probExpr <<
" does not have a numerical type.");
1721 std::vector<storm::jani::Assignment> assignments;
1722 unsigned assignmentDeclCount = destEntry.count(
"assignments");
1724 assignmentDeclCount < 2, storm::exceptions::InvalidJaniException,
1725 "Destination in edge from '" << sourceLoc <<
"' to '" << targetLoc <<
"' in automaton '" << name <<
"' has multiple assignment lists");
1726 if (assignmentDeclCount > 0) {
1727 for (
auto const& assignmentEntry : destEntry.at(
"assignments")) {
1730 assignmentEntry.count(
"ref") == 1, storm::exceptions::InvalidJaniException,
1731 "Assignment in edge from '" << sourceLoc <<
"' to '" << targetLoc <<
"' in automaton '" << name <<
"' must have one ref field");
1732 storm::jani::LValue lValue = parseLValue(assignmentEntry.at(
"ref"), scope.
refine(
"Assignment variable in edge from '" + sourceLoc +
1733 "' to '" + targetLoc +
"' in automaton '" + name +
"'"));
1736 assignmentEntry.count(
"value") == 1, storm::exceptions::InvalidJaniException,
1737 "Assignment in edge from '" << sourceLoc <<
"' to '" << targetLoc <<
"' in automaton '" << name <<
"' must have one value field");
1739 parseExpression(assignmentEntry.at(
"value"),
1740 scope.
refine(
"assignment in edge from '" + sourceLoc +
"' to '" + targetLoc +
"' in automaton '" + name +
"'"));
1743 int64_t assignmentIndex = 0;
1744 if (assignmentEntry.count(
"index") > 0) {
1745 assignmentIndex = getSignedInt<ValueType>(assignmentEntry.at(
"index"),
"assignment index in edge from '" + sourceLoc +
"' to '" +
1746 targetLoc +
"' in automaton '" + name +
"'");
1748 assignments.emplace_back(lValue, assignmentExpr, assignmentIndex);
1751 destinationLocationsAndProbabilities.emplace_back(locIds.at(targetLoc), probExpr);
1755 rateExpr.
isInitialized() ? boost::optional<storm::expressions::Expression>(rateExpr) : boost::none, templateEdge,
1756 destinationLocationsAndProbabilities));
1762template<
typename ValueType>
1764 std::vector<storm::jani::SynchronizationVector> syncVectors;
1766 for (
auto const& syncEntry : syncVectorStructure) {
1767 std::vector<std::string> inputs;
1768 for (
auto const& syncInput : syncEntry.at(
"synchronise")) {
1769 if (syncInput.is_null()) {
1772 inputs.push_back(syncInput);
1775 std::string syncResult;
1776 if (syncEntry.count(
"result")) {
1777 syncResult = syncEntry.at(
"result");
1781 syncVectors.emplace_back(inputs, syncResult);
1786template<
typename ValueType>
1787std::shared_ptr<storm::jani::Composition> JaniParser<ValueType>::parseComposition(Json
const& compositionStructure) {
1788 if (compositionStructure.count(
"automaton")) {
1789 std::set<std::string> inputEnabledActions;
1790 if (compositionStructure.count(
"input-enable")) {
1791 for (
auto const& actionDecl : compositionStructure.at(
"input-enable")) {
1792 inputEnabledActions.insert(actionDecl.template get<std::string>());
1795 return std::shared_ptr<storm::jani::AutomatonComposition>(
1799 STORM_LOG_THROW(compositionStructure.count(
"elements") == 1, storm::exceptions::InvalidJaniException,
1800 "Elements of a composition must be given, got " << compositionStructure.dump());
1802 if (compositionStructure.at(
"elements").size() == 1 && compositionStructure.count(
"syncs") == 0) {
1804 STORM_LOG_THROW(compositionStructure.at(
"elements").back().count(
"automaton") == 1, storm::exceptions::InvalidJaniException,
1805 "Automaton must be given in composition");
1806 if (compositionStructure.at(
"elements").back().at(
"automaton").is_string()) {
1807 std::string name = compositionStructure.at(
"elements").back().at(
"automaton");
1811 STORM_LOG_THROW(
false, storm::exceptions::InvalidJaniException,
"Trivial nesting parallel composition is not yet supported");
1814 std::vector<std::shared_ptr<storm::jani::Composition>> compositions;
1815 for (
auto const& elemDecl : compositionStructure.at(
"elements")) {
1816 if (!allowRecursion) {
1817 STORM_LOG_THROW(elemDecl.count(
"automaton") == 1, storm::exceptions::InvalidJaniException,
"Automaton must be given in the element");
1819 compositions.push_back(parseComposition(elemDecl));
1822 STORM_LOG_THROW(compositionStructure.count(
"syncs") < 2, storm::exceptions::InvalidJaniException,
"Sync vectors can be given at most once");
1823 std::vector<storm::jani::SynchronizationVector> syncVectors;
1824 if (compositionStructure.count(
"syncs") > 0) {
1825 syncVectors = parseSyncVectors<ValueType>(compositionStructure.at(
"syncs"));
1831template class JaniParser<double>;
1832template 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.
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.
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.
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()
@ MultiObjectiveProperties
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