33#include <boost/lexical_cast.hpp>
48template<
typename ValueType>
49const bool JaniParser<ValueType>::defaultVariableTransient =
false;
51template<
typename ValueType>
52const std::set<std::string> JaniParser<ValueType>::unsupportedOpstrings({
"tan",
"cot",
"sec",
"csc",
"asin",
"acos",
"atan",
53 "acot",
"asec",
"acsc",
"sinh",
"cosh",
"tanh",
"coth",
54 "sech",
"csch",
"asinh",
"acosh",
"atanh",
"asinh",
"acosh"});
56template<
typename ValueType>
58 STORM_LOG_THROW(structure.is_string(), storm::exceptions::InvalidJaniException,
59 "Expected a string in " << errorInfo <<
", got '" << structure.dump() <<
"'.");
60 return structure.front();
63template<
typename ValueType>
65 STORM_LOG_THROW(structure.is_boolean(), storm::exceptions::InvalidJaniException,
66 "Expected a Boolean in " << errorInfo <<
", got " << structure.dump() <<
"'.");
67 return structure.front();
70template<
typename ValueType>
72 STORM_LOG_THROW(structure.is_number(), storm::exceptions::InvalidJaniException,
73 "Expected a number in " << errorInfo <<
", got '" << structure.dump() <<
"'.");
74 int64_t num = structure.front();
75 STORM_LOG_THROW(num >= 0, storm::exceptions::InvalidJaniException,
"Expected a positive number in " << errorInfo <<
", got '" << num <<
"'.");
76 return static_cast<uint64_t
>(num);
79template<
typename ValueType>
81 STORM_LOG_THROW(structure.is_number(), storm::exceptions::InvalidJaniException,
82 "Expected a number in " << errorInfo <<
", got '" << structure.dump() <<
"'.");
83 return structure.front();
86template<
typename ValueType>
90 return parser.parseModel(parseProperties);
93template<
typename ValueType>
96 return parser.parseModel(parseProperties);
99template<
typename ValueType>
101 parsedStructure = Json::parse(jsonstring);
104template<
typename ValueType>
108 file >> parsedStructure;
112template<
typename ValueType>
115 STORM_LOG_THROW(parsedStructure.count(
"jani-version") == 1, storm::exceptions::InvalidJaniException,
"Jani-version must be given exactly once.");
117 STORM_LOG_WARN_COND(version >= 1 && version <= 1,
"JANI Version " << version <<
" is not supported. Results may be wrong.");
119 STORM_LOG_THROW(parsedStructure.count(
"name") == 1, storm::exceptions::InvalidJaniException,
"A model must have a (single) name.");
122 STORM_LOG_THROW(parsedStructure.count(
"type") == 1, storm::exceptions::InvalidJaniException,
"A type must be given exactly once.");
123 std::string modeltypestring =
getString<ValueType>(parsedStructure.at(
"type"),
"type of the model");
127 uint_fast64_t featuresCount = parsedStructure.count(
"features");
128 STORM_LOG_THROW(featuresCount < 2, storm::exceptions::InvalidJaniException,
"Features-declarations can be given at most once.");
129 if (featuresCount == 1) {
131 for (
auto const& feature : parsedStructure.at(
"features")) {
134 for (
auto const& knownFeature : allKnownModelFeatures.asSet()) {
141 STORM_LOG_THROW(found, storm::exceptions::NotSupportedException,
"Storm does not support the model feature " << featureStr <<
".");
144 uint_fast64_t actionCount = parsedStructure.count(
"actions");
145 STORM_LOG_THROW(actionCount < 2, storm::exceptions::InvalidJaniException,
"Action-declarations can be given at most once.");
146 if (actionCount > 0) {
147 parseActions(parsedStructure.at(
"actions"), model);
155 uint_fast64_t constantsCount = parsedStructure.count(
"constants");
156 STORM_LOG_THROW(constantsCount < 2, storm::exceptions::InvalidJaniException,
"Constant-declarations can be given at most once.");
157 if (constantsCount == 1) {
159 model.
getConstants().reserve(parsedStructure.at(
"constants").size());
160 for (
auto const& constStructure : parsedStructure.at(
"constants")) {
161 std::shared_ptr<storm::jani::Constant> constant =
162 parseConstant(constStructure, scope.
refine(
"constants[" + std::to_string(constants.size()) +
"]"));
165 constants.emplace(constant->getName(), &model.
getConstants().back());
170 uint_fast64_t variablesCount = parsedStructure.count(
"variables");
171 STORM_LOG_THROW(variablesCount < 2, storm::exceptions::InvalidJaniException,
"Variable-declarations can be given at most once for global variables.");
174 if (variablesCount == 1) {
175 for (
auto const& varStructure : parsedStructure.at(
"variables")) {
176 std::shared_ptr<storm::jani::Variable> variable =
parseVariable(varStructure, scope.
refine(
"variables[" + std::to_string(globalVars.size())));
177 globalVars.emplace(variable->getName(), &model.
addVariable(*variable));
181 uint64_t funDeclCount = parsedStructure.count(
"functions");
182 STORM_LOG_THROW(funDeclCount < 2, storm::exceptions::InvalidJaniException,
"Model '" << name <<
"' has more than one list of functions.");
185 if (funDeclCount > 0) {
187 std::vector<storm::jani::FunctionDefinition> dummyFunctionDefinitions;
188 for (
auto const& funStructure : parsedStructure.at(
"functions")) {
190 dummyFunctionDefinitions.push_back(
191 parseFunctionDefinition(funStructure, scope.
refine(
"functions[" + std::to_string(globalFuns.size()) +
"] of model " + name),
true));
195 for (
auto const& funDef : dummyFunctionDefinitions) {
196 bool unused = globalFuns.emplace(funDef.getName(), &funDef).second;
198 "Multiple definitions of functions with the name " << funDef.getName() <<
" in " << scope.
description <<
".");
200 for (
auto const& funStructure : parsedStructure.at(
"functions")) {
203 parseFunctionDefinition(funStructure, scope.
refine(
"functions[" + std::to_string(globalFuns.size()) +
"] of model " + name),
false);
210 STORM_LOG_THROW(parsedStructure.count(
"automata") == 1, storm::exceptions::InvalidJaniException,
"Exactly one list of automata must be given.");
211 STORM_LOG_THROW(parsedStructure.at(
"automata").is_array(), storm::exceptions::InvalidJaniException,
"Automata must be an array.");
213 for (
auto const& automataEntry : parsedStructure.at(
"automata")) {
216 STORM_LOG_THROW(parsedStructure.count(
"restrict-initial") < 2, storm::exceptions::InvalidJaniException,
"Model has multiple initial value restrictions.");
218 if (parsedStructure.count(
"restrict-initial") > 0) {
219 STORM_LOG_THROW(parsedStructure.at(
"restrict-initial").count(
"exp") == 1, storm::exceptions::InvalidJaniException,
220 "Model needs an expression inside the initial restricion.");
221 initialValueRestriction =
parseExpression(parsedStructure.at(
"restrict-initial").at(
"exp"), scope.
refine(
"Initial value restriction"));
224 STORM_LOG_THROW(parsedStructure.count(
"system") == 1, storm::exceptions::InvalidJaniException,
"Exactly one system description must be given.");
225 std::shared_ptr<storm::jani::Composition> composition = parseComposition(parsedStructure.at(
"system"));
231 STORM_LOG_THROW(parsedStructure.count(
"properties") <= 1, storm::exceptions::InvalidJaniException,
"At most one list of properties can be given.");
232 std::vector<storm::jani::Property> properties;
233 if (parseProperties && parsedStructure.count(
"properties") == 1) {
234 STORM_LOG_THROW(parsedStructure.at(
"properties").is_array(), storm::exceptions::InvalidJaniException,
"Properties should be an array.");
235 for (
auto const& propertyEntry : parsedStructure.at(
"properties")) {
237 auto prop = this->
parseProperty(model, propertyEntry, scope.
refine(
"property[" + std::to_string(properties.size()) +
"]"));
240 properties.push_back(prop);
241 }
catch (storm::exceptions::NotSupportedException
const& ex) {
243 }
catch (storm::exceptions::NotImplementedException
const& ex) {
248 return {model, properties};
251template<
typename ValueType>
252std::vector<std::shared_ptr<storm::logic::Formula const>> JaniParser<ValueType>::parseUnaryFormulaArgument(
storm::jani::Model& model,
253 Json
const& propertyStructure,
255 std::string
const& opstring, Scope
const& scope) {
256 STORM_LOG_THROW(propertyStructure.count(
"exp") == 1, storm::exceptions::InvalidJaniException,
257 "Expecting operand for operator " << opstring <<
" in " << scope.description <<
".");
258 return {parseFormula(model, propertyStructure.at(
"exp"), formulaContext, scope.refine(
"Operand of operator " + opstring))};
261template<
typename ValueType>
262std::vector<std::shared_ptr<storm::logic::Formula const>> JaniParser<ValueType>::parseBinaryFormulaArguments(
storm::jani::Model& model,
263 Json
const& propertyStructure,
265 std::string
const& opstring, Scope
const& scope) {
266 STORM_LOG_THROW(propertyStructure.count(
"left") == 1, storm::exceptions::InvalidJaniException,
267 "Expecting left operand for operator " << opstring <<
" in " << scope.description <<
".");
268 STORM_LOG_THROW(propertyStructure.count(
"right") == 1, storm::exceptions::InvalidJaniException,
269 "Expecting right operand for operator " << opstring <<
" in " << scope.description <<
".");
270 return {parseFormula(model, propertyStructure.at(
"left"), formulaContext, scope.refine(
"Operand of operator " + opstring)),
271 parseFormula(model, propertyStructure.at(
"right"), formulaContext, scope.refine(
"Operand of operator " + opstring))};
274template<
typename ValueType>
275storm::jani::PropertyInterval JaniParser<ValueType>::parsePropertyInterval(Json
const& piStructure, Scope
const& scope) {
276 storm::jani::PropertyInterval pi;
277 if (piStructure.count(
"lower") > 0) {
278 pi.
lowerBound = parseExpression(piStructure.at(
"lower"), scope.refine(
"Lower bound for property interval"));
280 if (piStructure.count(
"lower-exclusive") > 0) {
284 if (piStructure.count(
"upper") > 0) {
285 pi.
upperBound = parseExpression(piStructure.at(
"upper"), scope.refine(
"Upper bound for property interval"));
287 if (piStructure.count(
"upper-exclusive") > 0) {
292 "Bounded operator must have a bounded interval, but no bounds found in '" << piStructure <<
"'.");
296template<
typename ValueType>
297storm::logic::RewardAccumulation JaniParser<ValueType>::parseRewardAccumulation(Json
const& accStructure, std::string
const& context) {
298 bool accTime =
false;
299 bool accSteps =
false;
300 bool accExit =
false;
301 STORM_LOG_THROW(accStructure.is_array(), storm::exceptions::InvalidJaniException,
"Accumulate should be an array.");
302 for (
auto const& accEntry : accStructure) {
303 if (accEntry ==
"steps") {
305 }
else if (accEntry ==
"time") {
307 }
else if (accEntry ==
"exit") {
311 "One may only accumulate either 'steps' or 'time' or 'exit', got " << accEntry.dump() <<
" in " << context <<
".");
314 return storm::logic::RewardAccumulation(accSteps, accTime, accExit);
322 lowerBounds.push_back(std::nullopt);
327 upperBounds.push_back(std::nullopt);
331template<
typename ValueType>
332std::shared_ptr<storm::logic::Formula const> JaniParser<ValueType>::parseFormula(
storm::jani::Model& model, Json
const& propertyStructure,
334 boost::optional<storm::logic::Bound> bound) {
335 if (propertyStructure.is_boolean()) {
336 return std::make_shared<storm::logic::BooleanLiteralFormula>(propertyStructure.template get<bool>());
338 if (propertyStructure.is_string()) {
339 if (labels.count(propertyStructure.template get<std::string>()) > 0) {
340 return std::make_shared<storm::logic::AtomicLabelFormula>(propertyStructure.template get<std::string>());
343 storm::expressions::Expression expr = parseExpression(propertyStructure, scope.refine(
"expression in property"),
true);
345 bool exprContainsLabel =
false;
347 for (
auto const& varInExpr : varsInExpr) {
348 if (labels.count(varInExpr.getName()) > 0) {
349 exprContainsLabel =
true;
353 if (!exprContainsLabel) {
354 STORM_LOG_ASSERT(bound == boost::none,
"Unexpected bound for atomic expression formula.");
355 return std::make_shared<storm::logic::AtomicExpressionFormula>(expr);
358 if (propertyStructure.count(
"op") == 1) {
359 std::string opString =
getString<ValueType>(propertyStructure.at(
"op"),
"Operation description");
361 if (opString ==
"Pmin" || opString ==
"Pmax") {
362 std::vector<std::shared_ptr<storm::logic::Formula const>> args =
364 STORM_LOG_ASSERT(args.size() == 1,
"Expected one argument for probability operator.");
365 storm::logic::OperatorInformation opInfo;
366 opInfo.
optimalityType = opString ==
"Pmin" ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize;
367 opInfo.
bound = bound;
368 return std::make_shared<storm::logic::ProbabilityOperatorFormula>(args[0], opInfo);
370 }
else if (opString ==
"∀" || opString ==
"∃") {
371 STORM_LOG_ASSERT(bound == boost::none,
"Unexpected bound for forall/exists.");
372 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"Forall and Exists are currently not supported in " << scope.description <<
".");
373 }
else if (opString ==
"Emin" || opString ==
"Emax") {
375 STORM_LOG_THROW(propertyStructure.count(
"exp") == 1, storm::exceptions::InvalidJaniException,
376 "Expecting reward-expression for operator " << opString <<
" in " << scope.description <<
".");
377 storm::expressions::Expression rewExpr = parseExpression(propertyStructure.at(
"exp"), scope.refine(
"Reward expression"));
379 "Reward expression '" << rewExpr <<
"' does not have numerical type in " << scope.description <<
".");
380 std::string rewardName = rewExpr.
toString();
382 storm::logic::OperatorInformation opInfo;
383 opInfo.
optimalityType = opString ==
"Emin" ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize;
384 opInfo.
bound = bound;
386 storm::logic::RewardAccumulation rewardAccumulation(
false,
false,
false);
387 if (propertyStructure.count(
"accumulate") > 0) {
388 rewardAccumulation = parseRewardAccumulation(propertyStructure.at(
"accumulate"), scope.description);
392 if (propertyStructure.count(
"step-instant") > 0) {
393 STORM_LOG_THROW(propertyStructure.count(
"time-instant") == 0, storm::exceptions::NotSupportedException,
394 "Storm does not support to have a step-instant and a time-instant in " + scope.description +
".");
395 STORM_LOG_THROW(propertyStructure.count(
"reward-instants") == 0, storm::exceptions::NotSupportedException,
396 "Storm does not support to have a step-instant and a reward-instant in " + scope.description +
".");
398 storm::expressions::Expression stepInstantExpr = parseExpression(propertyStructure.at(
"step-instant"), scope.refine(
"Step instant"));
402 if (rewardAccumulation.isEmpty()) {
403 return std::make_shared<storm::logic::RewardOperatorFormula>(
406 return std::make_shared<storm::logic::RewardOperatorFormula>(
407 std::make_shared<storm::logic::CumulativeRewardFormula>(storm::logic::TimeBound(
false, stepInstantExpr),
412 }
else if (propertyStructure.count(
"time-instant") > 0) {
413 STORM_LOG_THROW(propertyStructure.count(
"reward-instants") == 0, storm::exceptions::NotSupportedException,
414 "Storm does not support to have a time-instant and a reward-instant in " + scope.description +
".");
415 storm::expressions::Expression timeInstantExpr = parseExpression(propertyStructure.at(
"time-instant"), scope.refine(
"time instant"));
419 if (rewardAccumulation.isEmpty()) {
420 return std::make_shared<storm::logic::RewardOperatorFormula>(
423 return std::make_shared<storm::logic::RewardOperatorFormula>(
424 std::make_shared<storm::logic::CumulativeRewardFormula>(storm::logic::TimeBound(
false, timeInstantExpr),
429 }
else if (propertyStructure.count(
"reward-instants") > 0) {
430 std::vector<storm::logic::TimeBound> bounds;
431 std::vector<storm::logic::TimeBoundReference> boundReferences;
432 for (
auto const& rewInst : propertyStructure.at(
"reward-instants")) {
433 storm::expressions::Expression rewInstRewardModelExpression =
434 parseExpression(rewInst.at(
"exp"), scope.refine(
"Reward expression at reward instant"));
436 "Reward expression '" << rewInstRewardModelExpression <<
"' does not have numerical type in " << scope.description <<
".");
437 storm::logic::RewardAccumulation boundRewardAccumulation = parseRewardAccumulation(rewInst.at(
"accumulate"), scope.description);
438 bool steps = (boundRewardAccumulation.
isStepsSet() || boundRewardAccumulation.
isExitSet()) && boundRewardAccumulation.
size() == 1;
444 std::string rewInstRewardModelName = rewInstRewardModelExpression.
toString();
445 if (!rewInstRewardModelExpression.
isVariable()) {
448 boundReferences.emplace_back(rewInstRewardModelName, boundRewardAccumulation);
450 storm::expressions::Expression rewInstantExpr = parseExpression(rewInst.at(
"instant"), scope.refine(
"reward instant"));
451 bounds.emplace_back(
false, rewInstantExpr);
456 return std::make_shared<storm::logic::RewardOperatorFormula>(
457 std::make_shared<storm::logic::CumulativeRewardFormula>(bounds, boundReferences, rewardAccumulation), rewardName, opInfo);
460 std::shared_ptr<storm::logic::Formula const> subformula;
461 if (propertyStructure.count(
"reach") > 0) {
463 subformula = std::make_shared<storm::logic::EventuallyFormula>(
464 parseFormula(model, propertyStructure.at(
"reach"), formulaContext, scope.refine(
"Reach-expression of operator " + opString)),
465 formulaContext, rewardAccumulation);
467 subformula = std::make_shared<storm::logic::TotalRewardFormula>(rewardAccumulation);
470 STORM_LOG_ASSERT(subformula->isTotalRewardFormula() || subformula->isTimePathFormula(),
"Expected total reward or time path formula.");
471 return std::make_shared<storm::logic::TimeOperatorFormula>(subformula, opInfo);
476 return std::make_shared<storm::logic::RewardOperatorFormula>(subformula, rewardName, opInfo);
479 }
else if (opString ==
"Smin" || opString ==
"Smax") {
480 storm::logic::OperatorInformation opInfo;
481 opInfo.
optimalityType = opString ==
"Smin" ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize;
482 opInfo.
bound = bound;
484 boost::optional<storm::logic::RewardAccumulation> rewardAccumulation;
485 if (propertyStructure.count(
"accumulate") > 0) {
487 rewardAccumulation = parseRewardAccumulation(propertyStructure.at(
"accumulate"), scope.description);
489 STORM_LOG_THROW(propertyStructure.count(
"exp") > 0, storm::exceptions::InvalidJaniException,
490 "Expected an expression at steady state property at " << scope.description <<
".");
491 auto exp = parseExpression(propertyStructure[
"exp"], scope.refine(
"steady-state operator"),
true);
492 if (!exp.isInitialized() || exp.hasBooleanType()) {
493 STORM_LOG_THROW(!rewardAccumulation.is_initialized(), storm::exceptions::InvalidJaniException,
494 "Long-run average probabilities are not allowed to have a reward accumulation at" << scope.description <<
".");
495 std::shared_ptr<storm::logic::Formula const> subformula =
496 parseUnaryFormulaArgument(model, propertyStructure, formulaContext, opString, scope.refine(
"Steady-state operator"))[0];
497 return std::make_shared<storm::logic::LongRunAverageOperatorFormula>(subformula, opInfo);
499 STORM_LOG_THROW(exp.hasNumericalType(), storm::exceptions::InvalidJaniException,
500 "Reward expression '" << exp <<
"' does not have numerical type in " << scope.description <<
".");
501 std::string rewardName = exp.toString();
502 if (!exp.isVariable()) {
505 auto subformula = std::make_shared<storm::logic::LongRunAverageRewardFormula>(rewardAccumulation);
506 return std::make_shared<storm::logic::RewardOperatorFormula>(subformula, rewardName, opInfo);
508 }
else if (opString ==
"U" || opString ==
"F") {
509 STORM_LOG_ASSERT(bound == boost::none,
"Unexpected bound for until/eventually.");
510 std::vector<std::shared_ptr<storm::logic::Formula const>> args;
511 if (opString ==
"U") {
512 args = parseBinaryFormulaArguments(model, propertyStructure, formulaContext, opString, scope);
515 args = parseUnaryFormulaArgument(model, propertyStructure, formulaContext, opString, scope);
516 args.push_back(args[0]);
520 std::vector<std::optional<storm::logic::TimeBound>> lowerBounds, upperBounds;
521 std::vector<storm::logic::TimeBoundReference> tbReferences;
522 if (propertyStructure.count(
"step-bounds") > 0) {
524 storm::jani::PropertyInterval pi =
525 parsePropertyInterval(propertyStructure.at(
"step-bounds"), scope.refine(
"step-bounded until").clearVariables());
529 if (propertyStructure.count(
"time-bounds") > 0) {
531 storm::jani::PropertyInterval pi =
532 parsePropertyInterval(propertyStructure.at(
"time-bounds"), scope.refine(
"time-bounded until").clearVariables());
536 if (propertyStructure.count(
"reward-bounds") > 0) {
537 for (
auto const& rbStructure : propertyStructure.at(
"reward-bounds")) {
538 storm::jani::PropertyInterval pi = parsePropertyInterval(rbStructure.at(
"bounds"), scope.refine(
"reward-bounded until").clearVariables());
540 STORM_LOG_THROW(rbStructure.count(
"exp") == 1, storm::exceptions::InvalidJaniException,
541 "Expecting reward-expression for operator " << opString <<
" in " << scope.description <<
".");
542 storm::expressions::Expression rewInstRewardModelExpression =
543 parseExpression(rbStructure.at(
"exp"), scope.refine(
"Reward expression at reward-bounds"));
545 "Reward expression '" << rewInstRewardModelExpression <<
"' does not have numerical type in " << scope.description <<
".");
546 storm::logic::RewardAccumulation boundRewardAccumulation = parseRewardAccumulation(rbStructure.at(
"accumulate"), scope.description);
547 bool steps = (boundRewardAccumulation.
isStepsSet() || boundRewardAccumulation.
isExitSet()) && boundRewardAccumulation.
size() == 1;
553 std::string rewInstRewardModelName = rewInstRewardModelExpression.
toString();
554 if (!rewInstRewardModelExpression.
isVariable()) {
557 tbReferences.emplace_back(rewInstRewardModelName, boundRewardAccumulation);
561 if (!tbReferences.empty()) {
562 return std::make_shared<storm::logic::BoundedUntilFormula const>(args[0], args[1], lowerBounds, upperBounds, tbReferences);
563 }
else if (args[0]->isTrueFormula()) {
564 return std::make_shared<storm::logic::EventuallyFormula const>(args[1], formulaContext);
566 return std::make_shared<storm::logic::UntilFormula const>(args[0], args[1]);
568 }
else if (opString ==
"G") {
570 std::vector<std::shared_ptr<storm::logic::Formula const>> args =
571 parseUnaryFormulaArgument(model, propertyStructure, formulaContext, opString, scope.refine(
"Subformula of globally operator "));
572 if (propertyStructure.count(
"step-bounds") > 0) {
573 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"Globally and step-bounds are not supported.");
574 }
else if (propertyStructure.count(
"time-bounds") > 0) {
575 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"Globally and time bounds are not supported.");
576 }
else if (propertyStructure.count(
"reward-bounds") > 0) {
577 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"Globally and reward bounded properties are not supported.");
579 return std::make_shared<storm::logic::GloballyFormula const>(args[0]);
581 }
else if (opString ==
"W") {
583 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"Weak until is not supported.");
584 }
else if (opString ==
"R") {
586 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"Release is not supported.");
587 }
else if (opString ==
"∧" || opString ==
"∨") {
588 STORM_LOG_ASSERT(bound == boost::none,
"Unexpected bound for conjunction/disjunction.");
589 std::vector<std::shared_ptr<storm::logic::Formula const>> args =
590 parseBinaryFormulaArguments(model, propertyStructure, formulaContext, opString, scope);
591 STORM_LOG_ASSERT(args.size() == 2,
"Expected two arguments for conjunction/disjunction.");
593 opString ==
"∧" ? storm::logic::BinaryBooleanStateFormula::OperatorType::And : storm::logic::BinaryBooleanStateFormula::OperatorType::Or;
594 return std::make_shared<storm::logic::BinaryBooleanStateFormula const>(oper, args[0], args[1]);
595 }
else if (opString ==
"⇒") {
596 STORM_LOG_ASSERT(bound == boost::none,
"Unexpected bound for implication.");
597 std::vector<std::shared_ptr<storm::logic::Formula const>> args =
598 parseBinaryFormulaArguments(model, propertyStructure, formulaContext, opString, scope);
599 STORM_LOG_ASSERT(args.size() == 2,
"Expected two arguments for implication.");
600 std::shared_ptr<storm::logic::UnaryBooleanStateFormula const> tmp =
601 std::make_shared<storm::logic::UnaryBooleanStateFormula const>(storm::logic::UnaryBooleanStateFormula::OperatorType::Not, args[0]);
602 return std::make_shared<storm::logic::BinaryBooleanStateFormula const>(storm::logic::BinaryBooleanStateFormula::OperatorType::Or, tmp, args[1]);
603 }
else if (opString ==
"¬") {
605 std::vector<std::shared_ptr<storm::logic::Formula const>> args =
606 parseUnaryFormulaArgument(model, propertyStructure, formulaContext, opString, scope);
608 return std::make_shared<storm::logic::UnaryBooleanStateFormula const>(storm::logic::UnaryBooleanStateFormula::OperatorType::Not, args[0]);
609 }
else if (!expr.
isInitialized() && (opString ==
"≥" || opString ==
"≤" || opString ==
"<" || opString ==
">" || opString ==
"=" || opString ==
"≠")) {
612 if (opString ==
"≥") {
614 }
else if (opString ==
"≤") {
616 }
else if (opString ==
"<") {
618 }
else if (opString ==
">") {
622 std::vector<std::string>
const leftRight = {
"left",
"right"};
623 for (uint64_t i = 0;
i < 2; ++
i) {
624 if (propertyStructure.at(leftRight[i]).count(
"op") > 0) {
625 std::string propertyOperatorString =
getString<ValueType>(propertyStructure.at(leftRight[i]).at(
"op"),
"property-operator");
626 std::set<std::string>
const propertyOperatorStrings = {
"Pmin",
"Pmax",
"Emin",
"Emax",
"Smin",
"Smax"};
627 if (propertyOperatorStrings.count(propertyOperatorString) > 0) {
629 parseExpression(propertyStructure.at(leftRight[1 - i]),
630 scope.refine(
"Threshold for operator " + propertyStructure.at(leftRight[i]).at(
"op").template get<std::string>()));
631 if ((opString ==
"=" || opString ==
"≠")) {
632 STORM_LOG_THROW(!boundExpr.containsVariables(), storm::exceptions::NotSupportedException,
633 "Comparison operators '=' or '≠' in property specifications are currently not supported.");
634 auto boundValue = boundExpr.evaluateAsRational();
636 if (opString ==
"=") {
641 }
else if (
storm::utility::isOne(boundValue) && (propertyOperatorString ==
"Pmin" || propertyOperatorString ==
"Pmax")) {
642 if (opString ==
"=") {
649 false, storm::exceptions::NotSupportedException,
650 "Comparison operators '=' or '≠' in property specifications are currently not supported in " << scope.description <<
".");
653 return parseFormula(model, propertyStructure.at(leftRight[i]), formulaContext, scope, storm::logic::Bound(ct, boundExpr));
657 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"No complex comparisons for properties are supported.");
658 }
else if (opString ==
"Multi") {
661 <<
" not enabled but model contains multi-objective property in " << scope.description
662 <<
". Continuing with that property anyways");
663 STORM_LOG_ASSERT(bound == boost::none,
"Unexpected bound for multi-objective.");
664 STORM_LOG_THROW(propertyStructure.count(
"properties") == 1, storm::exceptions::InvalidJaniException,
665 "Expecting properties for multi-objective operator in " << scope.description <<
".");
666 std::vector<std::shared_ptr<storm::logic::Formula const>> subformulas;
668 for (
auto const& subPropStructure : propertyStructure.at(
"properties")) {
669 STORM_LOG_THROW(subPropStructure.count(
"exp") == 1, storm::exceptions::InvalidJaniException,
670 "Expecting property expression in subproperty #" << i <<
" in " << scope.description <<
".");
671 subformulas.push_back(parseFormula(model, subPropStructure[
"exp"], formulaContext,
672 scope.refine(
"Subproperty #" + std::to_string(i) +
" of multi-objective operator")));
673 if (subPropStructure.count(
"opt") == 1) {
674 STORM_LOG_THROW(subformulas.back()->hasQuantitativeResult(), storm::exceptions::InvalidJaniException,
675 "Subformula #" << i <<
" has an optimization direction but is not numeric in " << scope.description <<
".");
676 STORM_LOG_THROW(subformulas.back()->isOperatorFormula(), storm::exceptions::NotSupportedException,
677 "Subformula #" << i <<
" is not an operator formula in " << scope.description <<
".");
678 std::string
const optString =
679 getString<ValueType>(subPropStructure.at(
"opt"),
"optimization direction for subproperty #" + std::to_string(i));
680 STORM_LOG_THROW(optString ==
"min" || optString ==
"max", storm::exceptions::InvalidJaniException,
681 "Unknown optimization direction " << optString <<
" for subproperty #" << i <<
" in " << scope.description <<
".");
682 auto const opt = optString ==
"min" ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize;
683 auto newFormula = subformulas.back()->clone();
684 newFormula->asOperatorFormula().setOptimalityType(opt);
685 subformulas.back() = newFormula;
687 STORM_LOG_THROW(subformulas.back()->hasQualitativeResult(), storm::exceptions::InvalidJaniException,
688 "Subformula #" << i <<
" has non-Boolean result but no optimization direction in " << scope.description <<
".");
692 STORM_LOG_THROW(propertyStructure.count(
"type") == 1, storm::exceptions::InvalidJaniException,
693 "Expecting type for multi-objective operator in " << scope.description <<
".");
694 std::string
const typeString =
getString<ValueType>(propertyStructure.at(
"type"),
"type of multi-objective operator");
695 STORM_LOG_THROW(typeString ==
"tradeoff" || typeString ==
"lexicographic", storm::exceptions::InvalidJaniException,
696 "Unknown type " << typeString <<
" for multi-objective operator in " << scope.description <<
".");
699 return std::make_shared<storm::logic::MultiObjectiveFormula const>(subformulas, type);
702 "Non-trivial Expression '" << expr <<
"' contains a boolean transient variable. Can not translate to PRCTL-like formula at "
703 << scope.description <<
".");
705 STORM_LOG_THROW(
false, storm::exceptions::InvalidJaniException,
"Unknown operator " << opString <<
".");
709 "Looking for operator for formula " << propertyStructure.dump() <<
", but did not find one.");
713template<
typename ValueType>
715 STORM_LOG_THROW(propertyStructure.count(
"name") == 1, storm::exceptions::InvalidJaniException,
"Property must have a name.");
719 std::string comment =
"";
720 if (propertyStructure.count(
"comment") > 0) {
721 comment =
getString<ValueType>(propertyStructure.at(
"comment"),
"comment for property named '" + name +
"'.");
723 STORM_LOG_THROW(propertyStructure.count(
"expression") == 1, storm::exceptions::InvalidJaniException,
"Property must have an expression.");
725 Json const& expressionStructure = propertyStructure.at(
"expression");
727 STORM_LOG_THROW(expressionStructure.count(
"op") == 1, storm::exceptions::InvalidJaniException,
728 "Expression in property must have an operation description.");
729 STORM_LOG_THROW(expressionStructure.at(
"op") ==
"filter", storm::exceptions::InvalidJaniException,
"Top level operation of a property must be a filter.");
730 STORM_LOG_THROW(expressionStructure.count(
"fun") == 1, storm::exceptions::InvalidJaniException,
"Filter must have a function descritpion.");
731 std::string funDescr =
getString<ValueType>(expressionStructure.at(
"fun"),
"Filter function in property named " + name);
733 if (funDescr ==
"min") {
735 }
else if (funDescr ==
"max") {
737 }
else if (funDescr ==
"sum") {
739 }
else if (funDescr ==
"avg") {
741 }
else if (funDescr ==
"count") {
743 }
else if (funDescr ==
"∀") {
745 }
else if (funDescr ==
"∃") {
747 }
else if (funDescr ==
"argmin") {
749 }
else if (funDescr ==
"argmax") {
751 }
else if (funDescr ==
"values") {
754 STORM_LOG_THROW(
false, storm::exceptions::InvalidJaniException,
"Unknown filter description " << funDescr <<
" in property named " << name <<
".");
757 STORM_LOG_THROW(expressionStructure.count(
"states") == 1, storm::exceptions::InvalidJaniException,
"Filter must have a states description.");
758 std::shared_ptr<storm::logic::Formula const> statesFormula;
759 if (expressionStructure.at(
"states").count(
"op") > 0) {
760 std::string statesDescr =
getString<ValueType>(expressionStructure.at(
"states").at(
"op"),
"Filtered states in property named " + name);
761 if (statesDescr ==
"initial") {
762 statesFormula = std::make_shared<storm::logic::AtomicLabelFormula>(
"init");
765 if (!statesFormula) {
770 }
catch (storm::exceptions::NotSupportedException
const& ex) {
772 }
catch (storm::exceptions::NotImplementedException
const& ex) {
776 STORM_LOG_THROW(statesFormula, storm::exceptions::NotImplementedException,
"Could not derive states formula.");
777 STORM_LOG_THROW(expressionStructure.count(
"values") == 1, storm::exceptions::InvalidJaniException,
"Values as input for a filter must be given.");
782template<
typename ValueType>
783std::shared_ptr<storm::jani::Constant> JaniParser<ValueType>::parseConstant(Json
const& constantStructure, Scope
const& scope) {
784 STORM_LOG_THROW(constantStructure.count(
"name") == 1, storm::exceptions::InvalidJaniException,
785 "Variable (scope: " + scope.description +
") must have a name.");
786 std::string name =
getString<ValueType>(constantStructure.at(
"name"),
"variable-name in " + scope.description +
"-scope");
789 std::string exprManagerName = name;
791 STORM_LOG_THROW(constantStructure.count(
"type") == 1, storm::exceptions::InvalidJaniException,
792 "Constant '" + name +
"' (scope: " + scope.description +
") must have a (single) type-declaration.");
793 auto type = parseType(constantStructure.at(
"type"), name, scope);
794 STORM_LOG_THROW((type.first->isBasicType() || type.first->isBoundedType()), storm::exceptions::InvalidJaniException,
795 "Constant '" + name +
"' (scope: " + scope.description +
") has unexpected type.");
797 uint_fast64_t valueCount = constantStructure.count(
"value");
799 STORM_LOG_THROW(valueCount < 2, storm::exceptions::InvalidJaniException,
800 "Value for constant '" + name +
"' (scope: " + scope.description +
") must be given at most once.");
801 if (valueCount == 1) {
804 definingExpression = parseExpression(constantStructure.at(
"value"), scope.refine(
"Value of constant " + name));
808 storm::exceptions::InvalidJaniException,
809 "Type of value for constant '" + name +
"' (scope: " + scope.description +
") does not match the given type '" +
810 type.first->getStringRepresentation() +
".");
816 if (type.first->isBoundedType()) {
817 auto const& bndType = type.first->asBoundedType();
818 if (bndType.hasLowerBound()) {
819 constraintExpression = var.
getExpression() >= bndType.getLowerBound();
820 if (bndType.hasUpperBound()) {
821 constraintExpression = constraintExpression && var.
getExpression() <= bndType.getUpperBound();
823 }
else if (bndType.hasUpperBound()) {
824 constraintExpression = var.
getExpression() <= bndType.getUpperBound();
827 return std::make_shared<storm::jani::Constant>(name, std::move(var), definingExpression, constraintExpression);
830template<
typename ValueType>
832 std::string variableName,
Scope const& scope) {
834 if (typeStructure.is_string()) {
835 if (typeStructure ==
"real") {
837 result.second = expressionManager->getRationalType();
838 }
else if (typeStructure ==
"bool") {
840 result.second = expressionManager->getBooleanType();
841 }
else if (typeStructure ==
"int") {
843 result.second = expressionManager->getIntegerType();
844 }
else if (typeStructure ==
"clock") {
845 result.first = std::make_unique<storm::jani::ClockType>();
846 result.second = expressionManager->getRationalType();
847 }
else if (typeStructure ==
"continuous") {
848 result.first = std::make_unique<storm::jani::ContinuousType>();
849 result.second = expressionManager->getRationalType();
852 "Unsupported type " << typeStructure.dump() <<
" for variable '" << variableName <<
"' (scope: " << scope.
description <<
").");
854 }
else if (typeStructure.is_object()) {
855 STORM_LOG_THROW(typeStructure.count(
"kind") == 1, storm::exceptions::InvalidJaniException,
856 "For complex type as in variable " << variableName <<
"(scope: " << scope.
description <<
") kind must be given.");
859 if (kind ==
"bounded") {
861 typeStructure.count(
"lower-bound") + typeStructure.count(
"upper-bound") > 0, storm::exceptions::InvalidJaniException,
862 "For bounded type as in variable " << variableName <<
"(scope: " << scope.
description <<
") lower-bound or upper-bound must be given.");
864 if (typeStructure.count(
"lower-bound") > 0) {
865 lowerboundExpr =
parseExpression(typeStructure.at(
"lower-bound"), scope.
refine(
"Lower bound for variable " + variableName));
868 if (typeStructure.count(
"upper-bound") > 0) {
869 upperboundExpr =
parseExpression(typeStructure.at(
"upper-bound"), scope.
refine(
"Upper bound for variable " + variableName));
871 STORM_LOG_THROW(typeStructure.count(
"base") == 1, storm::exceptions::InvalidJaniException,
872 "For bounded type as in variable " << variableName <<
"(scope: " << scope.
description <<
") base must be given.");
873 std::string basictype =
875 if (basictype ==
"int") {
877 "Lower bound for bounded integer variable " << variableName <<
"(scope: " << scope.
description <<
") must be integer-typed.");
879 "Upper bound for bounded integer variable " << variableName <<
"(scope: " << scope.
description <<
") must be integer-typed.");
883 "Lower bound must not be larger than upper bound for bounded integer variable " << variableName
887 result.second = expressionManager->getIntegerType();
888 }
else if (basictype ==
"real") {
890 "Lower bound for bounded real variable " << variableName <<
"(scope: " << scope.
description <<
") must be numeric.");
892 "Upper bound for bounded real variable " << variableName <<
"(scope: " << scope.
description <<
") must be numeric.");
895 using SubMap = std::map<storm::expressions::Variable, storm::expressions::Expression>;
899 STORM_LOG_THROW(lowerboundValue <= upperboundValue, storm::exceptions::InvalidJaniException,
900 "Lower bound must not be larger than upper bound for bounded real variable " << variableName
904 result.second = expressionManager->getRationalType();
907 "Unsupported base " << basictype <<
" for bounded variable " << variableName <<
"(scope: " << scope.
description <<
").");
909 }
else if (kind ==
"array") {
910 STORM_LOG_THROW(typeStructure.count(
"base") == 1, storm::exceptions::InvalidJaniException,
911 "For array type as in variable " << variableName <<
"(scope: " << scope.
description <<
") base must be given.");
912 auto base =
parseType(typeStructure.at(
"base"), variableName, scope);
913 result.first = std::make_unique<storm::jani::ArrayType>(std::move(base.first));
914 result.second = expressionManager->getArrayType(base.second);
917 "Unsupported kind " << kind <<
" for complex type of variable " << variableName <<
"(scope: " << scope.
description <<
").");
923template<
typename ValueType>
924storm::jani::FunctionDefinition JaniParser<ValueType>::parseFunctionDefinition(Json
const& functionDefinitionStructure, Scope
const& scope,
bool firstPass,
925 std::string
const& parameterNamePrefix) {
926 STORM_LOG_THROW(functionDefinitionStructure.count(
"name") == 1, storm::exceptions::InvalidJaniException,
927 "Function definition (scope: " + scope.description +
") must have a name.");
928 std::string functionName =
getString<ValueType>(functionDefinitionStructure.at(
"name"),
"function-name in " + scope.description);
929 STORM_LOG_THROW(functionDefinitionStructure.count(
"type") == 1, storm::exceptions::InvalidJaniException,
930 "Function definition '" + functionName +
"' (scope: " + scope.description +
") must have a (single) type-declaration.");
931 auto type = parseType(functionDefinitionStructure.at(
"type"), functionName, scope);
933 !type.first->isClockType() && !type.first->isContinuousType(), storm::exceptions::InvalidJaniException,
934 "Function definition '" + functionName +
"' (scope: " + scope.description +
") uses illegal type '" + type.first->getStringRepresentation() +
"'.");
936 std::unordered_map<std::string, storm::expressions::Variable> parameterNameToVariableMap;
937 std::vector<storm::expressions::Variable> parameters;
938 if (!firstPass && functionDefinitionStructure.count(
"parameters") > 0) {
939 STORM_LOG_THROW(functionDefinitionStructure.count(
"parameters") == 1, storm::exceptions::InvalidJaniException,
940 "Function definition '" + functionName +
"' (scope: " + scope.description +
") must have exactly one list of parameters.");
941 for (
auto const& parameterStructure : functionDefinitionStructure.at(
"parameters")) {
942 STORM_LOG_THROW(parameterStructure.count(
"name") == 1, storm::exceptions::InvalidJaniException,
943 "Parameter declaration of parameter " + std::to_string(parameters.size()) +
" of Function definition '" + functionName +
944 "' (scope: " + scope.description +
") must have a name.");
945 std::string parameterName =
946 getString<ValueType>(parameterStructure.at(
"name"),
"parameter-name of parameter " + std::to_string(parameters.size()) +
947 " of Function definition '" + functionName +
"' (scope: " + scope.description +
").");
948 STORM_LOG_THROW(parameterStructure.count(
"type") == 1, storm::exceptions::InvalidJaniException,
949 "Parameter declaration of parameter " + std::to_string(parameters.size()) +
" of Function definition '" + functionName +
950 "' (scope: " + scope.description +
") must have exactly one type.");
952 parseType(parameterStructure.at(
"type"), parameterName,
953 scope.refine(
"parameter declaration of parameter " + std::to_string(parameters.size()) +
" of Function definition " + functionName));
954 STORM_LOG_THROW(!parameterType.first->isClockType() && !parameterType.first->isContinuousType(), storm::exceptions::InvalidJaniException,
955 "Type of parameter " + std::to_string(parameters.size()) +
" of function definition '" + functionName +
956 "' (scope: " + scope.description +
") uses illegal type '" + parameterType.first->getStringRepresentation() +
"'.");
958 "Bounds on parameter" + parameterName +
" of function definition " + functionName +
" will be ignored.");
961 parameters.push_back(expressionManager->declareVariable(exprParameterName, parameterType.second));
962 parameterNameToVariableMap.emplace(parameterName, parameters.back());
966 STORM_LOG_THROW(functionDefinitionStructure.count(
"body") == 1, storm::exceptions::InvalidJaniException,
967 "Function definition '" + functionName +
"' (scope: " + scope.description +
") must have a (single) body.");
970 functionBody = parseExpression(functionDefinitionStructure.at(
"body"), scope.refine(
"body of function definition " + functionName),
false,
971 parameterNameToVariableMap);
973 "Type of body of function " + functionName +
"' (scope: " + scope.description +
") has type "
974 << functionBody.
getType() <<
" although the function type is given as " << type.second);
976 return storm::jani::FunctionDefinition(functionName, type.second, parameters, functionBody);
979template<
typename ValueType>
981 STORM_LOG_THROW(variableStructure.count(
"name") == 1, storm::exceptions::InvalidJaniException,
982 "Variable (scope: " + scope.
description +
") must have a name.");
986 std::string exprManagerName = namePrefix + name;
987 bool transientVar = defaultVariableTransient;
988 uint_fast64_t tvarcount = variableStructure.count(
"transient");
989 STORM_LOG_THROW(tvarcount <= 1, storm::exceptions::InvalidJaniException,
990 "Multiple definitions of transient not allowed in variable '" + name +
"' (scope: " + scope.
description +
").");
991 if (tvarcount == 1) {
995 STORM_LOG_THROW(variableStructure.count(
"type") == 1, storm::exceptions::InvalidJaniException,
996 "Variable '" + name +
"' (scope: " + scope.
description +
") must have a (single) type-declaration.");
997 auto type =
parseType(variableStructure.at(
"type"), name, scope);
999 uint_fast64_t initvalcount = variableStructure.count(
"initial-value");
1001 STORM_LOG_THROW(initvalcount == 1, storm::exceptions::InvalidJaniException,
1002 "Initial value must be given once for transient variable '" + name +
"' (scope: " + scope.
description +
") " + name +
1005 STORM_LOG_THROW(initvalcount <= 1, storm::exceptions::InvalidJaniException,
1006 "Initial value can be given at most one for variable " + name +
"' (scope: " + scope.
description +
").");
1008 boost::optional<storm::expressions::Expression> initVal;
1009 if (initvalcount == 1 && !variableStructure.at(
"initial-value").is_null()) {
1010 initVal =
parseExpression(variableStructure.at(
"initial-value"), scope.
refine(
"Initial value for variable " + name));
1018 if (transientVar && type.first->isBasicType() && type.first->asBasicType().isBooleanType()) {
1019 labels.insert(name);
1022 auto expressionVariable = expressionManager->declareVariable(exprManagerName, type.second);
1030 STORM_LOG_THROW(expected == actual, storm::exceptions::InvalidJaniException,
1031 "Operator " << opstring <<
" expects " << expected <<
" arguments, but got " << actual <<
" in " << errorInfo <<
".");
1034template<
typename ValueType>
1035std::vector<storm::expressions::Expression> JaniParser<ValueType>::parseUnaryExpressionArguments(
1036 Json
const& expressionDecl, std::string
const& opstring, Scope
const& scope,
bool returnNoneInitializedOnUnknownOperator,
1037 std::unordered_map<std::string, storm::expressions::Variable>
const& auxiliaryVariables) {
1039 parseExpression(expressionDecl.at(
"exp"), scope.refine(
"Argument of operator " + opstring), returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1043template<
typename ValueType>
1044std::vector<storm::expressions::Expression> JaniParser<ValueType>::parseBinaryExpressionArguments(
1045 Json
const& expressionDecl, std::string
const& opstring, Scope
const& scope,
bool returnNoneInitializedOnUnknownOperator,
1046 std::unordered_map<std::string, storm::expressions::Variable>
const& auxiliaryVariables) {
1047 storm::expressions::Expression left = parseExpression(expressionDecl.at(
"left"), scope.refine(
"Left argument of operator " + opstring),
1048 returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1049 storm::expressions::Expression right = parseExpression(expressionDecl.at(
"right"), scope.refine(
"Right argument of operator " + opstring),
1050 returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1051 return {left, right};
1058 "Operator " << opstring <<
" expects argument[" << argNr <<
"]: '" << expr <<
"' to be Boolean in " << errorInfo <<
".");
1066 "Operator " << opstring <<
" expects argument " + std::to_string(argNr) +
" to be numerical in " << errorInfo <<
".");
1074 "Operator " << opstring <<
" expects argument " + std::to_string(argNr) +
" to be numerical in " << errorInfo <<
".");
1082 "Operator " << opstring <<
" expects argument " + std::to_string(argNr) +
" to be of type 'array' in " << errorInfo <<
".");
1085template<
typename ValueType>
1087 if (lValueStructure.is_string()) {
1091 auto localVar = scope.
localVars->find(ident);
1092 if (localVar != scope.
localVars->end()) {
1093 var = localVar->second;
1096 if (var ==
nullptr) {
1098 "Unknown identifier '" << ident <<
"' occurs in " << scope.
description <<
".");
1099 auto globalVar = scope.
globalVars->find(ident);
1101 "Unknown identifier '" << ident <<
"' occurs in " << scope.
description <<
".");
1102 var = globalVar->second;
1106 }
else if (lValueStructure.count(
"op") == 1) {
1111 STORM_LOG_THROW(opstring ==
"aa", storm::exceptions::InvalidJaniException,
1112 "Unknown operation '" << opstring <<
"' occurs in " << scope.
description <<
".");
1113 STORM_LOG_THROW(lValueStructure.count(
"exp") == 1, storm::exceptions::InvalidJaniException,
1114 "Missing 'exp' in array access at " << scope.
description <<
".");
1115 auto expLValue =
parseLValue(lValueStructure.at(
"exp"), scope.
refine(
"Expression of array access"));
1116 STORM_LOG_THROW(expLValue.isArray(), storm::exceptions::InvalidJaniException,
1117 "Array access considers non-array expression at " << scope.
description <<
".");
1118 STORM_LOG_THROW(lValueStructure.count(
"index"), storm::exceptions::InvalidJaniException,
1119 "Missing 'index' in array access at " << scope.
description <<
".");
1120 auto indexExpression =
parseExpression(lValueStructure.at(
"index"), scope.
refine(
"Index of array access"));
1121 expLValue.addArrayAccessIndex(indexExpression);
1125 "Unknown LValue '" << lValueStructure.dump() <<
"' occurs in " << scope.
description <<
".");
1131template<
typename ValueType>
1133 std::string
const& ident, Scope
const& scope, std::unordered_map<std::string, storm::expressions::Variable>
const& auxiliaryVariables) {
1135 auto it = auxiliaryVariables.find(ident);
1136 if (it != auxiliaryVariables.end()) {
1140 if (scope.localVars !=
nullptr) {
1141 auto it = scope.localVars->find(ident);
1142 if (it != scope.localVars->end()) {
1143 return it->second->getExpressionVariable();
1146 if (scope.globalVars !=
nullptr) {
1147 auto it = scope.globalVars->find(ident);
1148 if (it != scope.globalVars->end()) {
1149 return it->second->getExpressionVariable();
1152 if (scope.constants !=
nullptr) {
1153 auto it = scope.constants->find(ident);
1154 if (it != scope.constants->end()) {
1155 return it->second->getExpressionVariable();
1158 STORM_LOG_THROW(
false, storm::exceptions::InvalidJaniException,
"Unknown identifier '" << ident <<
"' occurs in " << scope.description <<
".");
1160 return storm::expressions::Variable();
1163template<
typename ValueType>
1165 bool returnNoneInitializedOnUnknownOperator,
1166 std::unordered_map<std::string, storm::expressions::Variable>
const& auxiliaryVariables) {
1167 if (expressionStructure.is_boolean()) {
1168 if (expressionStructure.template get<bool>()) {
1169 return expressionManager->boolean(
true);
1171 return expressionManager->boolean(
false);
1173 }
else if (expressionStructure.is_number_integer()) {
1174 return expressionManager->integer(expressionStructure.template get<int64_t>());
1175 }
else if (expressionStructure.is_number_float()) {
1177 }
else if (expressionStructure.is_string()) {
1178 std::string ident = expressionStructure.template get<std::string>();
1180 }
else if (expressionStructure.is_object()) {
1181 if (expressionStructure.count(
"distribution") == 1) {
1183 false, storm::exceptions::InvalidJaniException,
1184 "Distributions are not supported by storm expressions, cannot import " << expressionStructure.dump() <<
" in " << scope.
description <<
".");
1186 if (expressionStructure.count(
"op") == 1) {
1188 std::vector<storm::expressions::Expression> arguments = {};
1189 if (opstring ==
"ite") {
1190 STORM_LOG_THROW(expressionStructure.count(
"if") == 1, storm::exceptions::InvalidJaniException,
"If operator required.");
1191 STORM_LOG_THROW(expressionStructure.count(
"else") == 1, storm::exceptions::InvalidJaniException,
"Else operator required.");
1192 STORM_LOG_THROW(expressionStructure.count(
"then") == 1, storm::exceptions::InvalidJaniException,
"Then operator required.");
1193 arguments.push_back(
1194 parseExpression(expressionStructure.at(
"if"), scope.
refine(
"if-formula"), returnNoneInitializedOnUnknownOperator, auxiliaryVariables));
1195 arguments.push_back(
1196 parseExpression(expressionStructure.at(
"then"), scope.
refine(
"then-formula"), returnNoneInitializedOnUnknownOperator, auxiliaryVariables));
1197 arguments.push_back(
1198 parseExpression(expressionStructure.at(
"else"), scope.
refine(
"else-formula"), returnNoneInitializedOnUnknownOperator, auxiliaryVariables));
1203 }
else if (opstring ==
"∨") {
1204 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1206 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1211 return arguments[0] || arguments[1];
1212 }
else if (opstring ==
"∧") {
1213 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1215 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1220 return arguments[0] && arguments[1];
1221 }
else if (opstring ==
"⇒") {
1222 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1224 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1229 return (!arguments[0]) || arguments[1];
1230 }
else if (opstring ==
"¬") {
1231 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1233 if (!arguments[0].isInitialized()) {
1237 return !arguments[0];
1238 }
else if (opstring ==
"=") {
1239 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1241 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1244 if (arguments[0].hasBooleanType()) {
1249 return arguments[0] == arguments[1];
1251 }
else if (opstring ==
"≠") {
1252 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1254 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1257 if (arguments[0].hasBooleanType()) {
1262 return arguments[0] != arguments[1];
1264 }
else if (opstring ==
"<") {
1265 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1267 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1272 return arguments[0] < arguments[1];
1273 }
else if (opstring ==
"≤") {
1274 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1276 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1281 return arguments[0] <= arguments[1];
1282 }
else if (opstring ==
">") {
1283 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1285 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1290 return arguments[0] > arguments[1];
1291 }
else if (opstring ==
"≥") {
1292 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1294 if (!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
1299 return arguments[0] >= arguments[1];
1300 }
else if (opstring ==
"+") {
1301 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1305 return arguments[0] + arguments[1];
1306 }
else if (opstring ==
"-" && expressionStructure.count(
"left") > 0) {
1307 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1311 return arguments[0] - arguments[1];
1312 }
else if (opstring ==
"-") {
1313 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1316 return -arguments[0];
1317 }
else if (opstring ==
"*") {
1318 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1322 return arguments[0] * arguments[1];
1323 }
else if (opstring ==
"/") {
1324 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1328 return arguments[0] / arguments[1];
1329 }
else if (opstring ==
"%") {
1330 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1334 return arguments[0] % arguments[1];
1335 }
else if (opstring ==
"max") {
1336 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1341 }
else if (opstring ==
"min") {
1342 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1347 }
else if (opstring ==
"floor") {
1348 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1352 }
else if (opstring ==
"ceil") {
1353 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1357 }
else if (opstring ==
"abs") {
1358 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1362 }
else if (opstring ==
"sgn") {
1363 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1367 }
else if (opstring ==
"trc") {
1368 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1372 }
else if (opstring ==
"pow") {
1373 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1378 }
else if (opstring ==
"exp") {
1379 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1384 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"Exp operation is not yet implemented.");
1385 }
else if (opstring ==
"log") {
1386 arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1391 }
else if (opstring ==
"cos") {
1392 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1396 }
else if (opstring ==
"sin") {
1397 arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1401 }
else if (opstring ==
"aa") {
1402 STORM_LOG_THROW(expressionStructure.count(
"exp") == 1, storm::exceptions::InvalidJaniException,
1403 "Array access operator requires exactly one exp (at " + scope.
description +
").");
1405 returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1406 STORM_LOG_THROW(expressionStructure.count(
"index") == 1, storm::exceptions::InvalidJaniException,
1407 "Array access operator requires exactly one index (at " + scope.
description +
").");
1409 returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1415 }
else if (opstring ==
"av") {
1416 STORM_LOG_THROW(expressionStructure.count(
"elements") == 1, storm::exceptions::InvalidJaniException,
1417 "Array value operator requires exactly one 'elements' (at " + scope.
description +
").");
1418 std::vector<std::shared_ptr<storm::expressions::BaseExpression const>> elements;
1421 for (
auto const& element : expressionStructure.at(
"elements")) {
1422 elements.push_back(
parseExpression(element, scope.
refine(
"element " + std::to_string(elements.size()) +
" of array value expression"),
1423 returnNoneInitializedOnUnknownOperator, auxiliaryVariables)
1424 .getBaseExpressionPointer());
1426 commonType = elements.back()->getType();
1428 }
else if (commonType != elements.back()->getType()) {
1429 if (commonType.
isIntegerType() && elements.back()->getType().isRationalType()) {
1430 commonType = elements.back()->getType();
1433 "Incompatible element types " << commonType <<
" and " << elements.back()->getType()
1434 <<
" of array value expression at " << scope.
description <<
".");
1438 return std::make_shared<storm::expressions::ValueArrayExpression>(*expressionManager, expressionManager->getArrayType(commonType), elements)
1440 }
else if (opstring ==
"ac") {
1441 STORM_LOG_THROW(expressionStructure.count(
"length") == 1, storm::exceptions::InvalidJaniException,
1442 "Array access operator requires exactly one length (at " + scope.
description +
").");
1444 returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
1446 STORM_LOG_THROW(expressionStructure.count(
"var") == 1, storm::exceptions::InvalidJaniException,
1447 "Array access operator requires exactly one var (at " + scope.
description +
").");
1448 std::string indexVarName =
1450 STORM_LOG_THROW(auxiliaryVariables.find(indexVarName) == auxiliaryVariables.end(), storm::exceptions::InvalidJaniException,
1451 "Index variable " << indexVarName <<
" is already defined as an auxiliary variable (at " + scope.
description +
").");
1452 auto newAuxVars = auxiliaryVariables;
1454 newAuxVars.emplace(indexVarName, indexVar);
1455 STORM_LOG_THROW(expressionStructure.count(
"exp") == 1, storm::exceptions::InvalidJaniException,
1456 "Array constructor operator requires exactly one exp (at " + scope.
description +
").");
1458 returnNoneInitializedOnUnknownOperator, newAuxVars);
1459 return std::make_shared<storm::expressions::ConstructorArrayExpression>(*expressionManager, expressionManager->getArrayType(exp.
getType()),
1463 }
else if (opstring ==
"call") {
1464 STORM_LOG_THROW(expressionStructure.count(
"function") == 1, storm::exceptions::InvalidJaniException,
1465 "Function call operator requires exactly one function (at " + scope.
description +
").");
1466 std::string functionName =
1475 "Function call operator calls unknown function '" + functionName +
"' (at " + scope.
description +
").");
1477 STORM_LOG_THROW(expressionStructure.count(
"args") == 1, storm::exceptions::InvalidJaniException,
1478 "Function call operator requires exactly one args (at " + scope.
description +
").");
1479 std::vector<std::shared_ptr<storm::expressions::BaseExpression const>> args;
1480 if (expressionStructure.count(
"args") > 0) {
1481 STORM_LOG_THROW(expressionStructure.count(
"args") == 1, storm::exceptions::InvalidJaniException,
1482 "Function call operator requires exactly one args (at " + scope.
description +
").");
1483 for (
auto const& arg : expressionStructure.at(
"args")) {
1484 args.push_back(
parseExpression(arg, scope.
refine(
"argument " + std::to_string(args.size()) +
" of function call expression"),
1485 returnNoneInitializedOnUnknownOperator, auxiliaryVariables)
1486 .getBaseExpressionPointer());
1489 return std::make_shared<storm::expressions::FunctionCallExpression>(*expressionManager, functionDefinition->
getType(), functionName, args)
1491 }
else if (unsupportedOpstrings.count(opstring) > 0) {
1492 STORM_LOG_THROW(
false, storm::exceptions::InvalidJaniException,
"Opstring " + opstring +
" is not supported by storm.");
1494 if (returnNoneInitializedOnUnknownOperator) {
1497 STORM_LOG_THROW(
false, storm::exceptions::InvalidJaniException,
"Unknown operator " << opstring <<
" in " << scope.
description <<
".");
1500 if (expressionStructure.count(
"constant") == 1) {
1503 if (constantStr ==
"Ï€") {
1504 return std::make_shared<storm::expressions::TranscendentalNumberLiteralExpression>(
1508 if (constantStr ==
"e") {
1509 return std::make_shared<storm::expressions::TranscendentalNumberLiteralExpression>(
1515 false, storm::exceptions::InvalidJaniException,
1516 "No supported operator declaration found for complex expressions as " << expressionStructure.dump() <<
" in " << scope.
description <<
".");
1519 "No supported expression found at " << expressionStructure.dump() <<
" in " << scope.
description <<
".");
1524template<
typename ValueType>
1525void JaniParser<ValueType>::parseActions(Json
const& actionStructure,
storm::jani::Model& parentModel) {
1526 std::set<std::string> actionNames;
1527 for (
auto const& actionEntry : actionStructure) {
1528 STORM_LOG_THROW(actionEntry.count(
"name") == 1, storm::exceptions::InvalidJaniException,
"Actions must have exactly one name.");
1530 STORM_LOG_THROW(actionNames.count(actionName) == 0, storm::exceptions::InvalidJaniException,
"Action with name " + actionName +
" already exists.");
1532 actionNames.emplace(actionName);
1536template<
typename ValueType>
1538 STORM_LOG_THROW(automatonStructure.count(
"name") == 1, storm::exceptions::InvalidJaniException,
"Each automaton must have a name.");
1539 std::string name =
getString<ValueType>(automatonStructure.at(
"name"),
" the name field for automaton");
1543 uint64_t varDeclCount = automatonStructure.count(
"variables");
1544 STORM_LOG_THROW(varDeclCount < 2, storm::exceptions::InvalidJaniException,
"Automaton '" << name <<
"' has more than one list of variables.");
1547 if (varDeclCount > 0) {
1548 for (
auto const& varStructure : automatonStructure.at(
"variables")) {
1551 STORM_LOG_ASSERT(localVars.count(var->getName()) == 0,
"Local variable already exists.");
1552 localVars.emplace(var->getName(), &automaton.
addVariable(*var));
1556 uint64_t funDeclCount = automatonStructure.count(
"functions");
1557 STORM_LOG_THROW(funDeclCount < 2, storm::exceptions::InvalidJaniException,
"Automaton '" << name <<
"' has more than one list of functions.");
1560 if (funDeclCount > 0) {
1562 std::vector<storm::jani::FunctionDefinition> dummyFunctionDefinitions;
1563 for (
auto const& funStructure : automatonStructure.at(
"functions")) {
1565 dummyFunctionDefinitions.push_back(
1566 parseFunctionDefinition(funStructure, scope.
refine(
"functions[" + std::to_string(localFuns.size()) +
"] of automaton " + name),
true));
1570 for (
auto const& funDef : dummyFunctionDefinitions) {
1571 bool unused = localFuns.emplace(funDef.getName(), &funDef).second;
1573 "Multiple definitions of functions with the name " << funDef.getName() <<
" in " << scope.
description <<
".");
1575 for (
auto const& funStructure : automatonStructure.at(
"functions")) {
1578 parseFunctionDefinition(funStructure, scope.
refine(
"functions[" + std::to_string(localFuns.size()) +
"] of automaton " + name),
false,
1585 STORM_LOG_THROW(automatonStructure.count(
"locations") > 0, storm::exceptions::InvalidJaniException,
"Automaton '" << name <<
"' does not have locations.");
1586 std::unordered_map<std::string, uint64_t> locIds;
1587 for (
auto const& locEntry : automatonStructure.at(
"locations")) {
1588 STORM_LOG_THROW(locEntry.count(
"name") == 1, storm::exceptions::InvalidJaniException,
1589 "Locations for automaton '" << name <<
"' must have exactly one name.");
1590 std::string locName =
getString<ValueType>(locEntry.at(
"name"),
"location of automaton " + name);
1591 STORM_LOG_THROW(locIds.count(locName) == 0, storm::exceptions::InvalidJaniException,
1592 "Location with name '" + locName +
"' already exists in automaton '" + name +
"'.");
1593 STORM_LOG_THROW(locEntry.count(
"invariant") == 0, storm::exceptions::InvalidJaniException,
1594 "Invariants in locations as in '" + locName +
"' in automaton '" + name +
"' are not supported.");
1597 std::vector<storm::jani::Assignment> transientAssignments;
1598 if (locEntry.count(
"transient-values") > 0) {
1599 for (
auto const& transientValueEntry : locEntry.at(
"transient-values")) {
1600 STORM_LOG_THROW(transientValueEntry.count(
"ref") == 1, storm::exceptions::InvalidJaniException,
1601 "Transient values in location " << locName <<
" need exactly one ref that is assigned to.");
1602 STORM_LOG_THROW(transientValueEntry.count(
"value") == 1, storm::exceptions::InvalidJaniException,
1603 "Transient values in location " << locName <<
" need exactly one assigned value.");
1606 "Assigned non-transient variable " << lValue <<
" in location " + locName +
" (automaton: '" + name +
"').");
1608 parseExpression(transientValueEntry.at(
"value"), scope.
refine(
"Assignment of lValue in location " + locName));
1609 transientAssignments.emplace_back(lValue, rhs);
1613 locIds.emplace(locName,
id);
1615 STORM_LOG_THROW(automatonStructure.count(
"initial-locations") == 1, storm::exceptions::InvalidJaniException,
1616 "Automaton '" << name <<
"' does not have initial locations.");
1617 for (
Json const& initLocStruct : automatonStructure.at(
"initial-locations")) {
1620 STORM_LOG_THROW(automatonStructure.count(
"restrict-initial") < 2, storm::exceptions::InvalidJaniException,
1621 "Automaton '" << name <<
"' has multiple initial value restrictions.");
1623 if (automatonStructure.count(
"restrict-initial") > 0) {
1624 STORM_LOG_THROW(automatonStructure.at(
"restrict-initial").count(
"exp") == 1, storm::exceptions::InvalidJaniException,
1625 "Automaton '" << name <<
"' needs an expression inside the initial restricion.");
1626 initialValueRestriction =
parseExpression(automatonStructure.at(
"restrict-initial").at(
"exp"), scope.
refine(
"Initial value restriction"));
1630 STORM_LOG_THROW(automatonStructure.count(
"edges") > 0, storm::exceptions::InvalidJaniException,
"Automaton '" << name <<
"' must have a list of edges.");
1631 for (
auto const& edgeEntry : automatonStructure.at(
"edges")) {
1633 STORM_LOG_THROW(edgeEntry.count(
"location") == 1, storm::exceptions::InvalidJaniException,
1634 "Each edge in automaton '" << name <<
"' must have a source.");
1635 std::string sourceLoc =
getString<ValueType>(edgeEntry.at(
"location"),
"source location for edge in automaton '" + name +
"'");
1636 STORM_LOG_THROW(locIds.count(sourceLoc) == 1, storm::exceptions::InvalidJaniException,
1637 "Source of edge has unknown location '" << sourceLoc <<
"' in automaton '" << name <<
"'.");
1639 STORM_LOG_THROW(edgeEntry.count(
"action") < 2, storm::exceptions::InvalidJaniException,
1640 "Edge from " << sourceLoc <<
" in automaton " << name <<
" has multiple actions.");
1642 if (edgeEntry.count(
"action") > 0) {
1643 action =
getString<ValueType>(edgeEntry.at(
"action"),
"action name in edge from '" + sourceLoc +
"' in automaton '" + name +
"'");
1648 STORM_LOG_THROW(edgeEntry.count(
"rate") < 2, storm::exceptions::InvalidJaniException,
1649 "Edge from '" << sourceLoc <<
"' in automaton '" << name <<
"' has multiple rates.");
1651 if (edgeEntry.count(
"rate") > 0) {
1652 STORM_LOG_THROW(edgeEntry.at(
"rate").count(
"exp") == 1, storm::exceptions::InvalidJaniException,
1653 "Rate in edge from '" << sourceLoc <<
"' in automaton '" << name <<
"' must have a defing expression.");
1654 rateExpr =
parseExpression(edgeEntry.at(
"rate").at(
"exp"), scope.
refine(
"rate expression in edge from '" + sourceLoc));
1657 storm::exceptions::InvalidJaniException,
"Only positive rates are allowed but rate '" << rateExpr <<
" was found.");
1660 STORM_LOG_THROW(edgeEntry.count(
"guard") <= 1, storm::exceptions::InvalidJaniException,
1661 "Guard can be given at most once in edge from '" << sourceLoc <<
"' in automaton '" << name <<
"'.");
1663 if (edgeEntry.count(
"guard") == 1) {
1664 STORM_LOG_THROW(edgeEntry.at(
"guard").count(
"exp") == 1, storm::exceptions::InvalidJaniException,
1665 "Guard in edge from '" + sourceLoc +
"' in automaton '" + name +
"' must have one expression.");
1666 guardExpr =
parseExpression(edgeEntry.at(
"guard").at(
"exp"), scope.
refine(
"guard expression in edge from '" + sourceLoc));
1667 STORM_LOG_THROW(guardExpr.
hasBooleanType(), storm::exceptions::InvalidJaniException,
"Guard " << guardExpr <<
" does not have Boolean type.");
1670 std::shared_ptr<storm::jani::TemplateEdge> templateEdge = std::make_shared<storm::jani::TemplateEdge>(guardExpr);
1673 if (edgeEntry.count(
"assignments") > 0) {
1674 STORM_LOG_THROW(edgeEntry.count(
"assignments") == 1, storm::exceptions::InvalidJaniException,
1675 "Multiple edge assignments in edge from '" + sourceLoc +
"' in automaton '" + name +
"'.");
1676 for (
auto const& assignmentEntry : edgeEntry.at(
"assignments")) {
1678 STORM_LOG_THROW(assignmentEntry.count(
"ref") == 1, storm::exceptions::InvalidJaniException,
1679 "Assignment in edge from '" << sourceLoc <<
"' in automaton '" << name <<
"'must have one ref field.");
1681 parseLValue(assignmentEntry.at(
"ref"), scope.
refine(
"Assignment variable in edge from '" + sourceLoc +
"' in automaton '" + name +
"'"));
1683 STORM_LOG_THROW(assignmentEntry.count(
"value") == 1, storm::exceptions::InvalidJaniException,
1684 "Assignment in edge from '" << sourceLoc <<
"' in automaton '" << name <<
"' must have one value field.");
1686 parseExpression(assignmentEntry.at(
"value"), scope.
refine(
"assignment in edge from '" + sourceLoc +
"' in automaton '" + name +
"'"));
1689 int64_t assignmentIndex = 0;
1690 if (assignmentEntry.count(
"index") > 0) {
1692 getSignedInt<ValueType>(assignmentEntry.at(
"index"),
"assignment index in edge from '" + sourceLoc +
"' in automaton '" + name +
"'");
1699 STORM_LOG_THROW(edgeEntry.count(
"destinations") == 1, storm::exceptions::InvalidJaniException,
1700 "A single list of destinations must be given in edge from '" << sourceLoc <<
"' in automaton '" << name <<
"'.");
1701 std::vector<std::pair<uint64_t, storm::expressions::Expression>> destinationLocationsAndProbabilities;
1702 for (
auto const& destEntry : edgeEntry.at(
"destinations")) {
1704 STORM_LOG_THROW(destEntry.count(
"location") == 1, storm::exceptions::InvalidJaniException,
1705 "Each destination in edge from '" << sourceLoc <<
"' in automaton '" << name <<
"' must have a target location.");
1706 std::string targetLoc =
1707 getString<ValueType>(destEntry.at(
"location"),
"target location for edge from '" + sourceLoc +
"' in automaton '" + name +
"'");
1708 STORM_LOG_THROW(locIds.count(targetLoc) == 1, storm::exceptions::InvalidJaniException,
1709 "Target of edge has unknown location '" << targetLoc <<
"' in automaton '" << name <<
"'.");
1712 unsigned probDeclCount = destEntry.count(
"probability");
1713 STORM_LOG_THROW(probDeclCount < 2, storm::exceptions::InvalidJaniException,
1714 "Destination in edge from '" << sourceLoc <<
"' to '" << targetLoc <<
"' in automaton '" << name <<
"' has multiple probabilites.");
1715 if (probDeclCount == 0) {
1716 probExpr = expressionManager->rational(1.0);
1718 STORM_LOG_THROW(destEntry.at(
"probability").count(
"exp") == 1, storm::exceptions::InvalidJaniException,
1719 "Destination in edge from '" << sourceLoc <<
"' to '" << targetLoc <<
"' in automaton '" << name
1720 <<
"' must have a probability expression.");
1721 probExpr =
parseExpression(destEntry.at(
"probability").at(
"exp"), scope.
refine(
"probability expression in edge from '" + sourceLoc +
"' to '" +
1722 targetLoc +
"' in automaton '" + name +
"'"));
1726 "Probability expression " << probExpr <<
" does not have a numerical type.");
1728 std::vector<storm::jani::Assignment> assignments;
1729 unsigned assignmentDeclCount = destEntry.count(
"assignments");
1731 assignmentDeclCount < 2, storm::exceptions::InvalidJaniException,
1732 "Destination in edge from '" << sourceLoc <<
"' to '" << targetLoc <<
"' in automaton '" << name <<
"' has multiple assignment lists.");
1733 if (assignmentDeclCount > 0) {
1734 for (
auto const& assignmentEntry : destEntry.at(
"assignments")) {
1737 assignmentEntry.count(
"ref") == 1, storm::exceptions::InvalidJaniException,
1738 "Assignment in edge from '" << sourceLoc <<
"' to '" << targetLoc <<
"' in automaton '" << name <<
"' must have one ref field.");
1740 "' to '" + targetLoc +
"' in automaton '" + name +
"'"));
1743 assignmentEntry.count(
"value") == 1, storm::exceptions::InvalidJaniException,
1744 "Assignment in edge from '" << sourceLoc <<
"' to '" << targetLoc <<
"' in automaton '" << name <<
"' must have one value field.");
1747 scope.
refine(
"assignment in edge from '" + sourceLoc +
"' to '" + targetLoc +
"' in automaton '" + name +
"'"));
1750 int64_t assignmentIndex = 0;
1751 if (assignmentEntry.count(
"index") > 0) {
1752 assignmentIndex =
getSignedInt<ValueType>(assignmentEntry.at(
"index"),
"assignment index in edge from '" + sourceLoc +
"' to '" +
1753 targetLoc +
"' in automaton '" + name +
"'");
1755 assignments.emplace_back(lValue, assignmentExpr, assignmentIndex);
1758 destinationLocationsAndProbabilities.emplace_back(locIds.at(targetLoc), probExpr);
1762 rateExpr.
isInitialized() ? boost::optional<storm::expressions::Expression>(rateExpr) : boost::none, templateEdge,
1763 destinationLocationsAndProbabilities));
1769template<
typename ValueType>
1771 std::vector<storm::jani::SynchronizationVector> syncVectors;
1773 for (
auto const& syncEntry : syncVectorStructure) {
1774 std::vector<std::string> inputs;
1775 for (
auto const& syncInput : syncEntry.at(
"synchronise")) {
1776 if (syncInput.is_null()) {
1779 inputs.push_back(syncInput);
1782 std::string syncResult;
1783 if (syncEntry.count(
"result")) {
1784 syncResult = syncEntry.at(
"result");
1788 syncVectors.emplace_back(inputs, syncResult);
1793template<
typename ValueType>
1794std::shared_ptr<storm::jani::Composition> JaniParser<ValueType>::parseComposition(Json
const& compositionStructure) {
1795 if (compositionStructure.count(
"automaton")) {
1796 std::set<std::string> inputEnabledActions;
1797 if (compositionStructure.count(
"input-enable")) {
1798 for (
auto const& actionDecl : compositionStructure.at(
"input-enable")) {
1799 inputEnabledActions.insert(actionDecl.template get<std::string>());
1802 return std::shared_ptr<storm::jani::AutomatonComposition>(
1803 new storm::jani::AutomatonComposition(compositionStructure.at(
"automaton").template get<std::string>(), inputEnabledActions));
1806 STORM_LOG_THROW(compositionStructure.count(
"elements") == 1, storm::exceptions::InvalidJaniException,
1807 "Elements of a composition must be given, got " << compositionStructure.dump() <<
".");
1809 if (compositionStructure.at(
"elements").size() == 1 && compositionStructure.count(
"syncs") == 0) {
1811 STORM_LOG_THROW(compositionStructure.at(
"elements").back().count(
"automaton") == 1, storm::exceptions::InvalidJaniException,
1812 "Automaton must be given in composition.");
1813 if (compositionStructure.at(
"elements").back().at(
"automaton").is_string()) {
1814 std::string name = compositionStructure.at(
"elements").back().at(
"automaton");
1816 return std::shared_ptr<storm::jani::AutomatonComposition>(
new storm::jani::AutomatonComposition(name));
1818 STORM_LOG_THROW(
false, storm::exceptions::InvalidJaniException,
"Trivial nesting parallel composition is not yet supported.");
1821 std::vector<std::shared_ptr<storm::jani::Composition>> compositions;
1822 for (
auto const& elemDecl : compositionStructure.at(
"elements")) {
1823 if (!allowRecursion) {
1824 STORM_LOG_THROW(elemDecl.count(
"automaton") == 1, storm::exceptions::InvalidJaniException,
"Automaton must be given in the element.");
1826 compositions.push_back(parseComposition(elemDecl));
1829 STORM_LOG_THROW(compositionStructure.count(
"syncs") < 2, storm::exceptions::InvalidJaniException,
"Sync vectors can be given at most once.");
1830 std::vector<storm::jani::SynchronizationVector> syncVectors;
1831 if (compositionStructure.count(
"syncs") > 0) {
1835 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_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
Contains all file parsers and helper classes.
void insertLowerUpperTimeBounds(std::vector< std::optional< storm::logic::TimeBound > > &lowerBounds, std::vector< std::optional< storm::logic::TimeBound > > &upperBounds, storm::jani::PropertyInterval const &pi)
void ensureNumberOfArguments(uint64_t expected, uint64_t actual, std::string const &opstring, std::string const &errorInfo)
Helper for parse expression.
std::string getString(typename JaniParser< ValueType >::Json const &structure, std::string const &errorInfo)
std::vector< storm::jani::SynchronizationVector > parseSyncVectors(typename JaniParser< ValueType >::Json const &syncVectorStructure)
int64_t getSignedInt(typename JaniParser< ValueType >::Json const &structure, std::string const &errorInfo)
bool getBoolean(typename JaniParser< ValueType >::Json const &structure, std::string const &errorInfo)
void ensureArrayType(storm::expressions::Expression const &expr, std::string const &opstring, unsigned argNr, std::string const &errorInfo)
Helper for parse expression.
void ensureBooleanType(storm::expressions::Expression const &expr, std::string const &opstring, unsigned argNr, std::string const &errorInfo)
Helper for parse expression.
void ensureIntegerType(storm::expressions::Expression const &expr, std::string const &opstring, unsigned argNr, std::string const &errorInfo)
Helper for parse expression.
const std::string VARIABLE_AUTOMATON_DELIMITER
uint64_t getUnsignedInt(typename JaniParser< ValueType >::Json const &structure, std::string const &errorInfo)
void ensureNumericalType(storm::expressions::Expression const &expr, std::string const &opstring, unsigned argNr, std::string const &errorInfo)
Helper for parse expression.
bool isOne(ValueType const &a)
bool isZero(ValueType const &a)
TargetType convertNumber(SourceType const &number)
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