35#include <boost/lexical_cast.hpp>
50template<
typename ValueType>
51const bool JaniParser<ValueType>::defaultVariableTransient =
false;
53template<
typename ValueType>
54const std::set<std::string> JaniParser<ValueType>::unsupportedOpstrings({
"tan",
"cot",
"sec",
"csc",
"asin",
"acos",
"atan",
55 "acot",
"asec",
"acsc",
"sinh",
"cosh",
"tanh",
"coth",
56 "sech",
"csch",
"asinh",
"acosh",
"atanh",
"asinh",
"acosh"});
58template<
typename ValueType>
60 STORM_LOG_THROW(structure.is_string(), storm::exceptions::InvalidJaniException,
61 "Expected a string in " << errorInfo <<
", got '" << structure.dump() <<
"'.");
62 return structure.front();
65template<
typename ValueType>
67 STORM_LOG_THROW(structure.is_boolean(), storm::exceptions::InvalidJaniException,
68 "Expected a Boolean in " << errorInfo <<
", got " << structure.dump() <<
"'.");
69 return structure.front();
72template<
typename ValueType>
74 STORM_LOG_THROW(structure.is_number(), storm::exceptions::InvalidJaniException,
75 "Expected a number in " << errorInfo <<
", got '" << structure.dump() <<
"'.");
76 int64_t num = structure.front();
77 STORM_LOG_THROW(num >= 0, storm::exceptions::InvalidJaniException,
"Expected a positive number in " << errorInfo <<
", got '" << num <<
"'.");
78 return static_cast<uint64_t
>(num);
81template<
typename ValueType>
83 STORM_LOG_THROW(structure.is_number(), storm::exceptions::InvalidJaniException,
84 "Expected a number in " << errorInfo <<
", got '" << structure.dump() <<
"'.");
85 return structure.front();
88template<
typename ValueType>
92 return parser.parseModel(parseProperties);
95template<
typename ValueType>
98 return parser.parseModel(parseProperties);
101template<
typename ValueType>
103 parsedStructure = Json::parse(jsonstring);
106template<
typename ValueType>
110 file >> parsedStructure;
114template<
typename ValueType>
117 STORM_LOG_THROW(parsedStructure.count(
"jani-version") == 1, storm::exceptions::InvalidJaniException,
"Jani-version must be given exactly once.");
119 STORM_LOG_WARN_COND(version >= 1 && version <= 1,
"JANI Version " << version <<
" is not supported. Results may be wrong.");
121 STORM_LOG_THROW(parsedStructure.count(
"name") == 1, storm::exceptions::InvalidJaniException,
"A model must have a (single) name.");
124 STORM_LOG_THROW(parsedStructure.count(
"type") == 1, storm::exceptions::InvalidJaniException,
"A type must be given exactly once.");
125 std::string modeltypestring =
getString<ValueType>(parsedStructure.at(
"type"),
"type of the model");
129 uint_fast64_t featuresCount = parsedStructure.count(
"features");
130 STORM_LOG_THROW(featuresCount < 2, storm::exceptions::InvalidJaniException,
"Features-declarations can be given at most once.");
131 if (featuresCount == 1) {
133 for (
auto const& feature : parsedStructure.at(
"features")) {
136 for (
auto const& knownFeature : allKnownModelFeatures.asSet()) {
143 STORM_LOG_THROW(found, storm::exceptions::NotSupportedException,
"Storm does not support the model feature " << featureStr <<
".");
146 uint_fast64_t actionCount = parsedStructure.count(
"actions");
147 STORM_LOG_THROW(actionCount < 2, storm::exceptions::InvalidJaniException,
"Action-declarations can be given at most once.");
148 if (actionCount > 0) {
149 parseActions(parsedStructure.at(
"actions"), model);
157 uint_fast64_t constantsCount = parsedStructure.count(
"constants");
158 STORM_LOG_THROW(constantsCount < 2, storm::exceptions::InvalidJaniException,
"Constant-declarations can be given at most once.");
159 if (constantsCount == 1) {
161 model.
getConstants().reserve(parsedStructure.at(
"constants").size());
162 for (
auto const& constStructure : parsedStructure.at(
"constants")) {
163 std::shared_ptr<storm::jani::Constant> constant =
164 parseConstant(constStructure, scope.
refine(
"constants[" + std::to_string(constants.size()) +
"]"));
167 constants.emplace(constant->getName(), &model.
getConstants().back());
172 uint_fast64_t variablesCount = parsedStructure.count(
"variables");
173 STORM_LOG_THROW(variablesCount < 2, storm::exceptions::InvalidJaniException,
"Variable-declarations can be given at most once for global variables.");
176 if (variablesCount == 1) {
177 for (
auto const& varStructure : parsedStructure.at(
"variables")) {
178 std::shared_ptr<storm::jani::Variable> variable =
parseVariable(varStructure, scope.
refine(
"variables[" + std::to_string(globalVars.size())));
179 globalVars.emplace(variable->getName(), &model.
addVariable(*variable));
183 uint64_t funDeclCount = parsedStructure.count(
"functions");
184 STORM_LOG_THROW(funDeclCount < 2, storm::exceptions::InvalidJaniException,
"Model '" << name <<
"' has more than one list of functions.");
187 if (funDeclCount > 0) {
189 std::vector<storm::jani::FunctionDefinition> dummyFunctionDefinitions;
190 for (
auto const& funStructure : parsedStructure.at(
"functions")) {
192 dummyFunctionDefinitions.push_back(
193 parseFunctionDefinition(funStructure, scope.
refine(
"functions[" + std::to_string(globalFuns.size()) +
"] of model " + name),
true));
197 for (
auto const& funDef : dummyFunctionDefinitions) {
198 bool unused = globalFuns.emplace(funDef.getName(), &funDef).second;
200 "Multiple definitions of functions with the name " << funDef.getName() <<
" in " << scope.
description <<
".");
202 for (
auto const& funStructure : parsedStructure.at(
"functions")) {
205 parseFunctionDefinition(funStructure, scope.
refine(
"functions[" + std::to_string(globalFuns.size()) +
"] of model " + name),
false);
212 STORM_LOG_THROW(parsedStructure.count(
"automata") == 1, storm::exceptions::InvalidJaniException,
"Exactly one list of automata must be given.");
213 STORM_LOG_THROW(parsedStructure.at(
"automata").is_array(), storm::exceptions::InvalidJaniException,
"Automata must be an array.");
215 for (
auto const& automataEntry : parsedStructure.at(
"automata")) {
218 STORM_LOG_THROW(parsedStructure.count(
"restrict-initial") < 2, storm::exceptions::InvalidJaniException,
"Model has multiple initial value restrictions.");
220 if (parsedStructure.count(
"restrict-initial") > 0) {
221 STORM_LOG_THROW(parsedStructure.at(
"restrict-initial").count(
"exp") == 1, storm::exceptions::InvalidJaniException,
222 "Model needs an expression inside the initial restricion.");
223 initialValueRestriction =
parseExpression(parsedStructure.at(
"restrict-initial").at(
"exp"), scope.
refine(
"Initial value restriction"));
226 STORM_LOG_THROW(parsedStructure.count(
"system") == 1, storm::exceptions::InvalidJaniException,
"Exactly one system description must be given.");
227 std::shared_ptr<storm::jani::Composition> composition = parseComposition(parsedStructure.at(
"system"));
233 STORM_LOG_THROW(parsedStructure.count(
"properties") <= 1, storm::exceptions::InvalidJaniException,
"At most one list of properties can be given.");
234 std::vector<storm::jani::Property> properties;
235 if (parseProperties && parsedStructure.count(
"properties") == 1) {
236 STORM_LOG_THROW(parsedStructure.at(
"properties").is_array(), storm::exceptions::InvalidJaniException,
"Properties should be an array.");
237 for (
auto const& propertyEntry : parsedStructure.at(
"properties")) {
239 auto prop = this->
parseProperty(model, propertyEntry, scope.
refine(
"property[" + std::to_string(properties.size()) +
"]"));
242 properties.push_back(prop);
243 }
catch (storm::exceptions::NotSupportedException
const& ex) {
245 }
catch (storm::exceptions::NotImplementedException
const& ex) {
250 return {model, properties};
253template<
typename ValueType>
254std::vector<std::shared_ptr<storm::logic::Formula const>> JaniParser<ValueType>::parseUnaryFormulaArgument(
storm::jani::Model& model,
255 Json
const& propertyStructure,
257 std::string
const& opstring, Scope
const& scope) {
258 STORM_LOG_THROW(propertyStructure.count(
"exp") == 1, storm::exceptions::InvalidJaniException,
259 "Expecting operand for operator " << opstring <<
" in " << scope.description <<
".");
260 return {parseFormula(model, propertyStructure.at(
"exp"), formulaContext, scope.refine(
"Operand of operator " + opstring))};
263template<
typename ValueType>
264std::vector<std::shared_ptr<storm::logic::Formula const>> JaniParser<ValueType>::parseBinaryFormulaArguments(
storm::jani::Model& model,
265 Json
const& propertyStructure,
267 std::string
const& opstring, Scope
const& scope) {
268 STORM_LOG_THROW(propertyStructure.count(
"left") == 1, storm::exceptions::InvalidJaniException,
269 "Expecting left operand for operator " << opstring <<
" in " << scope.description <<
".");
270 STORM_LOG_THROW(propertyStructure.count(
"right") == 1, storm::exceptions::InvalidJaniException,
271 "Expecting right operand for operator " << opstring <<
" in " << scope.description <<
".");
272 return {parseFormula(model, propertyStructure.at(
"left"), formulaContext, scope.refine(
"Operand of operator " + opstring)),
273 parseFormula(model, propertyStructure.at(
"right"), formulaContext, scope.refine(
"Operand of operator " + opstring))};
276template<
typename ValueType>
277storm::jani::PropertyInterval JaniParser<ValueType>::parsePropertyInterval(Json
const& piStructure, Scope
const& scope) {
278 storm::jani::PropertyInterval pi;
279 if (piStructure.count(
"lower") > 0) {
280 pi.
lowerBound = parseExpression(piStructure.at(
"lower"), scope.refine(
"Lower bound for property interval"));
282 if (piStructure.count(
"lower-exclusive") > 0) {
286 if (piStructure.count(
"upper") > 0) {
287 pi.
upperBound = parseExpression(piStructure.at(
"upper"), scope.refine(
"Upper bound for property interval"));
289 if (piStructure.count(
"upper-exclusive") > 0) {
294 "Bounded operator must have a bounded interval, but no bounds found in '" << piStructure <<
"'.");
298template<
typename ValueType>
299storm::logic::RewardAccumulation JaniParser<ValueType>::parseRewardAccumulation(Json
const& accStructure, std::string
const& context) {
300 bool accTime =
false;
301 bool accSteps =
false;
302 bool accExit =
false;
303 STORM_LOG_THROW(accStructure.is_array(), storm::exceptions::InvalidJaniException,
"Accumulate should be an array.");
304 for (
auto const& accEntry : accStructure) {
305 if (accEntry ==
"steps") {
307 }
else if (accEntry ==
"time") {
309 }
else if (accEntry ==
"exit") {
313 "One may only accumulate either 'steps' or 'time' or 'exit', got " << accEntry.dump() <<
" in " << context <<
".");
316 return storm::logic::RewardAccumulation(accSteps, accTime, accExit);
324 lowerBounds.push_back(std::nullopt);
329 upperBounds.push_back(std::nullopt);
334 std::shared_ptr<storm::logic::Formula const>
const& left,
335 std::shared_ptr<storm::logic::Formula const>
const& right,
337 if (left->isPathFormula() || right->isPathFormula()) {
338 return std::make_shared<storm::logic::BinaryBooleanPathFormula const>(oper, left, right, context);
340 return std::make_shared<storm::logic::BinaryBooleanStateFormula const>(oper, left, right);
344 std::shared_ptr<storm::logic::Formula const>
const& sub,
346 if (sub->isPathFormula()) {
347 return std::make_shared<storm::logic::UnaryBooleanPathFormula const>(oper, sub, context);
349 return std::make_shared<storm::logic::UnaryBooleanStateFormula const>(oper, sub);
352template<
typename ValueType>
353std::shared_ptr<storm::logic::Formula const> JaniParser<ValueType>::parseFormula(
storm::jani::Model& model, Json
const& propertyStructure,
355 boost::optional<storm::logic::Bound> bound) {
356 if (propertyStructure.is_boolean()) {
357 return std::make_shared<storm::logic::BooleanLiteralFormula>(propertyStructure.template get<bool>());
359 if (propertyStructure.is_string()) {
360 if (labels.count(propertyStructure.template get<std::string>()) > 0) {
361 return std::make_shared<storm::logic::AtomicLabelFormula>(propertyStructure.template get<std::string>());
364 storm::expressions::Expression expr = parseExpression(propertyStructure, scope.refine(
"expression in property"),
true);
366 bool exprContainsLabel =
false;
368 for (
auto const& varInExpr : varsInExpr) {
369 if (labels.count(varInExpr.getName()) > 0) {
370 exprContainsLabel =
true;
374 if (!exprContainsLabel) {
375 STORM_LOG_ASSERT(bound == boost::none,
"Unexpected bound for atomic expression formula.");
376 return std::make_shared<storm::logic::AtomicExpressionFormula>(expr);
379 if (propertyStructure.count(
"op") == 1) {
380 std::string opString =
getString<ValueType>(propertyStructure.at(
"op"),
"Operation description");
382 if (opString ==
"Pmin" || opString ==
"Pmax") {
383 std::vector<std::shared_ptr<storm::logic::Formula const>> args =
385 STORM_LOG_ASSERT(args.size() == 1,
"Expected one argument for probability operator.");
386 storm::logic::OperatorInformation opInfo;
387 opInfo.
optimalityType = opString ==
"Pmin" ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize;
388 opInfo.
bound = bound;
389 return std::make_shared<storm::logic::ProbabilityOperatorFormula>(args[0], opInfo);
391 }
else if (opString ==
"∀" || opString ==
"∃") {
392 STORM_LOG_ASSERT(bound == boost::none,
"Unexpected bound for forall/exists.");
394 "Forall and Exists are currently not supported in " << scope.description <<
".");
395 }
else if (opString ==
"Emin" || opString ==
"Emax") {
397 STORM_LOG_THROW(propertyStructure.count(
"exp") == 1, storm::exceptions::InvalidJaniException,
398 "Expecting reward-expression for operator " << opString <<
" in " << scope.description <<
".");
399 storm::expressions::Expression rewExpr = parseExpression(propertyStructure.at(
"exp"), scope.refine(
"Reward expression"));
401 "Reward expression '" << rewExpr <<
"' does not have numerical type in " << scope.description <<
".");
402 std::string rewardName = rewExpr.
toString();
404 storm::logic::OperatorInformation opInfo;
405 opInfo.
optimalityType = opString ==
"Emin" ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize;
406 opInfo.
bound = bound;
408 storm::logic::RewardAccumulation rewardAccumulation(
false,
false,
false);
409 if (propertyStructure.count(
"accumulate") > 0) {
410 rewardAccumulation = parseRewardAccumulation(propertyStructure.at(
"accumulate"), scope.description);
414 if (propertyStructure.count(
"step-instant") > 0) {
415 STORM_LOG_THROW(propertyStructure.count(
"time-instant") == 0, storm::exceptions::NotSupportedException,
416 "Storm does not support to have a step-instant and a time-instant in " + scope.description +
".");
417 STORM_LOG_THROW(propertyStructure.count(
"reward-instants") == 0, storm::exceptions::NotSupportedException,
418 "Storm does not support to have a step-instant and a reward-instant in " + scope.description +
".");
420 storm::expressions::Expression stepInstantExpr = parseExpression(propertyStructure.at(
"step-instant"), scope.refine(
"Step instant"));
424 if (rewardAccumulation.isEmpty()) {
425 return std::make_shared<storm::logic::RewardOperatorFormula>(
428 return std::make_shared<storm::logic::RewardOperatorFormula>(
429 std::make_shared<storm::logic::CumulativeRewardFormula>(storm::logic::TimeBound(
false, stepInstantExpr),
434 }
else if (propertyStructure.count(
"time-instant") > 0) {
435 STORM_LOG_THROW(propertyStructure.count(
"reward-instants") == 0, storm::exceptions::NotSupportedException,
436 "Storm does not support to have a time-instant and a reward-instant in " + scope.description +
".");
437 storm::expressions::Expression timeInstantExpr = parseExpression(propertyStructure.at(
"time-instant"), scope.refine(
"time instant"));
441 if (rewardAccumulation.isEmpty()) {
442 return std::make_shared<storm::logic::RewardOperatorFormula>(
445 return std::make_shared<storm::logic::RewardOperatorFormula>(
446 std::make_shared<storm::logic::CumulativeRewardFormula>(storm::logic::TimeBound(
false, timeInstantExpr),
451 }
else if (propertyStructure.count(
"reward-instants") > 0) {
452 std::vector<storm::logic::TimeBound> bounds;
453 std::vector<storm::logic::TimeBoundReference> boundReferences;
454 for (
auto const& rewInst : propertyStructure.at(
"reward-instants")) {
455 storm::expressions::Expression rewInstRewardModelExpression =
456 parseExpression(rewInst.at(
"exp"), scope.refine(
"Reward expression at reward instant"));
458 "Reward expression '" << rewInstRewardModelExpression <<
"' does not have numerical type in " << scope.description <<
".");
459 storm::logic::RewardAccumulation boundRewardAccumulation = parseRewardAccumulation(rewInst.at(
"accumulate"), scope.description);
460 bool steps = (boundRewardAccumulation.
isStepsSet() || boundRewardAccumulation.
isExitSet()) && boundRewardAccumulation.
size() == 1;
466 std::string rewInstRewardModelName = rewInstRewardModelExpression.
toString();
467 if (!rewInstRewardModelExpression.
isVariable()) {
470 boundReferences.emplace_back(rewInstRewardModelName, boundRewardAccumulation);
472 storm::expressions::Expression rewInstantExpr = parseExpression(rewInst.at(
"instant"), scope.refine(
"reward instant"));
473 bounds.emplace_back(
false, rewInstantExpr);
478 return std::make_shared<storm::logic::RewardOperatorFormula>(
479 std::make_shared<storm::logic::CumulativeRewardFormula>(bounds, boundReferences, rewardAccumulation), rewardName, opInfo);
482 std::shared_ptr<storm::logic::Formula const> subformula;
483 if (propertyStructure.count(
"reach") > 0) {
485 subformula = std::make_shared<storm::logic::EventuallyFormula>(
486 parseFormula(model, propertyStructure.at(
"reach"), formulaContext, scope.refine(
"Reach-expression of operator " + opString)),
487 formulaContext, rewardAccumulation);
489 subformula = std::make_shared<storm::logic::TotalRewardFormula>(rewardAccumulation);
492 STORM_LOG_ASSERT(subformula->isTotalRewardFormula() || subformula->isTimePathFormula(),
"Expected total reward or time path formula.");
493 return std::make_shared<storm::logic::TimeOperatorFormula>(subformula, opInfo);
498 return std::make_shared<storm::logic::RewardOperatorFormula>(subformula, rewardName, opInfo);
501 }
else if (opString ==
"Smin" || opString ==
"Smax") {
502 storm::logic::OperatorInformation opInfo;
503 opInfo.
optimalityType = opString ==
"Smin" ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize;
504 opInfo.
bound = bound;
506 boost::optional<storm::logic::RewardAccumulation> rewardAccumulation;
507 if (propertyStructure.count(
"accumulate") > 0) {
509 rewardAccumulation = parseRewardAccumulation(propertyStructure.at(
"accumulate"), scope.description);
511 STORM_LOG_THROW(propertyStructure.count(
"exp") > 0, storm::exceptions::InvalidJaniException,
512 "Expected an expression at steady state property at " << scope.description <<
".");
513 auto exp = parseExpression(propertyStructure[
"exp"], scope.refine(
"steady-state operator"),
true);
514 if (!exp.isInitialized() || exp.hasBooleanType()) {
515 STORM_LOG_THROW(!rewardAccumulation.is_initialized(), storm::exceptions::InvalidJaniException,
516 "Long-run average probabilities are not allowed to have a reward accumulation at" << scope.description <<
".");
517 std::shared_ptr<storm::logic::Formula const> subformula =
518 parseUnaryFormulaArgument(model, propertyStructure, formulaContext, opString, scope.refine(
"Steady-state operator"))[0];
519 return std::make_shared<storm::logic::LongRunAverageOperatorFormula>(subformula, opInfo);
521 STORM_LOG_THROW(exp.hasNumericalType(), storm::exceptions::InvalidJaniException,
522 "Reward expression '" << exp <<
"' does not have numerical type in " << scope.description <<
".");
523 std::string rewardName = exp.toString();
524 if (!exp.isVariable()) {
527 auto subformula = std::make_shared<storm::logic::LongRunAverageRewardFormula>(rewardAccumulation);
528 return std::make_shared<storm::logic::RewardOperatorFormula>(subformula, rewardName, opInfo);
530 }
else if (opString ==
"U" || opString ==
"F") {
531 STORM_LOG_ASSERT(bound == boost::none,
"Unexpected bound for until/eventually.");
532 std::vector<std::shared_ptr<storm::logic::Formula const>> args;
533 if (opString ==
"U") {
534 args = parseBinaryFormulaArguments(model, propertyStructure, formulaContext, opString, scope);
537 args = parseUnaryFormulaArgument(model, propertyStructure, formulaContext, opString, scope);
538 args.push_back(args[0]);
542 std::vector<std::optional<storm::logic::TimeBound>> lowerBounds, upperBounds;
543 std::vector<storm::logic::TimeBoundReference> tbReferences;
544 if (propertyStructure.count(
"step-bounds") > 0) {
546 storm::jani::PropertyInterval pi =
547 parsePropertyInterval(propertyStructure.at(
"step-bounds"), scope.refine(
"step-bounded until").clearVariables());
551 if (propertyStructure.count(
"time-bounds") > 0) {
553 storm::jani::PropertyInterval pi =
554 parsePropertyInterval(propertyStructure.at(
"time-bounds"), scope.refine(
"time-bounded until").clearVariables());
558 if (propertyStructure.count(
"reward-bounds") > 0) {
559 for (
auto const& rbStructure : propertyStructure.at(
"reward-bounds")) {
560 storm::jani::PropertyInterval pi = parsePropertyInterval(rbStructure.at(
"bounds"), scope.refine(
"reward-bounded until").clearVariables());
562 STORM_LOG_THROW(rbStructure.count(
"exp") == 1, storm::exceptions::InvalidJaniException,
563 "Expecting reward-expression for operator " << opString <<
" in " << scope.description <<
".");
564 storm::expressions::Expression rewInstRewardModelExpression =
565 parseExpression(rbStructure.at(
"exp"), scope.refine(
"Reward expression at reward-bounds"));
567 "Reward expression '" << rewInstRewardModelExpression <<
"' does not have numerical type in " << scope.description <<
".");
568 storm::logic::RewardAccumulation boundRewardAccumulation = parseRewardAccumulation(rbStructure.at(
"accumulate"), scope.description);
569 bool steps = (boundRewardAccumulation.
isStepsSet() || boundRewardAccumulation.
isExitSet()) && boundRewardAccumulation.
size() == 1;
575 std::string rewInstRewardModelName = rewInstRewardModelExpression.
toString();
576 if (!rewInstRewardModelExpression.
isVariable()) {
579 tbReferences.emplace_back(rewInstRewardModelName, boundRewardAccumulation);
583 if (!tbReferences.empty()) {
584 return std::make_shared<storm::logic::BoundedUntilFormula const>(args[0], args[1], lowerBounds, upperBounds, tbReferences);
585 }
else if (args[0]->isTrueFormula()) {
586 return std::make_shared<storm::logic::EventuallyFormula const>(args[1], formulaContext);
588 return std::make_shared<storm::logic::UntilFormula const>(args[0], args[1]);
590 }
else if (opString ==
"G") {
592 std::vector<std::shared_ptr<storm::logic::Formula const>> args =
593 parseUnaryFormulaArgument(model, propertyStructure, formulaContext, opString, scope.refine(
"Subformula of globally operator "));
594 if (propertyStructure.count(
"step-bounds") > 0) {
595 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"Globally and step-bounds are not supported.");
596 }
else if (propertyStructure.count(
"time-bounds") > 0) {
597 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"Globally and time bounds are not supported.");
598 }
else if (propertyStructure.count(
"reward-bounds") > 0) {
599 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"Globally and reward bounded properties are not supported.");
601 return std::make_shared<storm::logic::GloballyFormula const>(args[0]);
603 }
else if (opString ==
"W") {
605 std::vector<std::shared_ptr<storm::logic::Formula const>> args =
606 parseBinaryFormulaArguments(model, propertyStructure, formulaContext, opString, scope);
607 STORM_LOG_ASSERT(args.size() == 2,
"Expected two arguments for weak until.");
608 if (propertyStructure.count(
"step-bounds") > 0) {
609 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"Weak until and step-bounds are not supported.");
610 }
else if (propertyStructure.count(
"time-bounds") > 0) {
611 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"Weak until and time bounds are not supported.");
612 }
else if (propertyStructure.count(
"reward-bounds") > 0) {
613 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"Weak until and reward bounded properties are not supported.");
615 return std::make_shared<storm::logic::WeakUntilFormula const>(args[0], args[1]);
616 }
else if (opString ==
"R") {
618 std::vector<std::shared_ptr<storm::logic::Formula const>> args =
619 parseBinaryFormulaArguments(model, propertyStructure, formulaContext, opString, scope);
621 if (propertyStructure.count(
"step-bounds") > 0) {
622 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"Release and step-bounds are not supported.");
623 }
else if (propertyStructure.count(
"time-bounds") > 0) {
624 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"Release and time bounds are not supported.");
625 }
else if (propertyStructure.count(
"reward-bounds") > 0) {
626 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"Release and reward bounded properties are not supported.");
628 return std::make_shared<storm::logic::ReleaseFormula const>(args[0], args[1]);
629 }
else if (opString ==
"∧" || opString ==
"∨") {
630 STORM_LOG_ASSERT(bound == boost::none,
"Unexpected bound for conjunction/disjunction.");
631 std::vector<std::shared_ptr<storm::logic::Formula const>> args =
632 parseBinaryFormulaArguments(model, propertyStructure, formulaContext, opString, scope);
633 STORM_LOG_ASSERT(args.size() == 2,
"Expected two arguments for conjunction/disjunction.");
637 }
else if (opString ==
"⇒") {
638 STORM_LOG_ASSERT(bound == boost::none,
"Unexpected bound for implication.");
639 std::vector<std::shared_ptr<storm::logic::Formula const>> args =
640 parseBinaryFormulaArguments(model, propertyStructure, formulaContext, opString, scope);
641 STORM_LOG_ASSERT(args.size() == 2,
"Expected two arguments for implication.");
644 }
else if (opString ==
"¬") {
646 std::vector<std::shared_ptr<storm::logic::Formula const>> args =
647 parseUnaryFormulaArgument(model, propertyStructure, formulaContext, opString, scope);
650 }
else if (!expr.
isInitialized() && (opString ==
"≥" || opString ==
"≤" || opString ==
"<" || opString ==
">" || opString ==
"=" || opString ==
"≠")) {
653 if (opString ==
"≥") {
655 }
else if (opString ==
"≤") {
657 }
else if (opString ==
"<") {
659 }
else if (opString ==
">") {
663 std::vector<std::string>
const leftRight = {
"left",
"right"};
664 for (uint64_t i = 0;
i < 2; ++
i) {
665 if (propertyStructure.at(leftRight[i]).count(
"op") > 0) {
666 std::string propertyOperatorString =
getString<ValueType>(propertyStructure.at(leftRight[i]).at(
"op"),
"property-operator");
667 std::set<std::string>
const propertyOperatorStrings = {
"Pmin",
"Pmax",
"Emin",
"Emax",
"Smin",
"Smax"};
668 if (propertyOperatorStrings.count(propertyOperatorString) > 0) {
670 parseExpression(propertyStructure.at(leftRight[1 - i]),
671 scope.refine(
"Threshold for operator " + propertyStructure.at(leftRight[i]).at(
"op").template get<std::string>()));
672 if ((opString ==
"=" || opString ==
"≠")) {
673 STORM_LOG_THROW(!boundExpr.containsVariables(), storm::exceptions::NotSupportedException,
674 "Comparison operators '=' or '≠' in property specifications are currently not supported.");
675 auto boundValue = boundExpr.evaluateAsRational();
677 if (opString ==
"=") {
682 }
else if (
storm::utility::isOne(boundValue) && (propertyOperatorString ==
"Pmin" || propertyOperatorString ==
"Pmax")) {
683 if (opString ==
"=") {
690 storm::exceptions::NotSupportedException,
691 "Comparison operators '=' or '≠' in property specifications are currently not supported in " << scope.description <<
".");
694 return parseFormula(model, propertyStructure.at(leftRight[i]), formulaContext, scope, storm::logic::Bound(ct, boundExpr));
699 }
else if (opString ==
"Multi") {
702 <<
" not enabled but model contains multi-objective property in " << scope.description
703 <<
". Continuing with that property anyways");
704 STORM_LOG_ASSERT(bound == boost::none,
"Unexpected bound for multi-objective.");
705 STORM_LOG_THROW(propertyStructure.count(
"properties") == 1, storm::exceptions::InvalidJaniException,
706 "Expecting properties for multi-objective operator in " << scope.description <<
".");
707 std::vector<std::shared_ptr<storm::logic::Formula const>> subformulas;
709 for (
auto const& subPropStructure : propertyStructure.at(
"properties")) {
710 STORM_LOG_THROW(subPropStructure.count(
"exp") == 1, storm::exceptions::InvalidJaniException,
711 "Expecting property expression in subproperty #" << i <<
" in " << scope.description <<
".");
712 subformulas.push_back(parseFormula(model, subPropStructure[
"exp"], formulaContext,
713 scope.refine(
"Subproperty #" + std::to_string(i) +
" of multi-objective operator")));
714 if (subPropStructure.count(
"opt") == 1) {
715 STORM_LOG_THROW(subformulas.back()->hasQuantitativeResult(), storm::exceptions::InvalidJaniException,
716 "Subformula #" << i <<
" has an optimization direction but is not numeric in " << scope.description <<
".");
717 STORM_LOG_THROW(subformulas.back()->isOperatorFormula(), storm::exceptions::NotSupportedException,
718 "Subformula #" << i <<
" is not an operator formula in " << scope.description <<
".");
719 std::string
const optString =
720 getString<ValueType>(subPropStructure.at(
"opt"),
"optimization direction for subproperty #" + std::to_string(i));
721 STORM_LOG_THROW(optString ==
"min" || optString ==
"max", storm::exceptions::InvalidJaniException,
722 "Unknown optimization direction " << optString <<
" for subproperty #" << i <<
" in " << scope.description <<
".");
723 auto const opt = optString ==
"min" ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize;
724 auto newFormula = subformulas.back()->clone();
725 newFormula->asOperatorFormula().setOptimalityType(opt);
726 subformulas.back() = newFormula;
728 STORM_LOG_THROW(subformulas.back()->hasQualitativeResult(), storm::exceptions::InvalidJaniException,
729 "Subformula #" << i <<
" has non-Boolean result but no optimization direction in " << scope.description <<
".");
733 STORM_LOG_THROW(propertyStructure.count(
"type") == 1, storm::exceptions::InvalidJaniException,
734 "Expecting type for multi-objective operator in " << scope.description <<
".");
735 std::string
const typeString =
getString<ValueType>(propertyStructure.at(
"type"),
"type of multi-objective operator");
736 STORM_LOG_THROW(typeString ==
"tradeoff" || typeString ==
"lexicographic", storm::exceptions::InvalidJaniException,
737 "Unknown type " << typeString <<
" for multi-objective operator in " << scope.description <<
".");
740 return std::make_shared<storm::logic::MultiObjectiveFormula const>(subformulas, type);
743 "Non-trivial Expression '" << expr
744 <<
"' contains a boolean transient variable. Can not translate to PRCTL-like formula at "
745 << scope.description <<
".");
751 "Looking for operator for formula " << propertyStructure.dump() <<
", but did not find one.");
755template<
typename ValueType>
757 STORM_LOG_THROW(propertyStructure.count(
"name") == 1, storm::exceptions::InvalidJaniException,
"Property must have a name.");
761 std::string comment =
"";
762 if (propertyStructure.count(
"comment") > 0) {
763 comment =
getString<ValueType>(propertyStructure.at(
"comment"),
"comment for property named '" + name +
"'.");
765 STORM_LOG_THROW(propertyStructure.count(
"expression") == 1, storm::exceptions::InvalidJaniException,
"Property must have an expression.");
767 Json const& expressionStructure = propertyStructure.at(
"expression");
769 STORM_LOG_THROW(expressionStructure.count(
"op") == 1, storm::exceptions::InvalidJaniException,
770 "Expression in property must have an operation description.");
771 STORM_LOG_THROW(expressionStructure.at(
"op") ==
"filter", storm::exceptions::InvalidJaniException,
"Top level operation of a property must be a filter.");
772 STORM_LOG_THROW(expressionStructure.count(
"fun") == 1, storm::exceptions::InvalidJaniException,
"Filter must have a function descritpion.");
773 std::string funDescr =
getString<ValueType>(expressionStructure.at(
"fun"),
"Filter function in property named " + name);
775 if (funDescr ==
"min") {
777 }
else if (funDescr ==
"max") {
779 }
else if (funDescr ==
"sum") {
781 }
else if (funDescr ==
"avg") {
783 }
else if (funDescr ==
"count") {
785 }
else if (funDescr ==
"∀") {
787 }
else if (funDescr ==
"∃") {
789 }
else if (funDescr ==
"argmin") {
791 }
else if (funDescr ==
"argmax") {
793 }
else if (funDescr ==
"values") {
796 STORM_LOG_THROW(
false, storm::exceptions::InvalidJaniException,
"Unknown filter description " << funDescr <<
" in property named " << name <<
".");
799 STORM_LOG_THROW(expressionStructure.count(
"states") == 1, storm::exceptions::InvalidJaniException,
"Filter must have a states description.");
800 std::shared_ptr<storm::logic::Formula const> statesFormula;
801 if (expressionStructure.at(
"states").count(
"op") > 0) {
802 std::string statesDescr =
getString<ValueType>(expressionStructure.at(
"states").at(
"op"),
"Filtered states in property named " + name);
803 if (statesDescr ==
"initial") {
804 statesFormula = std::make_shared<storm::logic::AtomicLabelFormula>(
"init");
807 if (!statesFormula) {
812 }
catch (storm::exceptions::NotSupportedException
const& ex) {
814 }
catch (storm::exceptions::NotImplementedException
const& ex) {
818 STORM_LOG_THROW(statesFormula, storm::exceptions::NotImplementedException,
"Could not derive states formula.");
819 STORM_LOG_THROW(expressionStructure.count(
"values") == 1, storm::exceptions::InvalidJaniException,
"Values as input for a filter must be given.");
824template<
typename ValueType>
825std::shared_ptr<storm::jani::Constant> JaniParser<ValueType>::parseConstant(Json
const& constantStructure, Scope
const& scope) {
826 STORM_LOG_THROW(constantStructure.count(
"name") == 1, storm::exceptions::InvalidJaniException,
827 "Variable (scope: " + scope.description +
") must have a name.");
828 std::string name =
getString<ValueType>(constantStructure.at(
"name"),
"variable-name in " + scope.description +
"-scope");
831 std::string exprManagerName = name;
833 STORM_LOG_THROW(constantStructure.count(
"type") == 1, storm::exceptions::InvalidJaniException,
834 "Constant '" + name +
"' (scope: " + scope.description +
") must have a (single) type-declaration.");
835 auto type = parseType(constantStructure.at(
"type"), name, scope);
836 STORM_LOG_THROW((type.first->isBasicType() || type.first->isBoundedType()), storm::exceptions::InvalidJaniException,
837 "Constant '" + name +
"' (scope: " + scope.description +
") has unexpected type.");
839 uint_fast64_t valueCount = constantStructure.count(
"value");
841 STORM_LOG_THROW(valueCount < 2, storm::exceptions::InvalidJaniException,
842 "Value for constant '" + name +
"' (scope: " + scope.description +
") must be given at most once.");
843 if (valueCount == 1) {
846 definingExpression = parseExpression(constantStructure.at(
"value"), scope.refine(
"Value of constant " + name));
850 storm::exceptions::InvalidJaniException,
851 "Type of value for constant '" + name +
"' (scope: " + scope.description +
") does not match the given type '" +
852 type.first->getStringRepresentation() +
".");
858 if (type.first->isBoundedType()) {
859 auto const& bndType = type.first->asBoundedType();
860 if (bndType.hasLowerBound()) {
861 constraintExpression = var.
getExpression() >= bndType.getLowerBound();
862 if (bndType.hasUpperBound()) {
863 constraintExpression = constraintExpression && var.
getExpression() <= bndType.getUpperBound();
865 }
else if (bndType.hasUpperBound()) {
866 constraintExpression = var.
getExpression() <= bndType.getUpperBound();
869 return std::make_shared<storm::jani::Constant>(name, std::move(var), definingExpression, constraintExpression);
872template<
typename ValueType>
874 std::string variableName,
Scope const& scope) {
876 if (typeStructure.is_string()) {
877 if (typeStructure ==
"real") {
879 result.second = expressionManager->getRationalType();
880 }
else if (typeStructure ==
"bool") {
882 result.second = expressionManager->getBooleanType();
883 }
else if (typeStructure ==
"int") {
885 result.second = expressionManager->getIntegerType();
886 }
else if (typeStructure ==
"clock") {
887 result.first = std::make_unique<storm::jani::ClockType>();
888 result.second = expressionManager->getRationalType();
889 }
else if (typeStructure ==
"continuous") {
890 result.first = std::make_unique<storm::jani::ContinuousType>();
891 result.second = expressionManager->getRationalType();
894 "Unsupported type " << typeStructure.dump() <<
" for variable '" << variableName <<
"' (scope: " << scope.
description <<
").");
896 }
else if (typeStructure.is_object()) {
897 STORM_LOG_THROW(typeStructure.count(
"kind") == 1, storm::exceptions::InvalidJaniException,
898 "For complex type as in variable " << variableName <<
"(scope: " << scope.
description <<
") kind must be given.");
901 if (kind ==
"bounded") {
903 typeStructure.count(
"lower-bound") + typeStructure.count(
"upper-bound") > 0, storm::exceptions::InvalidJaniException,
904 "For bounded type as in variable " << variableName <<
"(scope: " << scope.
description <<
") lower-bound or upper-bound must be given.");
906 if (typeStructure.count(
"lower-bound") > 0) {
907 lowerboundExpr =
parseExpression(typeStructure.at(
"lower-bound"), scope.
refine(
"Lower bound for variable " + variableName));
910 if (typeStructure.count(
"upper-bound") > 0) {
911 upperboundExpr =
parseExpression(typeStructure.at(
"upper-bound"), scope.
refine(
"Upper bound for variable " + variableName));
913 STORM_LOG_THROW(typeStructure.count(
"base") == 1, storm::exceptions::InvalidJaniException,
914 "For bounded type as in variable " << variableName <<
"(scope: " << scope.
description <<
") base must be given.");
915 std::string basictype =
917 if (basictype ==
"int") {
919 "Lower bound for bounded integer variable " << variableName <<
"(scope: " << scope.
description <<
") must be integer-typed.");
921 "Upper bound for bounded integer variable " << variableName <<
"(scope: " << scope.
description <<
") must be integer-typed.");
925 "Lower bound must not be larger than upper bound for bounded integer variable " << variableName
929 result.second = expressionManager->getIntegerType();
930 }
else if (basictype ==
"real") {
932 "Lower bound for bounded real variable " << variableName <<
"(scope: " << scope.
description <<
") must be numeric.");
934 "Upper bound for bounded real variable " << variableName <<
"(scope: " << scope.
description <<
") must be numeric.");
937 using SubMap = std::map<storm::expressions::Variable, storm::expressions::Expression>;
941 STORM_LOG_THROW(lowerboundValue <= upperboundValue, storm::exceptions::InvalidJaniException,
942 "Lower bound must not be larger than upper bound for bounded real variable " << variableName
946 result.second = expressionManager->getRationalType();
949 "Unsupported base " << basictype <<
" for bounded variable " << variableName <<
"(scope: " << scope.
description <<
").");
951 }
else if (kind ==
"array") {
952 STORM_LOG_THROW(typeStructure.count(
"base") == 1, storm::exceptions::InvalidJaniException,
953 "For array type as in variable " << variableName <<
"(scope: " << scope.
description <<
") base must be given.");
954 auto base =
parseType(typeStructure.at(
"base"), variableName, scope);
955 result.first = std::make_unique<storm::jani::ArrayType>(std::move(base.first));
956 result.second = expressionManager->getArrayType(base.second);
959 "Unsupported kind " << kind <<
" for complex type of variable " << variableName <<
"(scope: " << scope.
description <<
").");
965template<
typename ValueType>
966storm::jani::FunctionDefinition JaniParser<ValueType>::parseFunctionDefinition(Json
const& functionDefinitionStructure, Scope
const& scope,
bool firstPass,
967 std::string
const& parameterNamePrefix) {
968 STORM_LOG_THROW(functionDefinitionStructure.count(
"name") == 1, storm::exceptions::InvalidJaniException,
969 "Function definition (scope: " + scope.description +
") must have a name.");
970 std::string functionName =
getString<ValueType>(functionDefinitionStructure.at(
"name"),
"function-name in " + scope.description);
971 STORM_LOG_THROW(functionDefinitionStructure.count(
"type") == 1, storm::exceptions::InvalidJaniException,
972 "Function definition '" + functionName +
"' (scope: " + scope.description +
") must have a (single) type-declaration.");
973 auto type = parseType(functionDefinitionStructure.at(
"type"), functionName, scope);
975 !type.first->isClockType() && !type.first->isContinuousType(), storm::exceptions::InvalidJaniException,
976 "Function definition '" + functionName +
"' (scope: " + scope.description +
") uses illegal type '" + type.first->getStringRepresentation() +
"'.");
978 std::unordered_map<std::string, storm::expressions::Variable> parameterNameToVariableMap;
979 std::vector<storm::expressions::Variable> parameters;
980 if (!firstPass && functionDefinitionStructure.count(
"parameters") > 0) {
981 STORM_LOG_THROW(functionDefinitionStructure.count(
"parameters") == 1, storm::exceptions::InvalidJaniException,
982 "Function definition '" + functionName +
"' (scope: " + scope.description +
") must have exactly one list of parameters.");
983 for (
auto const& parameterStructure : functionDefinitionStructure.at(
"parameters")) {
984 STORM_LOG_THROW(parameterStructure.count(
"name") == 1, storm::exceptions::InvalidJaniException,
985 "Parameter declaration of parameter " + std::to_string(parameters.size()) +
" of Function definition '" + functionName +
986 "' (scope: " + scope.description +
") must have a name.");
987 std::string parameterName =
988 getString<ValueType>(parameterStructure.at(
"name"),
"parameter-name of parameter " + std::to_string(parameters.size()) +
989 " of Function definition '" + functionName +
"' (scope: " + scope.description +
").");
990 STORM_LOG_THROW(parameterStructure.count(
"type") == 1, storm::exceptions::InvalidJaniException,
991 "Parameter declaration of parameter " + std::to_string(parameters.size()) +
" of Function definition '" + functionName +
992 "' (scope: " + scope.description +
") must have exactly one type.");
994 parseType(parameterStructure.at(
"type"), parameterName,
995 scope.refine(
"parameter declaration of parameter " + std::to_string(parameters.size()) +
" of Function definition " + functionName));
996 STORM_LOG_THROW(!parameterType.first->isClockType() && !parameterType.first->isContinuousType(), storm::exceptions::InvalidJaniException,
997 "Type of parameter " + std::to_string(parameters.size()) +
" of function definition '" + functionName +
998 "' (scope: " + scope.description +
") uses illegal type '" + parameterType.first->getStringRepresentation() +
"'.");
1000 "Bounds on parameter" + parameterName +
" of function definition " + functionName +
" will be ignored.");
1003 parameters.push_back(expressionManager->declareVariable(exprParameterName, parameterType.second));
1004 parameterNameToVariableMap.emplace(parameterName, parameters.back());
1008 STORM_LOG_THROW(functionDefinitionStructure.count(
"body") == 1, storm::exceptions::InvalidJaniException,
1009 "Function definition '" + functionName +
"' (scope: " + scope.description +
") must have a (single) body.");
1012 functionBody = parseExpression(functionDefinitionStructure.at(
"body"), scope.refine(
"body of function definition " + functionName),
false,
1013 parameterNameToVariableMap);
1015 "Type of body of function " + functionName +
"' (scope: " + scope.description +
") has type "
1016 << functionBody.
getType() <<
" although the function type is given as " << type.second);
1018 return storm::jani::FunctionDefinition(functionName, type.second, parameters, functionBody);
1021template<
typename ValueType>
1023 STORM_LOG_THROW(variableStructure.count(
"name") == 1, storm::exceptions::InvalidJaniException,
1024 "Variable (scope: " + scope.
description +
") must have a name.");
1028 std::string exprManagerName = namePrefix + name;
1029 bool transientVar = defaultVariableTransient;
1030 uint_fast64_t tvarcount = variableStructure.count(
"transient");
1031 STORM_LOG_THROW(tvarcount <= 1, storm::exceptions::InvalidJaniException,
1032 "Multiple definitions of transient not allowed in variable '" + name +
"' (scope: " + scope.
description +
").");
1033 if (tvarcount == 1) {
1037 STORM_LOG_THROW(variableStructure.count(
"type") == 1, storm::exceptions::InvalidJaniException,
1038 "Variable '" + name +
"' (scope: " + scope.
description +
") must have a (single) type-declaration.");
1039 auto type =
parseType(variableStructure.at(
"type"), name, scope);
1041 uint_fast64_t initvalcount = variableStructure.count(
"initial-value");
1043 STORM_LOG_THROW(initvalcount == 1, storm::exceptions::InvalidJaniException,
1044 "Initial value must be given once for transient variable '" + name +
"' (scope: " + scope.
description +
") " + name +
1047 STORM_LOG_THROW(initvalcount <= 1, storm::exceptions::InvalidJaniException,
1048 "Initial value can be given at most one for variable " + name +
"' (scope: " + scope.
description +
").");
1050 boost::optional<storm::expressions::Expression> initVal;
1051 if (initvalcount == 1 && !variableStructure.at(
"initial-value").is_null()) {
1052 initVal =
parseExpression(variableStructure.at(
"initial-value"), scope.
refine(
"Initial value for variable " + name));
1060 if (transientVar && type.first->isBasicType() && type.first->asBasicType().isBooleanType()) {
1061 labels.insert(name);
1064 auto expressionVariable = expressionManager->declareVariable(exprManagerName, type.second);
1072 STORM_LOG_THROW(expected == actual, storm::exceptions::InvalidJaniException,
1073 "Operator " << opstring <<
" expects " << expected <<
" arguments, but got " << actual <<
" in " << errorInfo <<
".");
1076template<
typename ValueType>
1077std::vector<storm::expressions::Expression> JaniParser<ValueType>::parseUnaryExpressionArguments(
1078 Json
const& expressionDecl, std::string
const& opstring, Scope
const& scope,
bool returnNoneInitializedOnUnknownOperator,
1079 std::unordered_map<std::string, storm::expressions::Variable>
const& auxiliaryVariables) {
1081 parseExpression(expressionDecl.at(
"exp"), scope.refine(
"Argument of operator " + opstring), returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1085template<
typename ValueType>
1086std::vector<storm::expressions::Expression> JaniParser<ValueType>::parseBinaryExpressionArguments(
1087 Json
const& expressionDecl, std::string
const& opstring, Scope
const& scope,
bool returnNoneInitializedOnUnknownOperator,
1088 std::unordered_map<std::string, storm::expressions::Variable>
const& auxiliaryVariables) {
1089 storm::expressions::Expression left = parseExpression(expressionDecl.at(
"left"), scope.refine(
"Left argument of operator " + opstring),
1090 returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1091 storm::expressions::Expression right = parseExpression(expressionDecl.at(
"right"), scope.refine(
"Right argument of operator " + opstring),
1092 returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1093 return {left, right};
1100 "Operator " << opstring <<
" expects argument[" << argNr <<
"]: '" << expr <<
"' to be Boolean in " << errorInfo <<
".");
1108 "Operator " << opstring <<
" expects argument " + std::to_string(argNr) +
" to be numerical in " << errorInfo <<
".");
1116 "Operator " << opstring <<
" expects argument " + std::to_string(argNr) +
" to be numerical in " << errorInfo <<
".");
1124 "Operator " << opstring <<
" expects argument " + std::to_string(argNr) +
" to be of type 'array' in " << errorInfo <<
".");
1127template<
typename ValueType>
1129 if (lValueStructure.is_string()) {
1133 auto localVar = scope.
localVars->find(ident);
1134 if (localVar != scope.
localVars->end()) {
1135 var = localVar->second;
1138 if (var ==
nullptr) {
1140 "Unknown identifier '" << ident <<
"' occurs in " << scope.
description <<
".");
1141 auto globalVar = scope.
globalVars->find(ident);
1143 "Unknown identifier '" << ident <<
"' occurs in " << scope.
description <<
".");
1144 var = globalVar->second;
1148 }
else if (lValueStructure.count(
"op") == 1) {
1153 STORM_LOG_THROW(opstring ==
"aa", storm::exceptions::InvalidJaniException,
1154 "Unknown operation '" << opstring <<
"' occurs in " << scope.
description <<
".");
1155 STORM_LOG_THROW(lValueStructure.count(
"exp") == 1, storm::exceptions::InvalidJaniException,
1156 "Missing 'exp' in array access at " << scope.
description <<
".");
1157 auto expLValue =
parseLValue(lValueStructure.at(
"exp"), scope.
refine(
"Expression of array access"));
1158 STORM_LOG_THROW(expLValue.isArray(), storm::exceptions::InvalidJaniException,
1159 "Array access considers non-array expression at " << scope.
description <<
".");
1160 STORM_LOG_THROW(lValueStructure.count(
"index"), storm::exceptions::InvalidJaniException,
1161 "Missing 'index' in array access at " << scope.
description <<
".");
1162 auto indexExpression =
parseExpression(lValueStructure.at(
"index"), scope.
refine(
"Index of array access"));
1163 expLValue.addArrayAccessIndex(indexExpression);
1167 "Unknown LValue '" << lValueStructure.dump() <<
"' occurs in " << scope.
description <<
".");
1173template<
typename ValueType>
1175 std::string
const& ident, Scope
const& scope, std::unordered_map<std::string, storm::expressions::Variable>
const& auxiliaryVariables) {
1177 auto it = auxiliaryVariables.find(ident);
1178 if (it != auxiliaryVariables.end()) {
1182 if (scope.localVars !=
nullptr) {
1183 auto it = scope.localVars->find(ident);
1184 if (it != scope.localVars->end()) {
1185 return it->second->getExpressionVariable();
1188 if (scope.globalVars !=
nullptr) {
1189 auto it = scope.globalVars->find(ident);
1190 if (it != scope.globalVars->end()) {
1191 return it->second->getExpressionVariable();
1194 if (scope.constants !=
nullptr) {
1195 auto it = scope.constants->find(ident);
1196 if (it != scope.constants->end()) {
1197 return it->second->getExpressionVariable();
1200 STORM_LOG_THROW(
false, storm::exceptions::InvalidJaniException,
"Unknown identifier '" << ident <<
"' occurs in " << scope.description <<
".");
1202 return storm::expressions::Variable();
1205template<
typename ValueType>
1207 bool returnNoneInitializedOnUnknownOperator,
1208 std::unordered_map<std::string, storm::expressions::Variable>
const& auxiliaryVariables) {
1209 if (expressionStructure.is_boolean()) {
1210 if (expressionStructure.template get<bool>()) {
1211 return expressionManager->boolean(
true);
1213 return expressionManager->boolean(
false);
1215 }
else if (expressionStructure.is_number_integer()) {
1216 return expressionManager->integer(expressionStructure.template get<int64_t>());
1217 }
else if (expressionStructure.is_number_float()) {
1219 }
else if (expressionStructure.is_string()) {
1220 std::string ident = expressionStructure.template get<std::string>();
1222 }
else if (expressionStructure.is_object()) {
1223 if (expressionStructure.count(
"distribution") == 1) {
1225 false, storm::exceptions::InvalidJaniException,
1226 "Distributions are not supported by storm expressions, cannot import " << expressionStructure.dump() <<
" in " << scope.
description <<
".");
1228 if (expressionStructure.count(
"op") == 1) {
1230 std::vector<storm::expressions::Expression> arguments = {};
1231 if (opstring ==
"ite") {
1232 STORM_LOG_THROW(expressionStructure.count(
"if") == 1, storm::exceptions::InvalidJaniException,
"If operator required.");
1233 STORM_LOG_THROW(expressionStructure.count(
"else") == 1, storm::exceptions::InvalidJaniException,
"Else operator required.");
1234 STORM_LOG_THROW(expressionStructure.count(
"then") == 1, storm::exceptions::InvalidJaniException,
"Then operator required.");
1235 arguments.push_back(
1236 parseExpression(expressionStructure.at(
"if"), scope.
refine(
"if-formula"), returnNoneInitializedOnUnknownOperator, auxiliaryVariables));
1237 arguments.push_back(
1238 parseExpression(expressionStructure.at(
"then"), scope.
refine(
"then-formula"), returnNoneInitializedOnUnknownOperator, auxiliaryVariables));
1239 arguments.push_back(
1240 parseExpression(expressionStructure.at(
"else"), scope.
refine(
"else-formula"), returnNoneInitializedOnUnknownOperator, auxiliaryVariables));
1245 }
else if (opstring ==
"∨") {
1246 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1248 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1253 return arguments[0] || arguments[1];
1254 }
else if (opstring ==
"∧") {
1255 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1257 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1262 return arguments[0] && arguments[1];
1263 }
else if (opstring ==
"⇒") {
1264 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1266 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1271 return (!arguments[0]) || arguments[1];
1272 }
else if (opstring ==
"¬") {
1273 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1275 if (!arguments[0].isInitialized()) {
1279 return !arguments[0];
1280 }
else if (opstring ==
"=") {
1281 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1283 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1286 if (arguments[0].hasBooleanType()) {
1291 return arguments[0] == arguments[1];
1293 }
else if (opstring ==
"≠") {
1294 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1296 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1299 if (arguments[0].hasBooleanType()) {
1304 return arguments[0] != arguments[1];
1306 }
else if (opstring ==
"<") {
1307 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1309 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1314 return arguments[0] < arguments[1];
1315 }
else if (opstring ==
"≤") {
1316 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1318 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1323 return arguments[0] <= arguments[1];
1324 }
else if (opstring ==
">") {
1325 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1327 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1332 return arguments[0] > arguments[1];
1333 }
else if (opstring ==
"≥") {
1334 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1336 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1341 return arguments[0] >= arguments[1];
1342 }
else if (opstring ==
"+") {
1343 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1347 return arguments[0] + arguments[1];
1348 }
else if (opstring ==
"-" && expressionStructure.count(
"left") > 0) {
1349 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1353 return arguments[0] - arguments[1];
1354 }
else if (opstring ==
"-") {
1355 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1358 return -arguments[0];
1359 }
else if (opstring ==
"*") {
1360 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1364 return arguments[0] * arguments[1];
1365 }
else if (opstring ==
"/") {
1366 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1370 return arguments[0] / arguments[1];
1371 }
else if (opstring ==
"%") {
1372 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1376 return arguments[0] % arguments[1];
1377 }
else if (opstring ==
"max") {
1378 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1383 }
else if (opstring ==
"min") {
1384 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1389 }
else if (opstring ==
"floor") {
1390 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1394 }
else if (opstring ==
"ceil") {
1395 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1399 }
else if (opstring ==
"abs") {
1400 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1404 }
else if (opstring ==
"sgn") {
1405 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1409 }
else if (opstring ==
"trc") {
1410 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1414 }
else if (opstring ==
"pow") {
1415 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1420 }
else if (opstring ==
"exp") {
1421 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1426 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"Exp operation is not yet implemented.");
1427 }
else if (opstring ==
"log") {
1428 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1433 }
else if (opstring ==
"cos") {
1434 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1438 }
else if (opstring ==
"sin") {
1439 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1443 }
else if (opstring ==
"aa") {
1444 STORM_LOG_THROW(expressionStructure.count(
"exp") == 1, storm::exceptions::InvalidJaniException,
1445 "Array access operator requires exactly one exp (at " + scope.
description +
").");
1447 returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1448 STORM_LOG_THROW(expressionStructure.count(
"index") == 1, storm::exceptions::InvalidJaniException,
1449 "Array access operator requires exactly one index (at " + scope.
description +
").");
1451 returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1457 }
else if (opstring ==
"av") {
1458 STORM_LOG_THROW(expressionStructure.count(
"elements") == 1, storm::exceptions::InvalidJaniException,
1459 "Array value operator requires exactly one 'elements' (at " + scope.
description +
").");
1460 std::vector<std::shared_ptr<storm::expressions::BaseExpression const>> elements;
1463 for (
auto const& element : expressionStructure.at(
"elements")) {
1464 elements.push_back(
parseExpression(element, scope.
refine(
"element " + std::to_string(elements.size()) +
" of array value expression"),
1465 returnNoneInitializedOnUnknownOperator, auxiliaryVariables)
1466 .getBaseExpressionPointer());
1468 commonType = elements.back()->getType();
1470 }
else if (commonType != elements.back()->getType()) {
1471 if (commonType.
isIntegerType() && elements.back()->getType().isRationalType()) {
1472 commonType = elements.back()->getType();
1475 "Incompatible element types " << commonType <<
" and " << elements.back()->getType()
1476 <<
" of array value expression at " << scope.
description <<
".");
1480 return std::make_shared<storm::expressions::ValueArrayExpression>(*expressionManager, expressionManager->getArrayType(commonType), elements)
1482 }
else if (opstring ==
"ac") {
1483 STORM_LOG_THROW(expressionStructure.count(
"length") == 1, storm::exceptions::InvalidJaniException,
1484 "Array access operator requires exactly one length (at " + scope.
description +
").");
1486 returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1488 STORM_LOG_THROW(expressionStructure.count(
"var") == 1, storm::exceptions::InvalidJaniException,
1489 "Array access operator requires exactly one var (at " + scope.
description +
").");
1490 std::string indexVarName =
1492 STORM_LOG_THROW(auxiliaryVariables.find(indexVarName) == auxiliaryVariables.end(), storm::exceptions::InvalidJaniException,
1493 "Index variable " << indexVarName <<
" is already defined as an auxiliary variable (at " + scope.
description +
").");
1494 auto newAuxVars = auxiliaryVariables;
1496 newAuxVars.emplace(indexVarName, indexVar);
1497 STORM_LOG_THROW(expressionStructure.count(
"exp") == 1, storm::exceptions::InvalidJaniException,
1498 "Array constructor operator requires exactly one exp (at " + scope.
description +
").");
1500 returnNoneInitializedOnUnknownOperator, newAuxVars);
1501 return std::make_shared<storm::expressions::ConstructorArrayExpression>(*expressionManager, expressionManager->getArrayType(exp.
getType()),
1505 }
else if (opstring ==
"call") {
1506 STORM_LOG_THROW(expressionStructure.count(
"function") == 1, storm::exceptions::InvalidJaniException,
1507 "Function call operator requires exactly one function (at " + scope.
description +
").");
1508 std::string functionName =
1517 "Function call operator calls unknown function '" + functionName +
"' (at " + scope.
description +
").");
1519 STORM_LOG_THROW(expressionStructure.count(
"args") == 1, storm::exceptions::InvalidJaniException,
1520 "Function call operator requires exactly one args (at " + scope.
description +
").");
1521 std::vector<std::shared_ptr<storm::expressions::BaseExpression const>> args;
1522 if (expressionStructure.count(
"args") > 0) {
1523 STORM_LOG_THROW(expressionStructure.count(
"args") == 1, storm::exceptions::InvalidJaniException,
1524 "Function call operator requires exactly one args (at " + scope.
description +
").");
1525 for (
auto const& arg : expressionStructure.at(
"args")) {
1526 args.push_back(
parseExpression(arg, scope.
refine(
"argument " + std::to_string(args.size()) +
" of function call expression"),
1527 returnNoneInitializedOnUnknownOperator, auxiliaryVariables)
1528 .getBaseExpressionPointer());
1531 return std::make_shared<storm::expressions::FunctionCallExpression>(*expressionManager, functionDefinition->
getType(), functionName, args)
1533 }
else if (unsupportedOpstrings.count(opstring) > 0) {
1534 STORM_LOG_THROW(
false, storm::exceptions::InvalidJaniException,
"Opstring " + opstring +
" is not supported by storm.");
1536 if (returnNoneInitializedOnUnknownOperator) {
1539 STORM_LOG_THROW(
false, storm::exceptions::InvalidJaniException,
"Unknown operator " << opstring <<
" in " << scope.
description <<
".");
1542 if (expressionStructure.count(
"constant") == 1) {
1545 if (constantStr ==
"Ï€") {
1546 return std::make_shared<storm::expressions::TranscendentalNumberLiteralExpression>(
1550 if (constantStr ==
"e") {
1551 return std::make_shared<storm::expressions::TranscendentalNumberLiteralExpression>(
1557 false, storm::exceptions::InvalidJaniException,
1558 "No supported operator declaration found for complex expressions as " << expressionStructure.dump() <<
" in " << scope.
description <<
".");
1561 "No supported expression found at " << expressionStructure.dump() <<
" in " << scope.
description <<
".");
1566template<
typename ValueType>
1567void JaniParser<ValueType>::parseActions(Json
const& actionStructure,
storm::jani::Model& parentModel) {
1568 std::set<std::string> actionNames;
1569 for (
auto const& actionEntry : actionStructure) {
1570 STORM_LOG_THROW(actionEntry.count(
"name") == 1, storm::exceptions::InvalidJaniException,
"Actions must have exactly one name.");
1572 STORM_LOG_THROW(actionNames.count(actionName) == 0, storm::exceptions::InvalidJaniException,
"Action with name " + actionName +
" already exists.");
1574 actionNames.emplace(actionName);
1578template<
typename ValueType>
1580 STORM_LOG_THROW(automatonStructure.count(
"name") == 1, storm::exceptions::InvalidJaniException,
"Each automaton must have a name.");
1581 std::string name =
getString<ValueType>(automatonStructure.at(
"name"),
" the name field for automaton");
1585 uint64_t varDeclCount = automatonStructure.count(
"variables");
1586 STORM_LOG_THROW(varDeclCount < 2, storm::exceptions::InvalidJaniException,
"Automaton '" << name <<
"' has more than one list of variables.");
1589 if (varDeclCount > 0) {
1590 for (
auto const& varStructure : automatonStructure.at(
"variables")) {
1593 STORM_LOG_ASSERT(localVars.count(var->getName()) == 0,
"Local variable already exists.");
1594 localVars.emplace(var->getName(), &automaton.
addVariable(*var));
1598 uint64_t funDeclCount = automatonStructure.count(
"functions");
1599 STORM_LOG_THROW(funDeclCount < 2, storm::exceptions::InvalidJaniException,
"Automaton '" << name <<
"' has more than one list of functions.");
1602 if (funDeclCount > 0) {
1604 std::vector<storm::jani::FunctionDefinition> dummyFunctionDefinitions;
1605 for (
auto const& funStructure : automatonStructure.at(
"functions")) {
1607 dummyFunctionDefinitions.push_back(
1608 parseFunctionDefinition(funStructure, scope.
refine(
"functions[" + std::to_string(localFuns.size()) +
"] of automaton " + name),
true));
1612 for (
auto const& funDef : dummyFunctionDefinitions) {
1613 bool unused = localFuns.emplace(funDef.getName(), &funDef).second;
1615 "Multiple definitions of functions with the name " << funDef.getName() <<
" in " << scope.
description <<
".");
1617 for (
auto const& funStructure : automatonStructure.at(
"functions")) {
1620 parseFunctionDefinition(funStructure, scope.
refine(
"functions[" + std::to_string(localFuns.size()) +
"] of automaton " + name),
false,
1627 STORM_LOG_THROW(automatonStructure.count(
"locations") > 0, storm::exceptions::InvalidJaniException,
"Automaton '" << name <<
"' does not have locations.");
1628 std::unordered_map<std::string, uint64_t> locIds;
1629 for (
auto const& locEntry : automatonStructure.at(
"locations")) {
1630 STORM_LOG_THROW(locEntry.count(
"name") == 1, storm::exceptions::InvalidJaniException,
1631 "Locations for automaton '" << name <<
"' must have exactly one name.");
1632 std::string locName =
getString<ValueType>(locEntry.at(
"name"),
"location of automaton " + name);
1633 STORM_LOG_THROW(locIds.count(locName) == 0, storm::exceptions::InvalidJaniException,
1634 "Location with name '" + locName +
"' already exists in automaton '" + name +
"'.");
1635 STORM_LOG_THROW(locEntry.count(
"invariant") == 0, storm::exceptions::InvalidJaniException,
1636 "Invariants in locations as in '" + locName +
"' in automaton '" + name +
"' are not supported.");
1639 std::vector<storm::jani::Assignment> transientAssignments;
1640 if (locEntry.count(
"transient-values") > 0) {
1641 for (
auto const& transientValueEntry : locEntry.at(
"transient-values")) {
1642 STORM_LOG_THROW(transientValueEntry.count(
"ref") == 1, storm::exceptions::InvalidJaniException,
1643 "Transient values in location " << locName <<
" need exactly one ref that is assigned to.");
1644 STORM_LOG_THROW(transientValueEntry.count(
"value") == 1, storm::exceptions::InvalidJaniException,
1645 "Transient values in location " << locName <<
" need exactly one assigned value.");
1648 "Assigned non-transient variable " << lValue <<
" in location " + locName +
" (automaton: '" + name +
"').");
1650 parseExpression(transientValueEntry.at(
"value"), scope.
refine(
"Assignment of lValue in location " + locName));
1651 transientAssignments.emplace_back(lValue, rhs);
1655 locIds.emplace(locName,
id);
1657 STORM_LOG_THROW(automatonStructure.count(
"initial-locations") == 1, storm::exceptions::InvalidJaniException,
1658 "Automaton '" << name <<
"' does not have initial locations.");
1659 for (
Json const& initLocStruct : automatonStructure.at(
"initial-locations")) {
1662 STORM_LOG_THROW(automatonStructure.count(
"restrict-initial") < 2, storm::exceptions::InvalidJaniException,
1663 "Automaton '" << name <<
"' has multiple initial value restrictions.");
1665 if (automatonStructure.count(
"restrict-initial") > 0) {
1666 STORM_LOG_THROW(automatonStructure.at(
"restrict-initial").count(
"exp") == 1, storm::exceptions::InvalidJaniException,
1667 "Automaton '" << name <<
"' needs an expression inside the initial restricion.");
1668 initialValueRestriction =
parseExpression(automatonStructure.at(
"restrict-initial").at(
"exp"), scope.
refine(
"Initial value restriction"));
1672 STORM_LOG_THROW(automatonStructure.count(
"edges") > 0, storm::exceptions::InvalidJaniException,
"Automaton '" << name <<
"' must have a list of edges.");
1673 for (
auto const& edgeEntry : automatonStructure.at(
"edges")) {
1675 STORM_LOG_THROW(edgeEntry.count(
"location") == 1, storm::exceptions::InvalidJaniException,
1676 "Each edge in automaton '" << name <<
"' must have a source.");
1677 std::string sourceLoc =
getString<ValueType>(edgeEntry.at(
"location"),
"source location for edge in automaton '" + name +
"'");
1678 STORM_LOG_THROW(locIds.count(sourceLoc) == 1, storm::exceptions::InvalidJaniException,
1679 "Source of edge has unknown location '" << sourceLoc <<
"' in automaton '" << name <<
"'.");
1681 STORM_LOG_THROW(edgeEntry.count(
"action") < 2, storm::exceptions::InvalidJaniException,
1682 "Edge from " << sourceLoc <<
" in automaton " << name <<
" has multiple actions.");
1684 if (edgeEntry.count(
"action") > 0) {
1685 action =
getString<ValueType>(edgeEntry.at(
"action"),
"action name in edge from '" + sourceLoc +
"' in automaton '" + name +
"'");
1690 STORM_LOG_THROW(edgeEntry.count(
"rate") < 2, storm::exceptions::InvalidJaniException,
1691 "Edge from '" << sourceLoc <<
"' in automaton '" << name <<
"' has multiple rates.");
1693 if (edgeEntry.count(
"rate") > 0) {
1694 STORM_LOG_THROW(edgeEntry.at(
"rate").count(
"exp") == 1, storm::exceptions::InvalidJaniException,
1695 "Rate in edge from '" << sourceLoc <<
"' in automaton '" << name <<
"' must have a defing expression.");
1696 rateExpr =
parseExpression(edgeEntry.at(
"rate").at(
"exp"), scope.
refine(
"rate expression in edge from '" + sourceLoc));
1699 storm::exceptions::InvalidJaniException,
"Only positive rates are allowed but rate '" << rateExpr <<
" was found.");
1702 STORM_LOG_THROW(edgeEntry.count(
"guard") <= 1, storm::exceptions::InvalidJaniException,
1703 "Guard can be given at most once in edge from '" << sourceLoc <<
"' in automaton '" << name <<
"'.");
1705 if (edgeEntry.count(
"guard") == 1) {
1706 STORM_LOG_THROW(edgeEntry.at(
"guard").count(
"exp") == 1, storm::exceptions::InvalidJaniException,
1707 "Guard in edge from '" + sourceLoc +
"' in automaton '" + name +
"' must have one expression.");
1708 guardExpr =
parseExpression(edgeEntry.at(
"guard").at(
"exp"), scope.
refine(
"guard expression in edge from '" + sourceLoc));
1709 STORM_LOG_THROW(guardExpr.
hasBooleanType(), storm::exceptions::InvalidJaniException,
"Guard " << guardExpr <<
" does not have Boolean type.");
1712 std::shared_ptr<storm::jani::TemplateEdge> templateEdge = std::make_shared<storm::jani::TemplateEdge>(guardExpr);
1715 if (edgeEntry.count(
"assignments") > 0) {
1716 STORM_LOG_THROW(edgeEntry.count(
"assignments") == 1, storm::exceptions::InvalidJaniException,
1717 "Multiple edge assignments in edge from '" + sourceLoc +
"' in automaton '" + name +
"'.");
1718 for (
auto const& assignmentEntry : edgeEntry.at(
"assignments")) {
1720 STORM_LOG_THROW(assignmentEntry.count(
"ref") == 1, storm::exceptions::InvalidJaniException,
1721 "Assignment in edge from '" << sourceLoc <<
"' in automaton '" << name <<
"'must have one ref field.");
1723 parseLValue(assignmentEntry.at(
"ref"), scope.
refine(
"Assignment variable in edge from '" + sourceLoc +
"' in automaton '" + name +
"'"));
1725 STORM_LOG_THROW(assignmentEntry.count(
"value") == 1, storm::exceptions::InvalidJaniException,
1726 "Assignment in edge from '" << sourceLoc <<
"' in automaton '" << name <<
"' must have one value field.");
1728 parseExpression(assignmentEntry.at(
"value"), scope.
refine(
"assignment in edge from '" + sourceLoc +
"' in automaton '" + name +
"'"));
1731 int64_t assignmentIndex = 0;
1732 if (assignmentEntry.count(
"index") > 0) {
1734 getSignedInt<ValueType>(assignmentEntry.at(
"index"),
"assignment index in edge from '" + sourceLoc +
"' in automaton '" + name +
"'");
1741 STORM_LOG_THROW(edgeEntry.count(
"destinations") == 1, storm::exceptions::InvalidJaniException,
1742 "A single list of destinations must be given in edge from '" << sourceLoc <<
"' in automaton '" << name <<
"'.");
1743 std::vector<std::pair<uint64_t, storm::expressions::Expression>> destinationLocationsAndProbabilities;
1744 for (
auto const& destEntry : edgeEntry.at(
"destinations")) {
1746 STORM_LOG_THROW(destEntry.count(
"location") == 1, storm::exceptions::InvalidJaniException,
1747 "Each destination in edge from '" << sourceLoc <<
"' in automaton '" << name <<
"' must have a target location.");
1748 std::string targetLoc =
1749 getString<ValueType>(destEntry.at(
"location"),
"target location for edge from '" + sourceLoc +
"' in automaton '" + name +
"'");
1750 STORM_LOG_THROW(locIds.count(targetLoc) == 1, storm::exceptions::InvalidJaniException,
1751 "Target of edge has unknown location '" << targetLoc <<
"' in automaton '" << name <<
"'.");
1754 unsigned probDeclCount = destEntry.count(
"probability");
1755 STORM_LOG_THROW(probDeclCount < 2, storm::exceptions::InvalidJaniException,
1756 "Destination in edge from '" << sourceLoc <<
"' to '" << targetLoc <<
"' in automaton '" << name <<
"' has multiple probabilites.");
1757 if (probDeclCount == 0) {
1758 probExpr = expressionManager->rational(1.0);
1760 STORM_LOG_THROW(destEntry.at(
"probability").count(
"exp") == 1, storm::exceptions::InvalidJaniException,
1761 "Destination in edge from '" << sourceLoc <<
"' to '" << targetLoc <<
"' in automaton '" << name
1762 <<
"' must have a probability expression.");
1763 probExpr =
parseExpression(destEntry.at(
"probability").at(
"exp"), scope.
refine(
"probability expression in edge from '" + sourceLoc +
"' to '" +
1764 targetLoc +
"' in automaton '" + name +
"'"));
1768 "Probability expression " << probExpr <<
" does not have a numerical type.");
1770 std::vector<storm::jani::Assignment> assignments;
1771 unsigned assignmentDeclCount = destEntry.count(
"assignments");
1773 assignmentDeclCount < 2, storm::exceptions::InvalidJaniException,
1774 "Destination in edge from '" << sourceLoc <<
"' to '" << targetLoc <<
"' in automaton '" << name <<
"' has multiple assignment lists.");
1775 if (assignmentDeclCount > 0) {
1776 for (
auto const& assignmentEntry : destEntry.at(
"assignments")) {
1779 assignmentEntry.count(
"ref") == 1, storm::exceptions::InvalidJaniException,
1780 "Assignment in edge from '" << sourceLoc <<
"' to '" << targetLoc <<
"' in automaton '" << name <<
"' must have one ref field.");
1782 "' to '" + targetLoc +
"' in automaton '" + name +
"'"));
1785 assignmentEntry.count(
"value") == 1, storm::exceptions::InvalidJaniException,
1786 "Assignment in edge from '" << sourceLoc <<
"' to '" << targetLoc <<
"' in automaton '" << name <<
"' must have one value field.");
1789 scope.
refine(
"assignment in edge from '" + sourceLoc +
"' to '" + targetLoc +
"' in automaton '" + name +
"'"));
1792 int64_t assignmentIndex = 0;
1793 if (assignmentEntry.count(
"index") > 0) {
1794 assignmentIndex =
getSignedInt<ValueType>(assignmentEntry.at(
"index"),
"assignment index in edge from '" + sourceLoc +
"' to '" +
1795 targetLoc +
"' in automaton '" + name +
"'");
1797 assignments.emplace_back(lValue, assignmentExpr, assignmentIndex);
1800 destinationLocationsAndProbabilities.emplace_back(locIds.at(targetLoc), probExpr);
1804 rateExpr.
isInitialized() ? boost::optional<storm::expressions::Expression>(rateExpr) : boost::none, templateEdge,
1805 destinationLocationsAndProbabilities));
1811template<
typename ValueType>
1813 std::vector<storm::jani::SynchronizationVector> syncVectors;
1815 for (
auto const& syncEntry : syncVectorStructure) {
1816 std::vector<std::string> inputs;
1817 for (
auto const& syncInput : syncEntry.at(
"synchronise")) {
1818 if (syncInput.is_null()) {
1821 inputs.push_back(syncInput);
1824 std::string syncResult;
1825 if (syncEntry.count(
"result")) {
1826 syncResult = syncEntry.at(
"result");
1830 syncVectors.emplace_back(inputs, syncResult);
1835template<
typename ValueType>
1836std::shared_ptr<storm::jani::Composition> JaniParser<ValueType>::parseComposition(Json
const& compositionStructure) {
1837 if (compositionStructure.count(
"automaton")) {
1838 std::set<std::string> inputEnabledActions;
1839 if (compositionStructure.count(
"input-enable")) {
1840 for (
auto const& actionDecl : compositionStructure.at(
"input-enable")) {
1841 inputEnabledActions.insert(actionDecl.template get<std::string>());
1844 return std::shared_ptr<storm::jani::AutomatonComposition>(
1845 new storm::jani::AutomatonComposition(compositionStructure.at(
"automaton").template get<std::string>(), inputEnabledActions));
1848 STORM_LOG_THROW(compositionStructure.count(
"elements") == 1, storm::exceptions::InvalidJaniException,
1849 "Elements of a composition must be given, got " << compositionStructure.dump() <<
".");
1851 if (compositionStructure.at(
"elements").size() == 1 && compositionStructure.count(
"syncs") == 0) {
1853 STORM_LOG_THROW(compositionStructure.at(
"elements").back().count(
"automaton") == 1, storm::exceptions::InvalidJaniException,
1854 "Automaton must be given in composition.");
1855 if (compositionStructure.at(
"elements").back().at(
"automaton").is_string()) {
1856 std::string name = compositionStructure.at(
"elements").back().at(
"automaton");
1858 return std::shared_ptr<storm::jani::AutomatonComposition>(
new storm::jani::AutomatonComposition(name));
1860 STORM_LOG_THROW(
false, storm::exceptions::InvalidJaniException,
"Trivial nesting parallel composition is not yet supported.");
1863 std::vector<std::shared_ptr<storm::jani::Composition>> compositions;
1864 for (
auto const& elemDecl : compositionStructure.at(
"elements")) {
1865 if (!allowRecursion) {
1866 STORM_LOG_THROW(elemDecl.count(
"automaton") == 1, storm::exceptions::InvalidJaniException,
"Automaton must be given in the element.");
1868 compositions.push_back(parseComposition(elemDecl));
1871 STORM_LOG_THROW(compositionStructure.count(
"syncs") < 2, storm::exceptions::InvalidJaniException,
"Sync vectors can be given at most once.");
1872 std::vector<storm::jani::SynchronizationVector> syncVectors;
1873 if (compositionStructure.count(
"syncs") > 0) {
1877 return std::shared_ptr<storm::jani::Composition>(
new storm::jani::ParallelComposition(compositions, syncVectors));
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_THROW_UNCONDITIONALLY(exception, message)
#define STORM_LOG_ASSERT(cond, 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 pow(Expression const &base, Expression const &exponent, bool allowIntegerType)
The type of the resulting expression is.
Expression minimum(Expression const &first, Expression const &second)
Expression floor(Expression const &first)
Expression sin(Expression const &first)
Expression sign(Expression const &first)
Expression truncate(Expression const &first)
Expression xclusiveor(Expression const &first, Expression const &second)
Expression logarithm(Expression const &first, Expression const &second)
Expression cos(Expression const &first)
void closeFile(std::ofstream &stream)
Close the given file after writing.
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
Contains all file parsers and helper classes.
void insertLowerUpperTimeBounds(std::vector< std::optional< storm::logic::TimeBound > > &lowerBounds, std::vector< std::optional< storm::logic::TimeBound > > &upperBounds, storm::jani::PropertyInterval const &pi)
void ensureNumberOfArguments(uint64_t expected, uint64_t actual, std::string const &opstring, std::string const &errorInfo)
Helper for parse expression.
std::string getString(typename JaniParser< ValueType >::Json const &structure, std::string const &errorInfo)
std::vector< storm::jani::SynchronizationVector > parseSyncVectors(typename JaniParser< ValueType >::Json const &syncVectorStructure)
std::shared_ptr< storm::logic::Formula const > makeUnaryBooleanFormula(storm::logic::UnaryBooleanOperatorType oper, std::shared_ptr< storm::logic::Formula const > const &sub, storm::logic::FormulaContext context)
int64_t getSignedInt(typename JaniParser< ValueType >::Json const &structure, std::string const &errorInfo)
bool getBoolean(typename JaniParser< ValueType >::Json const &structure, std::string const &errorInfo)
void ensureArrayType(storm::expressions::Expression const &expr, std::string const &opstring, unsigned argNr, std::string const &errorInfo)
Helper for parse expression.
void ensureBooleanType(storm::expressions::Expression const &expr, std::string const &opstring, unsigned argNr, std::string const &errorInfo)
Helper for parse expression.
std::shared_ptr< storm::logic::Formula const > makeBinaryBooleanFormula(storm::logic::BinaryBooleanOperatorType oper, std::shared_ptr< storm::logic::Formula const > const &left, std::shared_ptr< storm::logic::Formula const > const &right, storm::logic::FormulaContext context)
void ensureIntegerType(storm::expressions::Expression const &expr, std::string const &opstring, unsigned argNr, std::string const &errorInfo)
Helper for parse expression.
const std::string VARIABLE_AUTOMATON_DELIMITER
uint64_t getUnsignedInt(typename JaniParser< ValueType >::Json const &structure, std::string const &errorInfo)
void ensureNumericalType(storm::expressions::Expression const &expr, std::string const &opstring, unsigned argNr, std::string const &errorInfo)
Helper for parse expression.
bool isOne(ValueType const &a)
bool isZero(ValueType const &a)
TargetType convertNumber(SourceType const &number)
#define STORM_PARSERS_API
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