diff --git a/examples/dtmc/die/die.pm b/examples/dtmc/die/die.pm index dacde8a79..c5634bf4f 100644 --- a/examples/dtmc/die/die.pm +++ b/examples/dtmc/die/die.pm @@ -29,3 +29,4 @@ label "three" = s=7&d=3; label "four" = s=7&d=4; label "five" = s=7&d=5; label "six" = s=7&d=6; +label "end" = s=7; diff --git a/resources/3rdparty/CMakeLists.txt b/resources/3rdparty/CMakeLists.txt index 042eaea43..244e24057 100644 --- a/resources/3rdparty/CMakeLists.txt +++ b/resources/3rdparty/CMakeLists.txt @@ -204,7 +204,7 @@ if(USE_CARL) LOG_INSTALL ON ) - add_dependencies(resources xercesc) + add_dependencies(resources carl) include_directories(${STORM_3RDPARTY_BINARY_DIR}/carl/include) list(APPEND STORM_LINK_LIBRARIES ${STORM_3RDPARTY_BINARY_DIR}/carl/lib/libcarl${DYNAMIC_EXT}) set(STORM_HAVE_CARL ON) diff --git a/src/adapters/AddExpressionAdapter.cpp b/src/adapters/AddExpressionAdapter.cpp index e50620db8..f8a8f200e 100644 --- a/src/adapters/AddExpressionAdapter.cpp +++ b/src/adapters/AddExpressionAdapter.cpp @@ -19,37 +19,37 @@ namespace storm { template<storm::dd::DdType Type, typename ValueType> storm::dd::Add<Type, ValueType> AddExpressionAdapter<Type, ValueType>::translateExpression(storm::expressions::Expression const& expression) { if (expression.hasBooleanType()) { - return boost::any_cast<storm::dd::Bdd<Type>>(expression.accept(*this)).template toAdd<ValueType>(); + return boost::any_cast<storm::dd::Bdd<Type>>(expression.accept(*this, boost::none)).template toAdd<ValueType>(); } else { - return boost::any_cast<storm::dd::Add<Type, ValueType>>(expression.accept(*this)); + return boost::any_cast<storm::dd::Add<Type, ValueType>>(expression.accept(*this, boost::none)); } } template<storm::dd::DdType Type, typename ValueType> storm::dd::Bdd<Type> AddExpressionAdapter<Type, ValueType>::translateBooleanExpression(storm::expressions::Expression const& expression) { STORM_LOG_THROW(expression.hasBooleanType(), storm::exceptions::InvalidArgumentException, "Expected expression of boolean type."); - return boost::any_cast<storm::dd::Bdd<Type>>(expression.accept(*this)); + return boost::any_cast<storm::dd::Bdd<Type>>(expression.accept(*this, boost::none)); } template<storm::dd::DdType Type, typename ValueType> - boost::any AddExpressionAdapter<Type, ValueType>::visit(storm::expressions::IfThenElseExpression const& expression) { + boost::any AddExpressionAdapter<Type, ValueType>::visit(storm::expressions::IfThenElseExpression const& expression, boost::any const& data) { if (expression.hasBooleanType()) { - storm::dd::Bdd<Type> elseDd = boost::any_cast<storm::dd::Bdd<Type>>(expression.getElseExpression()->accept(*this)); - storm::dd::Bdd<Type> thenDd = boost::any_cast<storm::dd::Bdd<Type>>(expression.getThenExpression()->accept(*this)); - storm::dd::Bdd<Type> conditionDd = boost::any_cast<storm::dd::Bdd<Type>>(expression.getCondition()->accept(*this)); + storm::dd::Bdd<Type> elseDd = boost::any_cast<storm::dd::Bdd<Type>>(expression.getElseExpression()->accept(*this, data)); + storm::dd::Bdd<Type> thenDd = boost::any_cast<storm::dd::Bdd<Type>>(expression.getThenExpression()->accept(*this, data)); + storm::dd::Bdd<Type> conditionDd = boost::any_cast<storm::dd::Bdd<Type>>(expression.getCondition()->accept(*this, data)); return conditionDd.ite(thenDd, elseDd); } else { - storm::dd::Add<Type, ValueType> elseDd = boost::any_cast<storm::dd::Add<Type, ValueType>>(expression.getElseExpression()->accept(*this)); - storm::dd::Add<Type, ValueType> thenDd = boost::any_cast<storm::dd::Add<Type, ValueType>>(expression.getThenExpression()->accept(*this)); - storm::dd::Bdd<Type> conditionDd = boost::any_cast<storm::dd::Bdd<Type>>(expression.getCondition()->accept(*this)); + storm::dd::Add<Type, ValueType> elseDd = boost::any_cast<storm::dd::Add<Type, ValueType>>(expression.getElseExpression()->accept(*this, data)); + storm::dd::Add<Type, ValueType> thenDd = boost::any_cast<storm::dd::Add<Type, ValueType>>(expression.getThenExpression()->accept(*this, data)); + storm::dd::Bdd<Type> conditionDd = boost::any_cast<storm::dd::Bdd<Type>>(expression.getCondition()->accept(*this, data)); return conditionDd.ite(thenDd, elseDd); } } template<storm::dd::DdType Type, typename ValueType> - boost::any AddExpressionAdapter<Type, ValueType>::visit(storm::expressions::BinaryBooleanFunctionExpression const& expression) { - storm::dd::Bdd<Type> leftResult = boost::any_cast<storm::dd::Bdd<Type>>(expression.getFirstOperand()->accept(*this)); - storm::dd::Bdd<Type> rightResult = boost::any_cast<storm::dd::Bdd<Type>>(expression.getSecondOperand()->accept(*this)); + boost::any AddExpressionAdapter<Type, ValueType>::visit(storm::expressions::BinaryBooleanFunctionExpression const& expression, boost::any const& data) { + storm::dd::Bdd<Type> leftResult = boost::any_cast<storm::dd::Bdd<Type>>(expression.getFirstOperand()->accept(*this, data)); + storm::dd::Bdd<Type> rightResult = boost::any_cast<storm::dd::Bdd<Type>>(expression.getSecondOperand()->accept(*this, data)); storm::dd::Bdd<Type> result; switch (expression.getOperatorType()) { @@ -74,9 +74,9 @@ namespace storm { } template<storm::dd::DdType Type, typename ValueType> - boost::any AddExpressionAdapter<Type, ValueType>::visit(storm::expressions::BinaryNumericalFunctionExpression const& expression) { - storm::dd::Add<Type, ValueType> leftResult = boost::any_cast<storm::dd::Add<Type, ValueType>>(expression.getFirstOperand()->accept(*this)); - storm::dd::Add<Type, ValueType> rightResult = boost::any_cast<storm::dd::Add<Type, ValueType>>(expression.getSecondOperand()->accept(*this)); + boost::any AddExpressionAdapter<Type, ValueType>::visit(storm::expressions::BinaryNumericalFunctionExpression const& expression, boost::any const& data) { + storm::dd::Add<Type, ValueType> leftResult = boost::any_cast<storm::dd::Add<Type, ValueType>>(expression.getFirstOperand()->accept(*this, data)); + storm::dd::Add<Type, ValueType> rightResult = boost::any_cast<storm::dd::Add<Type, ValueType>>(expression.getSecondOperand()->accept(*this, data)); storm::dd::Add<Type, ValueType> result; switch (expression.getOperatorType()) { @@ -109,9 +109,9 @@ namespace storm { } template<storm::dd::DdType Type, typename ValueType> - boost::any AddExpressionAdapter<Type, ValueType>::visit(storm::expressions::BinaryRelationExpression const& expression) { - storm::dd::Add<Type, ValueType> leftResult = boost::any_cast<storm::dd::Add<Type, ValueType>>(expression.getFirstOperand()->accept(*this)); - storm::dd::Add<Type, ValueType> rightResult = boost::any_cast<storm::dd::Add<Type, ValueType>>(expression.getSecondOperand()->accept(*this)); + boost::any AddExpressionAdapter<Type, ValueType>::visit(storm::expressions::BinaryRelationExpression const& expression, boost::any const& data) { + storm::dd::Add<Type, ValueType> leftResult = boost::any_cast<storm::dd::Add<Type, ValueType>>(expression.getFirstOperand()->accept(*this, data)); + storm::dd::Add<Type, ValueType> rightResult = boost::any_cast<storm::dd::Add<Type, ValueType>>(expression.getSecondOperand()->accept(*this, data)); storm::dd::Bdd<Type> result; switch (expression.getRelationType()) { @@ -139,7 +139,7 @@ namespace storm { } template<storm::dd::DdType Type, typename ValueType> - boost::any AddExpressionAdapter<Type, ValueType>::visit(storm::expressions::VariableExpression const& expression) { + boost::any AddExpressionAdapter<Type, ValueType>::visit(storm::expressions::VariableExpression const& expression, boost::any const& data) { auto const& variablePair = variableMapping->find(expression.getVariable()); STORM_LOG_THROW(variablePair != variableMapping->end(), storm::exceptions::InvalidArgumentException, "Cannot translate the given expression, because it contains the variable '" << expression.getVariableName() << "' for which no DD counterpart is known."); if (expression.hasBooleanType()) { @@ -150,8 +150,8 @@ namespace storm { } template<storm::dd::DdType Type, typename ValueType> - boost::any AddExpressionAdapter<Type, ValueType>::visit(storm::expressions::UnaryBooleanFunctionExpression const& expression) { - storm::dd::Bdd<Type> result = boost::any_cast<storm::dd::Bdd<Type>>(expression.getOperand()->accept(*this)); + boost::any AddExpressionAdapter<Type, ValueType>::visit(storm::expressions::UnaryBooleanFunctionExpression const& expression, boost::any const& data) { + storm::dd::Bdd<Type> result = boost::any_cast<storm::dd::Bdd<Type>>(expression.getOperand()->accept(*this, data)); switch (expression.getOperatorType()) { case storm::expressions::UnaryBooleanFunctionExpression::OperatorType::Not: @@ -163,8 +163,8 @@ namespace storm { } template<storm::dd::DdType Type, typename ValueType> - boost::any AddExpressionAdapter<Type, ValueType>::visit(storm::expressions::UnaryNumericalFunctionExpression const& expression) { - storm::dd::Add<Type, ValueType> result = boost::any_cast<storm::dd::Add<Type, ValueType>>(expression.getOperand()->accept(*this)); + boost::any AddExpressionAdapter<Type, ValueType>::visit(storm::expressions::UnaryNumericalFunctionExpression const& expression, boost::any const& data) { + storm::dd::Add<Type, ValueType> result = boost::any_cast<storm::dd::Add<Type, ValueType>>(expression.getOperand()->accept(*this, data)); switch (expression.getOperatorType()) { case storm::expressions::UnaryNumericalFunctionExpression::OperatorType::Minus: @@ -184,18 +184,18 @@ namespace storm { } template<storm::dd::DdType Type, typename ValueType> - boost::any AddExpressionAdapter<Type, ValueType>::visit(storm::expressions::BooleanLiteralExpression const& expression) { + boost::any AddExpressionAdapter<Type, ValueType>::visit(storm::expressions::BooleanLiteralExpression const& expression, boost::any const& data) { return expression.getValue() ? ddManager->getBddOne() : ddManager->getBddZero(); } template<storm::dd::DdType Type, typename ValueType> - boost::any AddExpressionAdapter<Type, ValueType>::visit(storm::expressions::IntegerLiteralExpression const& expression) { + boost::any AddExpressionAdapter<Type, ValueType>::visit(storm::expressions::IntegerLiteralExpression const& expression, boost::any const& data) { return ddManager->getConstant(static_cast<ValueType>(expression.getValue())); } template<storm::dd::DdType Type, typename ValueType> - boost::any AddExpressionAdapter<Type, ValueType>::visit(storm::expressions::DoubleLiteralExpression const& expression) { - return ddManager->getConstant(static_cast<ValueType>(expression.getValue())); + boost::any AddExpressionAdapter<Type, ValueType>::visit(storm::expressions::RationalLiteralExpression const& expression, boost::any const& data) { + return ddManager->getConstant(static_cast<ValueType>(expression.getValueAsDouble())); } // Explicitly instantiate the symbolic expression adapter diff --git a/src/adapters/AddExpressionAdapter.h b/src/adapters/AddExpressionAdapter.h index 89d825d96..cac29156f 100644 --- a/src/adapters/AddExpressionAdapter.h +++ b/src/adapters/AddExpressionAdapter.h @@ -22,16 +22,16 @@ namespace storm { storm::dd::Add<Type, ValueType> translateExpression(storm::expressions::Expression const& expression); storm::dd::Bdd<Type> translateBooleanExpression(storm::expressions::Expression const& expression); - virtual boost::any visit(storm::expressions::IfThenElseExpression const& expression) override; - virtual boost::any visit(storm::expressions::BinaryBooleanFunctionExpression const& expression) override; - virtual boost::any visit(storm::expressions::BinaryNumericalFunctionExpression const& expression) override; - virtual boost::any visit(storm::expressions::BinaryRelationExpression const& expression) override; - virtual boost::any visit(storm::expressions::VariableExpression const& expression) override; - virtual boost::any visit(storm::expressions::UnaryBooleanFunctionExpression const& expression) override; - virtual boost::any visit(storm::expressions::UnaryNumericalFunctionExpression const& expression) override; - virtual boost::any visit(storm::expressions::BooleanLiteralExpression const& expression) override; - virtual boost::any visit(storm::expressions::IntegerLiteralExpression const& expression) override; - virtual boost::any visit(storm::expressions::DoubleLiteralExpression const& expression) override; + virtual boost::any visit(storm::expressions::IfThenElseExpression const& expression, boost::any const& data) override; + virtual boost::any visit(storm::expressions::BinaryBooleanFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(storm::expressions::BinaryNumericalFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(storm::expressions::BinaryRelationExpression const& expression, boost::any const& data) override; + virtual boost::any visit(storm::expressions::VariableExpression const& expression, boost::any const& data) override; + virtual boost::any visit(storm::expressions::UnaryBooleanFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(storm::expressions::UnaryNumericalFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(storm::expressions::BooleanLiteralExpression const& expression, boost::any const& data) override; + virtual boost::any visit(storm::expressions::IntegerLiteralExpression const& expression, boost::any const& data) override; + virtual boost::any visit(storm::expressions::RationalLiteralExpression const& expression, boost::any const& data) override; private: // The manager responsible for the DDs built by this adapter. diff --git a/src/adapters/DereferenceIteratorAdapter.h b/src/adapters/DereferenceIteratorAdapter.h new file mode 100644 index 000000000..16ac0834e --- /dev/null +++ b/src/adapters/DereferenceIteratorAdapter.h @@ -0,0 +1,46 @@ +#pragma once + +#include <type_traits> + +#include <boost/iterator/transform_iterator.hpp> + +namespace storm { + namespace adapters { + + template<typename T> + struct Dereferencer { + decltype((*std::declval<T>())) operator()(T const& t) const { + return *t; + } + }; + + template<typename ContainerType> + class DereferenceIteratorAdapter { + public: + typedef typename ContainerType::value_type value_type; + typedef typename std::conditional<std::is_const<ContainerType>::value, typename ContainerType::const_iterator, typename ContainerType::iterator>::type input_iterator; + typedef typename boost::transform_iterator<Dereferencer<value_type>, input_iterator> iterator; + + DereferenceIteratorAdapter(input_iterator it, input_iterator ite) : it(it), ite(ite) { + // Intentionally left empty. + } + + iterator begin() const { + return boost::make_transform_iterator(it, Dereferencer<value_type>()); + } + + iterator end() const { + return boost::make_transform_iterator(ite, Dereferencer<value_type>()); + } + + static iterator make_iterator(input_iterator it) { + return boost::make_transform_iterator(it, Dereferencer<value_type>()); + } + + private: + input_iterator it; + input_iterator ite; + }; + + } +} \ No newline at end of file diff --git a/src/adapters/MathsatExpressionAdapter.h b/src/adapters/MathsatExpressionAdapter.h index 9fec0cf15..8fad6a8af 100644 --- a/src/adapters/MathsatExpressionAdapter.h +++ b/src/adapters/MathsatExpressionAdapter.h @@ -57,7 +57,7 @@ namespace storm { * @return An equivalent term for MathSAT. */ msat_term translateExpression(storm::expressions::Expression const& expression) { - msat_term result = boost::any_cast<msat_term>(expression.getBaseExpression().accept(*this)); + msat_term result = boost::any_cast<msat_term>(expression.getBaseExpression().accept(*this, boost::none)); STORM_LOG_THROW(!MSAT_ERROR_TERM(result), storm::exceptions::ExpressionEvaluationException, "Could not translate expression to MathSAT's format."); return result; } @@ -90,9 +90,9 @@ namespace storm { return declarationVariablePair->second; } - virtual boost::any visit(expressions::BinaryBooleanFunctionExpression const& expression) override { - msat_term leftResult = boost::any_cast<msat_term>(expression.getFirstOperand()->accept(*this)); - msat_term rightResult = boost::any_cast<msat_term>(expression.getSecondOperand()->accept(*this)); + virtual boost::any visit(storm::expressions::BinaryBooleanFunctionExpression const& expression, boost::any const& data) override { + msat_term leftResult = boost::any_cast<msat_term>(expression.getFirstOperand()->accept(*this, data)); + msat_term rightResult = boost::any_cast<msat_term>(expression.getSecondOperand()->accept(*this, data)); switch (expression.getOperatorType()) { case storm::expressions::BinaryBooleanFunctionExpression::OperatorType::And: @@ -108,9 +108,9 @@ namespace storm { } } - virtual boost::any visit(expressions::BinaryNumericalFunctionExpression const& expression) override { - msat_term leftResult = boost::any_cast<msat_term>(expression.getFirstOperand()->accept(*this)); - msat_term rightResult = boost::any_cast<msat_term>(expression.getSecondOperand()->accept(*this)); + virtual boost::any visit(storm::expressions::BinaryNumericalFunctionExpression const& expression, boost::any const& data) override { + msat_term leftResult = boost::any_cast<msat_term>(expression.getFirstOperand()->accept(*this, data)); + msat_term rightResult = boost::any_cast<msat_term>(expression.getSecondOperand()->accept(*this, data)); switch (expression.getOperatorType()) { case storm::expressions::BinaryNumericalFunctionExpression::OperatorType::Plus: @@ -130,9 +130,9 @@ namespace storm { } } - virtual boost::any visit(expressions::BinaryRelationExpression const& expression) override { - msat_term leftResult = boost::any_cast<msat_term>(expression.getFirstOperand()->accept(*this)); - msat_term rightResult = boost::any_cast<msat_term>(expression.getSecondOperand()->accept(*this)); + virtual boost::any visit(storm::expressions::BinaryRelationExpression const& expression, boost::any const& data) override { + msat_term leftResult = boost::any_cast<msat_term>(expression.getFirstOperand()->accept(*this, data)); + msat_term rightResult = boost::any_cast<msat_term>(expression.getSecondOperand()->accept(*this, data)); switch (expression.getRelationType()) { case storm::expressions::BinaryRelationExpression::RelationType::Equal: @@ -160,27 +160,27 @@ namespace storm { } } - virtual boost::any visit(storm::expressions::IfThenElseExpression const& expression) override { - msat_term conditionResult = boost::any_cast<msat_term>(expression.getCondition()->accept(*this)); - msat_term thenResult = boost::any_cast<msat_term>(expression.getThenExpression()->accept(*this)); - msat_term elseResult = boost::any_cast<msat_term>(expression.getElseExpression()->accept(*this)); + virtual boost::any visit(storm::expressions::IfThenElseExpression const& expression, boost::any const& data) override { + msat_term conditionResult = boost::any_cast<msat_term>(expression.getCondition()->accept(*this, data)); + msat_term thenResult = boost::any_cast<msat_term>(expression.getThenExpression()->accept(*this, data)); + msat_term elseResult = boost::any_cast<msat_term>(expression.getElseExpression()->accept(*this, data)); return msat_make_term_ite(env, conditionResult, thenResult, elseResult); } - virtual boost::any visit(expressions::BooleanLiteralExpression const& expression) override { + virtual boost::any visit(storm::expressions::BooleanLiteralExpression const& expression, boost::any const& data) override { return expression.getValue() ? msat_make_true(env) : msat_make_false(env); } - virtual boost::any visit(expressions::DoubleLiteralExpression const& expression) override { - return msat_make_number(env, std::to_string(expression.getValue()).c_str()); + virtual boost::any visit(storm::expressions::RationalLiteralExpression const& expression, boost::any const& data) override { + return msat_make_number(env, std::to_string(expression.getValueAsDouble()).c_str()); } - virtual boost::any visit(expressions::IntegerLiteralExpression const& expression) override { + virtual boost::any visit(storm::expressions::IntegerLiteralExpression const& expression, boost::any const& data) override { return msat_make_number(env, std::to_string(static_cast<int>(expression.getValue())).c_str()); } - virtual boost::any visit(expressions::UnaryBooleanFunctionExpression const& expression) override { - msat_term childResult = boost::any_cast<msat_term>(expression.getOperand()->accept(*this)); + virtual boost::any visit(storm::expressions::UnaryBooleanFunctionExpression const& expression, boost::any const& data) override { + msat_term childResult = boost::any_cast<msat_term>(expression.getOperand()->accept(*this, data)); switch (expression.getOperatorType()) { case storm::expressions::UnaryBooleanFunctionExpression::OperatorType::Not: @@ -191,8 +191,8 @@ namespace storm { } } - virtual boost::any visit(expressions::UnaryNumericalFunctionExpression const& expression) override { - msat_term childResult = boost::any_cast<msat_term>(expression.getOperand()->accept(*this)); + virtual boost::any visit(storm::expressions::UnaryNumericalFunctionExpression const& expression, boost::any const& data) override { + msat_term childResult = boost::any_cast<msat_term>(expression.getOperand()->accept(*this, data)); switch (expression.getOperatorType()) { case storm::expressions::UnaryNumericalFunctionExpression::OperatorType::Minus: @@ -209,7 +209,7 @@ namespace storm { } } - virtual boost::any visit(expressions::VariableExpression const& expression) override { + virtual boost::any visit(storm::expressions::VariableExpression const& expression, boost::any const& data) override { return translateExpression(expression.getVariable()); } diff --git a/src/adapters/Z3ExpressionAdapter.cpp b/src/adapters/Z3ExpressionAdapter.cpp index 60b4e8e1c..8258f42f5 100644 --- a/src/adapters/Z3ExpressionAdapter.cpp +++ b/src/adapters/Z3ExpressionAdapter.cpp @@ -16,7 +16,7 @@ namespace storm { z3::expr Z3ExpressionAdapter::translateExpression(storm::expressions::Expression const& expression) { STORM_LOG_ASSERT(expression.getManager() == this->manager, "Invalid expression for solver."); - z3::expr result = boost::any_cast<z3::expr>(expression.getBaseExpression().accept(*this)); + z3::expr result = boost::any_cast<z3::expr>(expression.getBaseExpression().accept(*this, boost::none)); for (z3::expr const& assertion : additionalAssertions) { result = result && assertion; @@ -139,9 +139,9 @@ namespace storm { } } - boost::any Z3ExpressionAdapter::visit(storm::expressions::BinaryBooleanFunctionExpression const& expression) { - z3::expr leftResult = boost::any_cast<z3::expr>(expression.getFirstOperand()->accept(*this)); - z3::expr rightResult = boost::any_cast<z3::expr>(expression.getSecondOperand()->accept(*this)); + boost::any Z3ExpressionAdapter::visit(storm::expressions::BinaryBooleanFunctionExpression const& expression, boost::any const& data) { + z3::expr leftResult = boost::any_cast<z3::expr>(expression.getFirstOperand()->accept(*this, data)); + z3::expr rightResult = boost::any_cast<z3::expr>(expression.getSecondOperand()->accept(*this, data)); switch(expression.getOperatorType()) { case storm::expressions::BinaryBooleanFunctionExpression::OperatorType::And: @@ -160,9 +160,9 @@ namespace storm { } - boost::any Z3ExpressionAdapter::visit(storm::expressions::BinaryNumericalFunctionExpression const& expression) { - z3::expr leftResult = boost::any_cast<z3::expr>(expression.getFirstOperand()->accept(*this)); - z3::expr rightResult = boost::any_cast<z3::expr>(expression.getSecondOperand()->accept(*this)); + boost::any Z3ExpressionAdapter::visit(storm::expressions::BinaryNumericalFunctionExpression const& expression, boost::any const& data) { + z3::expr leftResult = boost::any_cast<z3::expr>(expression.getFirstOperand()->accept(*this, data)); + z3::expr rightResult = boost::any_cast<z3::expr>(expression.getSecondOperand()->accept(*this, data)); switch(expression.getOperatorType()) { case storm::expressions::BinaryNumericalFunctionExpression::OperatorType::Plus: @@ -177,14 +177,16 @@ namespace storm { return ite(leftResult <= rightResult, leftResult, rightResult); case storm::expressions::BinaryNumericalFunctionExpression::OperatorType::Max: return ite(leftResult >= rightResult, leftResult, rightResult); + case storm::expressions::BinaryNumericalFunctionExpression::OperatorType::Power: + return pw(leftResult,rightResult); default: STORM_LOG_THROW(false, storm::exceptions::ExpressionEvaluationException, "Cannot evaluate expression: unknown numerical binary operator '" << static_cast<int>(expression.getOperatorType()) << "' in expression " << expression << "."); } } - boost::any Z3ExpressionAdapter::visit(storm::expressions::BinaryRelationExpression const& expression) { - z3::expr leftResult = boost::any_cast<z3::expr>(expression.getFirstOperand()->accept(*this)); - z3::expr rightResult = boost::any_cast<z3::expr>(expression.getSecondOperand()->accept(*this)); + boost::any Z3ExpressionAdapter::visit(storm::expressions::BinaryRelationExpression const& expression, boost::any const& data) { + z3::expr leftResult = boost::any_cast<z3::expr>(expression.getFirstOperand()->accept(*this, data)); + z3::expr rightResult = boost::any_cast<z3::expr>(expression.getSecondOperand()->accept(*this, data)); switch(expression.getRelationType()) { case storm::expressions::BinaryRelationExpression::RelationType::Equal: @@ -204,22 +206,22 @@ namespace storm { } } - boost::any Z3ExpressionAdapter::visit(storm::expressions::BooleanLiteralExpression const& expression) { + boost::any Z3ExpressionAdapter::visit(storm::expressions::BooleanLiteralExpression const& expression, boost::any const& data) { return context.bool_val(expression.getValue()); } - boost::any Z3ExpressionAdapter::visit(storm::expressions::DoubleLiteralExpression const& expression) { + boost::any Z3ExpressionAdapter::visit(storm::expressions::RationalLiteralExpression const& expression, boost::any const& data) { std::stringstream fractionStream; fractionStream << expression.getValue(); return context.real_val(fractionStream.str().c_str()); } - boost::any Z3ExpressionAdapter::visit(storm::expressions::IntegerLiteralExpression const& expression) { + boost::any Z3ExpressionAdapter::visit(storm::expressions::IntegerLiteralExpression const& expression, boost::any const& data) { return context.int_val(static_cast<int>(expression.getValue())); } - boost::any Z3ExpressionAdapter::visit(storm::expressions::UnaryBooleanFunctionExpression const& expression) { - z3::expr childResult = boost::any_cast<z3::expr>(expression.getOperand()->accept(*this)); + boost::any Z3ExpressionAdapter::visit(storm::expressions::UnaryBooleanFunctionExpression const& expression, boost::any const& data) { + z3::expr childResult = boost::any_cast<z3::expr>(expression.getOperand()->accept(*this, data)); switch (expression.getOperatorType()) { case storm::expressions::UnaryBooleanFunctionExpression::OperatorType::Not: @@ -229,8 +231,8 @@ namespace storm { } } - boost::any Z3ExpressionAdapter::visit(storm::expressions::UnaryNumericalFunctionExpression const& expression) { - z3::expr childResult = boost::any_cast<z3::expr>(expression.getOperand()->accept(*this)); + boost::any Z3ExpressionAdapter::visit(storm::expressions::UnaryNumericalFunctionExpression const& expression, boost::any const& data) { + z3::expr childResult = boost::any_cast<z3::expr>(expression.getOperand()->accept(*this, data)); switch(expression.getOperatorType()) { case storm::expressions::UnaryNumericalFunctionExpression::OperatorType::Minus: @@ -251,14 +253,14 @@ namespace storm { } } - boost::any Z3ExpressionAdapter::visit(storm::expressions::IfThenElseExpression const& expression) { - z3::expr conditionResult = boost::any_cast<z3::expr>(expression.getCondition()->accept(*this)); - z3::expr thenResult = boost::any_cast<z3::expr>(expression.getThenExpression()->accept(*this)); - z3::expr elseResult = boost::any_cast<z3::expr>(expression.getElseExpression()->accept(*this)); + boost::any Z3ExpressionAdapter::visit(storm::expressions::IfThenElseExpression const& expression, boost::any const& data) { + z3::expr conditionResult = boost::any_cast<z3::expr>(expression.getCondition()->accept(*this, data)); + z3::expr thenResult = boost::any_cast<z3::expr>(expression.getThenExpression()->accept(*this, data)); + z3::expr elseResult = boost::any_cast<z3::expr>(expression.getElseExpression()->accept(*this, data)); return z3::expr(context, Z3_mk_ite(context, conditionResult, thenResult, elseResult)); } - boost::any Z3ExpressionAdapter::visit(storm::expressions::VariableExpression const& expression) { + boost::any Z3ExpressionAdapter::visit(storm::expressions::VariableExpression const& expression, boost::any const& data) { return this->translateExpression(expression.getVariable()); } diff --git a/src/adapters/Z3ExpressionAdapter.h b/src/adapters/Z3ExpressionAdapter.h index 4fc60c8a5..70cfac18b 100644 --- a/src/adapters/Z3ExpressionAdapter.h +++ b/src/adapters/Z3ExpressionAdapter.h @@ -55,25 +55,25 @@ namespace storm { */ storm::expressions::Variable const& getVariable(z3::func_decl z3Declaration); - virtual boost::any visit(storm::expressions::BinaryBooleanFunctionExpression const& expression) override; + virtual boost::any visit(storm::expressions::BinaryBooleanFunctionExpression const& expression, boost::any const& data) override; - virtual boost::any visit(storm::expressions::BinaryNumericalFunctionExpression const& expression) override; + virtual boost::any visit(storm::expressions::BinaryNumericalFunctionExpression const& expression, boost::any const& data) override; - virtual boost::any visit(storm::expressions::BinaryRelationExpression const& expression) override; + virtual boost::any visit(storm::expressions::BinaryRelationExpression const& expression, boost::any const& data) override; - virtual boost::any visit(storm::expressions::BooleanLiteralExpression const& expression) override; + virtual boost::any visit(storm::expressions::BooleanLiteralExpression const& expression, boost::any const& data) override; - virtual boost::any visit(storm::expressions::DoubleLiteralExpression const& expression) override; + virtual boost::any visit(storm::expressions::RationalLiteralExpression const& expression, boost::any const& data) override; - virtual boost::any visit(storm::expressions::IntegerLiteralExpression const& expression) override; + virtual boost::any visit(storm::expressions::IntegerLiteralExpression const& expression, boost::any const& data) override; - virtual boost::any visit(storm::expressions::UnaryBooleanFunctionExpression const& expression) override; + virtual boost::any visit(storm::expressions::UnaryBooleanFunctionExpression const& expression, boost::any const& data) override; - virtual boost::any visit(storm::expressions::UnaryNumericalFunctionExpression const& expression) override; + virtual boost::any visit(storm::expressions::UnaryNumericalFunctionExpression const& expression, boost::any const& data) override; - virtual boost::any visit(storm::expressions::IfThenElseExpression const& expression) override; + virtual boost::any visit(storm::expressions::IfThenElseExpression const& expression, boost::any const& data) override; - virtual boost::any visit(storm::expressions::VariableExpression const& expression) override; + virtual boost::any visit(storm::expressions::VariableExpression const& expression, boost::any const& data) override; private: /*! diff --git a/src/builder/DdJaniModelBuilder.cpp b/src/builder/DdJaniModelBuilder.cpp index 9397ed925..5ab18683b 100644 --- a/src/builder/DdJaniModelBuilder.cpp +++ b/src/builder/DdJaniModelBuilder.cpp @@ -103,19 +103,13 @@ namespace storm { } template <storm::dd::DdType Type, typename ValueType> - DdJaniModelBuilder<Type, ValueType>::DdJaniModelBuilder(storm::jani::Model const& model, Options const& options) : model(model), options(options) { - if (this->model->hasUndefinedConstants()) { - std::vector<std::reference_wrapper<storm::jani::Constant const>> undefinedConstants = this->model->getUndefinedConstants(); - std::vector<std::string> strings; - for (auto const& constant : undefinedConstants) { - std::stringstream stream; - stream << constant.get().getName() << " (" << constant.get().getType() << ")"; - strings.push_back(stream.str()); - } - STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Program still contains these undefined constants: " << boost::join(strings, ", ") << "."); - } - - this->model = this->model->substituteConstants(); + std::set<std::string> const& DdJaniModelBuilder<Type, ValueType>::Options::getRewardModelNames() const { + return rewardModelsToBuild; + } + + template <storm::dd::DdType Type, typename ValueType> + bool DdJaniModelBuilder<Type, ValueType>::Options::isBuildAllRewardModelsSet() const { + return buildAllRewardModels; } template <storm::dd::DdType Type, typename ValueType> @@ -188,11 +182,15 @@ namespace storm { this->model.getSystemComposition().accept(*this, boost::none); STORM_LOG_THROW(automata.size() == this->model.getNumberOfAutomata(), storm::exceptions::InvalidArgumentException, "Cannot build symbolic model from JANI model whose system composition refers to a subset of automata."); - // Then, check that the model does not contain unbounded integer variables. + // Then, check that the model does not contain unbounded integer or non-transient real variables. STORM_LOG_THROW(!this->model.getGlobalVariables().containsUnboundedIntegerVariables(), storm::exceptions::InvalidArgumentException, "Cannot build symbolic model from JANI model that contains global unbounded integer variables."); for (auto const& automaton : this->model.getAutomata()) { STORM_LOG_THROW(!automaton.getVariables().containsUnboundedIntegerVariables(), storm::exceptions::InvalidArgumentException, "Cannot build symbolic model from JANI model that contains unbounded integer variables in automaton '" << automaton.getName() << "'."); } + STORM_LOG_THROW(!this->model.getGlobalVariables().containsNonTransientRealVariables(), storm::exceptions::InvalidArgumentException, "Cannot build symbolic model from JANI model that contains global non-transient real variables."); + for (auto const& automaton : this->model.getAutomata()) { + STORM_LOG_THROW(!automaton.getVariables().containsNonTransientRealVariables(), storm::exceptions::InvalidArgumentException, "Cannot build symbolic model from JANI model that contains non-transient real variables in automaton '" << automaton.getName() << "'."); + } // Based on this assumption, we create the variables. return createVariables(); @@ -243,6 +241,7 @@ namespace storm { // Start by creating a meta variable for the location of the automaton. std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair = result.manager->addMetaVariable("l_" + automaton.getName(), 0, automaton.getNumberOfLocations() - 1); result.automatonToLocationVariableMap[automaton.getName()] = variablePair; + result.rowColumnMetaVariablePairs.push_back(variablePair); // Add the location variable to the row/column variables. result.rowMetaVariables.insert(variablePair.first); @@ -256,6 +255,11 @@ namespace storm { // Create global variables. storm::dd::Bdd<Type> globalVariableRanges = result.manager->getBddOne(); for (auto const& variable : this->model.getGlobalVariables()) { + // Only create the variable if it's non-transient. + if (variable.isTransient()) { + continue; + } + createVariable(variable, result); globalVariableRanges &= result.manager->getRange(result.variableToRowMetaVariableMap->at(variable.getExpressionVariable())); } @@ -274,6 +278,11 @@ namespace storm { // Then create variables for the variables of the automaton. for (auto const& variable : automaton.getVariables()) { + // Only create the variable if it's non-transient. + if (variable.isTransient()) { + continue; + } + createVariable(variable, result); identity &= result.variableToIdentityMap.at(variable.getExpressionVariable()).toBdd(); range &= result.manager->getRange(result.variableToRowMetaVariableMap->at(variable.getExpressionVariable())); @@ -345,11 +354,13 @@ namespace storm { template <storm::dd::DdType Type, typename ValueType> struct ComposerResult { - ComposerResult(storm::dd::Add<Type, ValueType> const& transitions, storm::dd::Bdd<Type> const& illegalFragment, uint64_t numberOfNondeterminismVariables = 0) : transitions(transitions), illegalFragment(illegalFragment), numberOfNondeterminismVariables(numberOfNondeterminismVariables) { + ComposerResult(storm::dd::Add<Type, ValueType> const& transitions, std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> const& transientLocationAssignments, std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> const& transientEdgeAssignments, storm::dd::Bdd<Type> const& illegalFragment, uint64_t numberOfNondeterminismVariables = 0) : transitions(transitions), transientLocationAssignments(transientLocationAssignments), transientEdgeAssignments(transientEdgeAssignments), illegalFragment(illegalFragment), numberOfNondeterminismVariables(numberOfNondeterminismVariables) { // Intentionally left empty. } storm::dd::Add<Type, ValueType> transitions; + std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientLocationAssignments; + std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientEdgeAssignments; storm::dd::Bdd<Type> illegalFragment; uint64_t numberOfNondeterminismVariables; }; @@ -358,7 +369,7 @@ namespace storm { template <storm::dd::DdType Type, typename ValueType> class SystemComposer : public storm::jani::CompositionVisitor { public: - SystemComposer(storm::jani::Model const& model, CompositionVariables<Type, ValueType> const& variables) : model(model), variables(variables) { + SystemComposer(storm::jani::Model const& model, CompositionVariables<Type, ValueType> const& variables, std::vector<storm::expressions::Variable> const& transientVariables) : model(model), variables(variables), transientVariables(transientVariables) { // Intentionally left empty. } @@ -370,6 +381,9 @@ namespace storm { // The variable to use when building an automaton. CompositionVariables<Type, ValueType> const& variables; + + // The transient variables to consider during system composition. + std::vector<storm::expressions::Variable> transientVariables; }; // This structure represents an edge destination. @@ -391,7 +405,7 @@ namespace storm { // Iterate over all assignments (boolean and integer) and build the DD for it. std::set<storm::expressions::Variable> assignedVariables; - for (auto const& assignment : destination.getAssignments()) { + for (auto const& assignment : destination.getNonTransientAssignments()) { // Record the variable as being written. STORM_LOG_TRACE("Assigning to variable " << variables.variableToRowMetaVariableMap->at(assignment.getExpressionVariable()).getName()); assignedVariables.insert(assignment.getExpressionVariable()); @@ -908,7 +922,7 @@ namespace storm { public: // This structure represents an edge. struct EdgeDd { - EdgeDd(storm::dd::Add<Type> const& guard = storm::dd::Add<Type>(), storm::dd::Add<Type, ValueType> const& transitions = storm::dd::Add<Type, ValueType>(), std::set<storm::expressions::Variable> const& writtenGlobalVariables = {}) : guard(guard), transitions(transitions), writtenGlobalVariables(writtenGlobalVariables) { + EdgeDd(storm::dd::Add<Type> const& guard = storm::dd::Add<Type>(), storm::dd::Add<Type, ValueType> const& transitions = storm::dd::Add<Type, ValueType>(), std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> const& transientEdgeAssignments = {}, std::set<storm::expressions::Variable> const& writtenGlobalVariables = {}) : guard(guard), transitions(transitions), transientEdgeAssignments(transientEdgeAssignments), writtenGlobalVariables(writtenGlobalVariables) { // Intentionally left empty. } @@ -918,13 +932,16 @@ namespace storm { // A DD that represents the transitions of this edge. storm::dd::Add<Type, ValueType> transitions; + // A mapping from transient variables to the DDs representing their value assignments. + std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientEdgeAssignments; + // The set of global variables written by this edge. std::set<storm::expressions::Variable> writtenGlobalVariables; }; // This structure represents an edge. struct ActionDd { - ActionDd(storm::dd::Add<Type> const& guard = storm::dd::Add<Type>(), storm::dd::Add<Type, ValueType> const& transitions = storm::dd::Add<Type, ValueType>(), std::pair<uint64_t, uint64_t> localNondeterminismVariables = std::pair<uint64_t, uint64_t>(0, 0), std::map<storm::expressions::Variable, storm::dd::Bdd<Type>> const& variableToWritingFragment = {}, storm::dd::Bdd<Type> const& illegalFragment = storm::dd::Bdd<Type>()) : guard(guard), transitions(transitions), localNondeterminismVariables(localNondeterminismVariables), variableToWritingFragment(variableToWritingFragment), illegalFragment(illegalFragment) { + ActionDd(storm::dd::Add<Type> const& guard = storm::dd::Add<Type>(), storm::dd::Add<Type, ValueType> const& transitions = storm::dd::Add<Type, ValueType>(), std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> const& transientEdgeAssignments = {}, std::pair<uint64_t, uint64_t> localNondeterminismVariables = std::pair<uint64_t, uint64_t>(0, 0), std::map<storm::expressions::Variable, storm::dd::Bdd<Type>> const& variableToWritingFragment = {}, storm::dd::Bdd<Type> const& illegalFragment = storm::dd::Bdd<Type>()) : guard(guard), transitions(transitions), transientEdgeAssignments(transientEdgeAssignments), localNondeterminismVariables(localNondeterminismVariables), variableToWritingFragment(variableToWritingFragment), illegalFragment(illegalFragment) { // Intentionally left empty. } @@ -946,6 +963,9 @@ namespace storm { // A DD that represents the transitions of this edge. storm::dd::Add<Type, ValueType> transitions; + // A mapping from transient variables to their assignments. + std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientEdgeAssignments; + // The local nondeterminism variables used by this action DD, given as the lowest std::pair<uint64_t, uint64_t> localNondeterminismVariables; @@ -960,7 +980,7 @@ namespace storm { // This structure represents a subcomponent of a composition. struct AutomatonDd { - AutomatonDd(storm::dd::Add<Type, ValueType> const& identity) : actionIndexToAction(), identity(identity), localNondeterminismVariables(std::make_pair<uint64_t, uint64_t>(0, 0)) { + AutomatonDd(storm::dd::Add<Type, ValueType> const& identity, std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> const& transientLocationAssignments = {}) : actionIndexToAction(), transientLocationAssignments(transientLocationAssignments), identity(identity), localNondeterminismVariables(std::make_pair<uint64_t, uint64_t>(0, 0)) { // Intentionally left empty. } @@ -988,6 +1008,9 @@ namespace storm { // A mapping from action indices to the action DDs. std::map<uint64_t, ActionDd> actionIndexToAction; + // A mapping from transient variables to their location-based transient assignment values. + std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientLocationAssignments; + // The identity of the automaton's variables. storm::dd::Add<Type, ValueType> identity; @@ -996,7 +1019,7 @@ namespace storm { }; - CombinedEdgesSystemComposer(storm::jani::Model const& model, CompositionVariables<Type, ValueType> const& variables) : SystemComposer<Type, ValueType>(model, variables) { + CombinedEdgesSystemComposer(storm::jani::Model const& model, CompositionVariables<Type, ValueType> const& variables, std::vector<storm::expressions::Variable> const& transientVariables) : SystemComposer<Type, ValueType>(model, variables, transientVariables) { // Intentionally left empty. } @@ -1080,6 +1103,7 @@ namespace storm { private: AutomatonDd composeInParallel(AutomatonDd const& automaton1, AutomatonDd const& automaton2, std::set<uint64_t> const& synchronizingActionIndices) { AutomatonDd result(automaton1); + result.transientLocationAssignments = joinTransientAssignmentMaps(automaton1.transientLocationAssignments, automaton2.transientLocationAssignments); // Treat all actions of the first automaton. for (auto const& action1 : automaton1.actionIndexToAction) { @@ -1103,7 +1127,7 @@ namespace storm { } else { // If only the first automaton has this action, we only need to apply the identity of the // second automaton. - result.actionIndexToAction[action1.first] = ActionDd(action1.second.guard, action1.second.transitions * automaton2.identity, action1.second.localNondeterminismVariables, action1.second.variableToWritingFragment, action1.second.illegalFragment); + result.actionIndexToAction[action1.first] = ActionDd(action1.second.guard, action1.second.transitions * automaton2.identity, action1.second.transientEdgeAssignments, action1.second.localNondeterminismVariables, action1.second.variableToWritingFragment, action1.second.illegalFragment); } } } @@ -1116,7 +1140,7 @@ namespace storm { if (synchronizingActionIndices.find(action2.first) == synchronizingActionIndices.end()) { // If only the second automaton has this action, we only need to apply the identity of the // first automaton. - result.actionIndexToAction[action2.first] = ActionDd(action2.second.guard, action2.second.transitions * automaton1.identity, action2.second.localNondeterminismVariables, action2.second.variableToWritingFragment, action2.second.illegalFragment); + result.actionIndexToAction[action2.first] = ActionDd(action2.second.guard, action2.second.transitions * automaton1.identity, action2.second.transientEdgeAssignments, action2.second.localNondeterminismVariables, action2.second.variableToWritingFragment, action2.second.illegalFragment); } } } @@ -1152,7 +1176,7 @@ namespace storm { } } - return ActionDd(action1.guard * action2.guard, action1.transitions * action2.transitions, std::make_pair(std::min(action1.getLowestLocalNondeterminismVariable(), action2.getLowestLocalNondeterminismVariable()), std::max(action1.getHighestLocalNondeterminismVariable(), action2.getHighestLocalNondeterminismVariable())), globalVariableToWritingFragment, illegalFragment); + return ActionDd(combinedGuard.template toAdd<ValueType>(), action1.transitions * action2.transitions, joinTransientAssignmentMaps(action1.transientEdgeAssignments, action2.transientEdgeAssignments), std::make_pair(std::min(action1.getLowestLocalNondeterminismVariable(), action2.getLowestLocalNondeterminismVariable()), std::max(action1.getHighestLocalNondeterminismVariable(), action2.getHighestLocalNondeterminismVariable())), globalVariableToWritingFragment, illegalFragment); } ActionDd combineUnsynchronizedActions(ActionDd action1, ActionDd action2, storm::dd::Add<Type, ValueType> const& identity1, storm::dd::Add<Type, ValueType> const& identity2) { @@ -1169,7 +1193,7 @@ namespace storm { STORM_LOG_TRACE("Combining unsynchronized actions."); if (this->model.getModelType() == storm::jani::ModelType::DTMC || this->model.getModelType() == storm::jani::ModelType::CTMC) { - return ActionDd(action1.guard + action2.guard, action1.transitions + action2.transitions, std::make_pair<uint64_t, uint64_t>(0, 0), joinVariableWritingFragmentMaps(action1.variableToWritingFragment, action2.variableToWritingFragment), this->variables.manager->getBddZero()); + return ActionDd(action1.guard + action2.guard, action1.transitions + action2.transitions, joinTransientAssignmentMaps(action1.transientEdgeAssignments, action2.transientEdgeAssignments), std::make_pair<uint64_t, uint64_t>(0, 0), joinVariableWritingFragmentMaps(action1.variableToWritingFragment, action2.variableToWritingFragment), this->variables.manager->getBddZero()); } else if (this->model.getModelType() == storm::jani::ModelType::MDP) { if (action1.transitions.isZero()) { return action2; @@ -1178,7 +1202,7 @@ namespace storm { } // Bring both choices to the same number of variables that encode the nondeterminism. - assert(action1.getLowestLocalNondeterminismVariable() == action2.getLowestLocalNondeterminismVariable()); + STORM_LOG_ASSERT(action1.getLowestLocalNondeterminismVariable() == action2.getLowestLocalNondeterminismVariable(), "Mismatching lowest nondeterminism variable indices."); uint_fast64_t highestLocalNondeterminismVariable = std::max(action1.getHighestLocalNondeterminismVariable(), action2.getHighestLocalNondeterminismVariable()); if (action1.getHighestLocalNondeterminismVariable() > action2.getHighestLocalNondeterminismVariable()) { storm::dd::Add<Type, ValueType> nondeterminismEncoding = this->variables.manager->template getAddOne<ValueType>(); @@ -1187,6 +1211,10 @@ namespace storm { nondeterminismEncoding *= this->variables.manager->getEncoding(this->variables.localNondeterminismVariables[i], 0).template toAdd<ValueType>(); } action2.transitions *= nondeterminismEncoding; + + for (auto& transientAssignment : action2.transientEdgeAssignments) { + transientAssignment.second *= nondeterminismEncoding; + } } else if (action2.getHighestLocalNondeterminismVariable() > action1.getHighestLocalNondeterminismVariable()) { storm::dd::Add<Type, ValueType> nondeterminismEncoding = this->variables.manager->template getAddOne<ValueType>(); @@ -1194,6 +1222,10 @@ namespace storm { nondeterminismEncoding *= this->variables.manager->getEncoding(this->variables.localNondeterminismVariables[i], 0).template toAdd<ValueType>(); } action1.transitions *= nondeterminismEncoding; + + for (auto& transientAssignment : action1.transientEdgeAssignments) { + transientAssignment.second *= nondeterminismEncoding; + } } // Add a new variable that resolves the nondeterminism between the two choices. @@ -1207,14 +1239,14 @@ namespace storm { entry.second = this->variables.manager->getEncoding(this->variables.localNondeterminismVariables[highestLocalNondeterminismVariable], 1) && entry.second; } - return ActionDd((action1.guard.toBdd() || action2.guard.toBdd()).template toAdd<ValueType>(), combinedTransitions, std::make_pair(action1.getLowestLocalNondeterminismVariable(), highestLocalNondeterminismVariable + 1), joinVariableWritingFragmentMaps(action1.variableToWritingFragment, action2.variableToWritingFragment), action1.illegalFragment || action2.illegalFragment); + return ActionDd((action1.guard.toBdd() || action2.guard.toBdd()).template toAdd<ValueType>(), combinedTransitions, joinTransientAssignmentMaps(action1.transientEdgeAssignments, action2.transientEdgeAssignments), std::make_pair(action1.getLowestLocalNondeterminismVariable(), highestLocalNondeterminismVariable + 1), joinVariableWritingFragmentMaps(action1.variableToWritingFragment, action2.variableToWritingFragment), action1.illegalFragment || action2.illegalFragment); } else { STORM_LOG_THROW(false, storm::exceptions::InvalidStateException, "Illegal model type."); } } AutomatonDd rename(AutomatonDd const& automaton, std::map<uint64_t, uint64_t> const& indexToIndex) { - AutomatonDd result(automaton.identity); + AutomatonDd result(automaton.identity, automaton.transientLocationAssignments); for (auto const& action : automaton.actionIndexToAction) { auto renamingIt = indexToIndex.find(action.first); @@ -1244,6 +1276,23 @@ namespace storm { return result; } + void performTransientAssignments(storm::jani::detail::ConstAssignments const& transientAssignments, std::function<void (storm::jani::Assignment const&)> const& callback) { + auto transientVariableIt = this->transientVariables.begin(); + auto transientVariableIte = this->transientVariables.end(); + for (auto const& assignment : transientAssignments) { + while (transientVariableIt != transientVariableIte && *transientVariableIt < assignment.getExpressionVariable()) { + ++transientVariableIt; + } + if (transientVariableIt == transientVariableIte) { + break; + } + if (*transientVariableIt == assignment.getExpressionVariable()) { + callback(assignment); + ++transientVariableIt; + } + } + } + EdgeDd buildEdgeDd(storm::jani::Automaton const& automaton, storm::jani::Edge const& edge) { STORM_LOG_TRACE("Translating guard " << edge.getGuard()); @@ -1309,7 +1358,13 @@ namespace storm { transitions *= this->variables.rowExpressionAdapter->translateExpression(edge.getRate()); } - return EdgeDd(guard, guard * transitions, globalVariablesInSomeDestination); + // Finally treat the transient assignments. + std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientEdgeAssignments; + if (!this->transientVariables.empty()) { + performTransientAssignments(edge.getAssignments().getTransientAssignments(), [this, &transientEdgeAssignments, &guard] (storm::jani::Assignment const& assignment) { transientEdgeAssignments[assignment.getExpressionVariable()] = guard * this->variables.rowExpressionAdapter->translateExpression(assignment.getAssignedExpression()); } ); + } + + return EdgeDd(guard, guard * transitions, transientEdgeAssignments, globalVariablesInSomeDestination); } else { return EdgeDd(this->variables.manager->template getAddZero<ValueType>(), this->variables.manager->template getAddZero<ValueType>()); } @@ -1338,16 +1393,52 @@ namespace storm { STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Cannot translate model of this type."); } } else { - return ActionDd(this->variables.manager->template getAddZero<ValueType>(), this->variables.manager->template getAddZero<ValueType>(), std::make_pair<uint64_t, uint64_t>(0, 0), {}, this->variables.manager->getBddZero()); + return ActionDd(this->variables.manager->template getAddZero<ValueType>(), this->variables.manager->template getAddZero<ValueType>(), {}, std::make_pair<uint64_t, uint64_t>(0, 0), {}, this->variables.manager->getBddZero()); } } + void addToTransientAssignmentMap(std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>>& transientAssignments, std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> const& assignmentsToAdd) { + for (auto const& entry : assignmentsToAdd) { + auto it = transientAssignments.find(entry.first); + if (it != transientAssignments.end()) { + it->second += entry.second; + } else { + transientAssignments[entry.first] = entry.second; + } + } + } + + void addToTransientAssignmentMap(std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>>& transientAssignments, storm::expressions::Variable const& variable, storm::dd::Add<Type, ValueType> const& assignmentToAdd) { + auto it = transientAssignments.find(variable); + if (it != transientAssignments.end()) { + it->second += assignmentToAdd; + } else { + transientAssignments[variable] = assignmentToAdd; + } + } + + std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> joinTransientAssignmentMaps(std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> const& transientAssignments1, std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> const& transientAssignments2) { + std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> result = transientAssignments1; + + for (auto const& entry : transientAssignments2) { + auto resultIt = result.find(entry.first); + if (resultIt != result.end()) { + resultIt->second += entry.second; + } else { + result[entry.first] = entry.second; + } + } + + return result; + } + ActionDd combineEdgesToActionMarkovChain(std::vector<EdgeDd> const& edgeDds) { storm::dd::Bdd<Type> allGuards = this->variables.manager->getBddZero(); storm::dd::Add<Type, ValueType> allTransitions = this->variables.manager->template getAddZero<ValueType>(); storm::dd::Bdd<Type> temporary; std::map<storm::expressions::Variable, storm::dd::Bdd<Type>> globalVariableToWritingFragment; + std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientEdgeAssignments; for (auto const& edgeDd : edgeDds) { // Check for overlapping guards. storm::dd::Bdd<Type> guardBdd = edgeDd.guard.toBdd(); @@ -1360,6 +1451,9 @@ namespace storm { allGuards |= guardBdd; allTransitions += edgeDd.transitions; + // Add the transient variable assignments to the resulting one. + addToTransientAssignmentMap(transientEdgeAssignments, edgeDd.transientEdgeAssignments); + // Keep track of the fragment that is writing global variables. for (auto const& variable : edgeDd.writtenGlobalVariables) { auto it = globalVariableToWritingFragment.find(variable); @@ -1371,7 +1465,7 @@ namespace storm { } } - return ActionDd(allGuards.template toAdd<ValueType>(), allTransitions, std::make_pair<uint64_t, uint64_t>(0, 0), globalVariableToWritingFragment, this->variables.manager->getBddZero()); + return ActionDd(allGuards.template toAdd<ValueType>(), allTransitions, transientEdgeAssignments, std::make_pair<uint64_t, uint64_t>(0, 0), globalVariableToWritingFragment, this->variables.manager->getBddZero()); } void addToVariableWritingFragmentMap(std::map<storm::expressions::Variable, storm::dd::Bdd<Type>>& globalVariableToWritingFragment, storm::expressions::Variable const& variable, storm::dd::Bdd<Type> const& partToAdd) const { @@ -1401,13 +1495,15 @@ namespace storm { ActionDd combineEdgesBySummation(storm::dd::Add<Type, ValueType> const& guard, std::vector<EdgeDd> const& edges) { storm::dd::Add<Type, ValueType> transitions = this->variables.manager->template getAddZero<ValueType>(); std::map<storm::expressions::Variable, storm::dd::Bdd<Type>> globalVariableToWritingFragment; + std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientEdgeAssignments; for (auto const& edge : edges) { transitions += edge.transitions; + addToTransientAssignmentMap(transientEdgeAssignments, edge.transientEdgeAssignments); for (auto const& variable : edge.writtenGlobalVariables) { addToVariableWritingFragmentMap(globalVariableToWritingFragment, variable, edge.guard.toBdd()); } } - return ActionDd(guard, transitions, std::make_pair<uint64_t, uint64_t>(0, 0), globalVariableToWritingFragment, this->variables.manager->getBddZero()); + return ActionDd(guard, transitions, transientEdgeAssignments, std::make_pair<uint64_t, uint64_t>(0, 0), globalVariableToWritingFragment, this->variables.manager->getBddZero()); } ActionDd combineEdgesToActionMdp(std::vector<EdgeDd> const& edges, uint64_t localNondeterminismVariableOffset) { @@ -1430,6 +1526,7 @@ namespace storm { storm::dd::Add<Type, ValueType> allEdges = this->variables.manager->template getAddZero<ValueType>(); std::map<storm::expressions::Variable, storm::dd::Bdd<Type>> globalVariableToWritingFragment; + std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientAssignments; storm::dd::Bdd<Type> equalsNumberOfChoicesDd; std::vector<storm::dd::Add<Type, ValueType>> choiceDds(maxChoices, this->variables.manager->template getAddZero<ValueType>()); @@ -1480,6 +1577,11 @@ namespace storm { // Combine the overlapping part of the guard with command updates and add it to the resulting DD. choiceDds[k] += remainingGuardChoicesIntersection.template toAdd<ValueType>() * currentEdge.transitions; + // Keep track of the fragment of transient assignments. + for (auto const& transientAssignment : currentEdge.transientEdgeAssignments) { + addToTransientAssignmentMap(transientAssignments, transientAssignment.first, remainingGuardChoicesIntersection.template toAdd<ValueType>() * transientAssignment.second * indicesEncodedWithLocalNondeterminismVariables[k].first.template toAdd<ValueType>()); + } + // Keep track of the written global variables of the fragment. for (auto const& variable : currentEdge.writtenGlobalVariables) { addToVariableWritingFragmentMap(globalVariableToWritingFragment, variable, remainingGuardChoicesIntersection && indicesEncodedWithLocalNondeterminismVariables[k].first); @@ -1505,7 +1607,7 @@ namespace storm { sumOfGuards = sumOfGuards * (!equalsNumberOfChoicesDd).template toAdd<ValueType>(); } - return ActionDd(allGuards.template toAdd<ValueType>(), allEdges, std::make_pair(localNondeterminismVariableOffset, localNondeterminismVariableOffset + numberOfBinaryVariables), globalVariableToWritingFragment, this->variables.manager->getBddZero()); + return ActionDd(allGuards.template toAdd<ValueType>(), allEdges, transientAssignments, std::make_pair(localNondeterminismVariableOffset, localNondeterminismVariableOffset + numberOfBinaryVariables), globalVariableToWritingFragment, this->variables.manager->getBddZero()); } } @@ -1523,6 +1625,20 @@ namespace storm { result.setLowestLocalNondeterminismVariable(std::max(result.getLowestLocalNondeterminismVariable(), actionDd.getLowestLocalNondeterminismVariable())); result.setHighestLocalNondeterminismVariable(std::max(result.getHighestLocalNondeterminismVariable(), actionDd.getHighestLocalNondeterminismVariable())); } + + for (uint64_t locationIndex = 0; locationIndex < automaton.getNumberOfLocations(); ++locationIndex) { + auto const& location = automaton.getLocation(locationIndex); + performTransientAssignments(location.getAssignments().getTransientAssignments(), [this,&automatonName,locationIndex,&result] (storm::jani::Assignment const& assignment) { + storm::dd::Add<Type, ValueType> assignedValues = this->variables.manager->getEncoding(this->variables.automatonToLocationVariableMap.at(automatonName).first, locationIndex).template toAdd<ValueType>() * this->variables.rowExpressionAdapter->translateExpression(assignment.getAssignedExpression()); + auto it = result.transientLocationAssignments.find(assignment.getExpressionVariable()); + if (it != result.transientLocationAssignments.end()) { + it->second += assignedValues; + } else { + result.transientLocationAssignments[assignment.getExpressionVariable()] = assignedValues; + } + }); + } + return result; } @@ -1553,28 +1669,36 @@ namespace storm { uint64_t numberOfUsedNondeterminismVariables = automaton.getHighestLocalNondeterminismVariable(); // Add missing global variable identities, action and nondeterminism encodings. + std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientEdgeAssignments; for (auto& action : automaton.actionIndexToAction) { illegalFragment |= action.second.illegalFragment; addMissingGlobalVariableIdentities(action.second); storm::dd::Add<Type, ValueType> actionEncoding = encodeAction(action.first != this->model.getSilentActionIndex() ? boost::optional<uint64_t>(action.first) : boost::none, this->variables); storm::dd::Add<Type, ValueType> missingNondeterminismEncoding = encodeIndex(0, action.second.getHighestLocalNondeterminismVariable(), numberOfUsedNondeterminismVariables - action.second.getHighestLocalNondeterminismVariable(), this->variables); storm::dd::Add<Type, ValueType> extendedTransitions = actionEncoding * missingNondeterminismEncoding * action.second.transitions; + + for (auto const& transientAssignment : action.second.transientEdgeAssignments) { + addToTransientAssignmentMap(transientEdgeAssignments, transientAssignment.first, actionEncoding * missingNondeterminismEncoding * transientAssignment.second); + } + result += extendedTransitions; } - return ComposerResult<Type, ValueType>(result, illegalFragment, numberOfUsedNondeterminismVariables); + return ComposerResult<Type, ValueType>(result, automaton.transientLocationAssignments, transientEdgeAssignments, illegalFragment, numberOfUsedNondeterminismVariables); } else if (this->model.getModelType() == storm::jani::ModelType::DTMC || this->model.getModelType() == storm::jani::ModelType::CTMC) { // Simply add all actions, but make sure to include the missing global variable identities. storm::dd::Add<Type, ValueType> result = this->variables.manager->template getAddZero<ValueType>(); storm::dd::Bdd<Type> illegalFragment = this->variables.manager->getBddZero(); + std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientEdgeAssignments; for (auto& action : automaton.actionIndexToAction) { illegalFragment |= action.second.illegalFragment; addMissingGlobalVariableIdentities(action.second); + addToTransientAssignmentMap(transientEdgeAssignments, action.second.transientEdgeAssignments); result += action.second.transitions; } - return ComposerResult<Type, ValueType>(result, illegalFragment, 0); + return ComposerResult<Type, ValueType>(result, automaton.transientLocationAssignments, transientEdgeAssignments, illegalFragment, 0); } else { STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Illegal model type."); } @@ -1718,31 +1842,100 @@ namespace storm { } template <storm::dd::DdType Type, typename ValueType> - std::shared_ptr<storm::models::symbolic::Model<Type, ValueType>> DdJaniModelBuilder<Type, ValueType>::build() { + std::vector<storm::expressions::Variable> selectRewardVariables(storm::jani::Model const& model, typename DdJaniModelBuilder<Type, ValueType>::Options const& options) { + std::vector<storm::expressions::Variable> result; + if (options.isBuildAllRewardModelsSet()) { + for (auto const& variable : model.getGlobalVariables()) { + if (variable.isTransient()) { + result.push_back(variable.getExpressionVariable()); + } + } + } else { + auto const& globalVariables = model.getGlobalVariables(); + for (auto const& rewardModelName : options.getRewardModelNames()) { + if (globalVariables.hasVariable(rewardModelName)) { + result.push_back(globalVariables.getVariable(rewardModelName).getExpressionVariable()); + } else { + STORM_LOG_THROW(rewardModelName.empty(), storm::exceptions::InvalidArgumentException, "Cannot build unknown reward model '" << rewardModelName << "'."); + STORM_LOG_THROW(globalVariables.getNumberOfTransientVariables() == 1, storm::exceptions::InvalidArgumentException, "Reference to standard reward model is ambiguous."); + } + } + + // If no reward model was yet added, but there was one that was given in the options, we try to build the + // standard reward model. + if (result.empty() && !options.getRewardModelNames().empty()) { + result.push_back(globalVariables.getTransientVariables().front()->getExpressionVariable()); + } + } + + return result; + } + + template <storm::dd::DdType Type, typename ValueType> + std::unordered_map<std::string, storm::models::symbolic::StandardRewardModel<Type, ValueType>> buildRewardModels(ComposerResult<Type, ValueType> const& system, std::vector<storm::expressions::Variable> const& rewardVariables) { + std::unordered_map<std::string, storm::models::symbolic::StandardRewardModel<Type, ValueType>> result; + + for (auto const& variable : rewardVariables) { + boost::optional<storm::dd::Add<Type, ValueType>> stateRewards = boost::none; + boost::optional<storm::dd::Add<Type, ValueType>> stateActionRewards = boost::none; + boost::optional<storm::dd::Add<Type, ValueType>> transitionRewards = boost::none; + + auto it = system.transientLocationAssignments.find(variable); + if (it != system.transientLocationAssignments.end()) { + stateRewards = it->second; + } + + it = system.transientEdgeAssignments.find(variable); + if (it != system.transientEdgeAssignments.end()) { + stateActionRewards = it->second; + } + + result.emplace(variable.getName(), storm::models::symbolic::StandardRewardModel<Type, ValueType>(stateRewards, stateActionRewards, transitionRewards)); + } + + return result; + } + + template <storm::dd::DdType Type, typename ValueType> + std::shared_ptr<storm::models::symbolic::Model<Type, ValueType>> DdJaniModelBuilder<Type, ValueType>::build(storm::jani::Model const& model, Options const& options) { + if (model.hasUndefinedConstants()) { + std::vector<std::reference_wrapper<storm::jani::Constant const>> undefinedConstants = model.getUndefinedConstants(); + std::vector<std::string> strings; + for (auto const& constant : undefinedConstants) { + std::stringstream stream; + stream << constant.get().getName() << " (" << constant.get().getType() << ")"; + strings.push_back(stream.str()); + } + STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Model still contains these undefined constants: " << boost::join(strings, ", ") << "."); + } + // Create all necessary variables. - CompositionVariableCreator<Type, ValueType> variableCreator(*this->model); + CompositionVariableCreator<Type, ValueType> variableCreator(model); CompositionVariables<Type, ValueType> variables = variableCreator.create(); + // Determine which transient assignments need to be considered in the building process. + std::vector<storm::expressions::Variable> rewardVariables = selectRewardVariables<Type, ValueType>(model, options); + // Create a builder to compose and build the model. -// SeparateEdgesSystemComposer<Type, ValueType> composer(*this->model, variables); - CombinedEdgesSystemComposer<Type, ValueType> composer(*this->model, variables); +// SeparateEdgesSystemComposer<Type, ValueType> composer(model, variables); + CombinedEdgesSystemComposer<Type, ValueType> composer(model, variables, rewardVariables); ComposerResult<Type, ValueType> system = composer.compose(); // Postprocess the variables in place. - postprocessVariables(this->model->getModelType(), system, variables); + postprocessVariables(model.getModelType(), system, variables); // Postprocess the system in place and get the states that were terminal (i.e. whose transitions were cut off). - storm::dd::Bdd<Type> terminalStates = postprocessSystem(*this->model, system, variables, options); + storm::dd::Bdd<Type> terminalStates = postprocessSystem(model, system, variables, options); // Start creating the model components. ModelComponents<Type, ValueType> modelComponents; // Build initial states. - modelComponents.initialStates = computeInitialStates(*this->model, variables); + modelComponents.initialStates = computeInitialStates(model, variables); // Perform reachability analysis to obtain reachable states. storm::dd::Bdd<Type> transitionMatrixBdd = system.transitions.notZero(); - if (this->model->getModelType() == storm::jani::ModelType::MDP) { + if (model.getModelType() == storm::jani::ModelType::MDP) { transitionMatrixBdd = transitionMatrixBdd.existsAbstract(variables.allNondeterminismVariables); } modelComponents.reachableStates = storm::utility::dd::computeReachableStates(modelComponents.initialStates, transitionMatrixBdd, variables.rowMetaVariables, variables.columnMetaVariables); @@ -1756,13 +1949,16 @@ namespace storm { modelComponents.transitionMatrix = system.transitions * reachableStatesAdd; // Fix deadlocks if existing. - modelComponents.deadlockStates = fixDeadlocks(this->model->getModelType(), modelComponents.transitionMatrix, transitionMatrixBdd, modelComponents.reachableStates, variables); + modelComponents.deadlockStates = fixDeadlocks(model.getModelType(), modelComponents.transitionMatrix, transitionMatrixBdd, modelComponents.reachableStates, variables); // Cut the deadlock states by removing all states that we 'converted' to deadlock states by making them terminal. modelComponents.deadlockStates = modelComponents.deadlockStates && !terminalStates; + // Build the reward models. + modelComponents.rewardModels = buildRewardModels(system, rewardVariables); + // Finally, create the model. - return createModel(this->model->getModelType(), variables, modelComponents); + return createModel(model.getModelType(), variables, modelComponents); } template class DdJaniModelBuilder<storm::dd::DdType::CUDD, double>; diff --git a/src/builder/DdJaniModelBuilder.h b/src/builder/DdJaniModelBuilder.h index 348d017ad..dd99f5fcb 100644 --- a/src/builder/DdJaniModelBuilder.h +++ b/src/builder/DdJaniModelBuilder.h @@ -59,6 +59,16 @@ namespace storm { */ void setTerminalStatesFromFormula(storm::logic::Formula const& formula); + /*! + * Retrieves the names of the reward models to build. + */ + std::set<std::string> const& getRewardModelNames() const; + + /*! + * Retrieves whether the flag to build all reward models is set. + */ + bool isBuildAllRewardModelsSet() const; + // A flag that indicates whether or not all reward models are to be build. bool buildAllRewardModels; @@ -77,11 +87,6 @@ namespace storm { boost::optional<storm::expressions::Expression> negatedTerminalStates; }; - /*! - * Creates a builder for the given model that uses the given options. - */ - DdJaniModelBuilder(storm::jani::Model const& model, Options const& options = Options()); - /*! * Translates the given program into a symbolic model (i.e. one that stores the transition relation as a * decision diagram). @@ -89,22 +94,7 @@ namespace storm { * @param model The model to translate. * @return A pointer to the resulting model. */ - std::shared_ptr<storm::models::symbolic::Model<Type, ValueType>> build(); - - /*! - * Retrieves the model that was actually translated (i.e. including constant substitutions etc.). Note - * that this function may only be called after a succesful translation. - * - * @return The translated model. - */ - storm::jani::Model const& getTranslatedModel() const; - - private: - /// The model to translate. - boost::optional<storm::jani::Model> model; - - /// The options to use for building the model. - Options options; + std::shared_ptr<storm::models::symbolic::Model<Type, ValueType>> build(storm::jani::Model const& model, Options const& options = Options()); }; } diff --git a/src/builder/DdPrismModelBuilder.cpp b/src/builder/DdPrismModelBuilder.cpp index a397b0de0..b45269dc4 100644 --- a/src/builder/DdPrismModelBuilder.cpp +++ b/src/builder/DdPrismModelBuilder.cpp @@ -1219,20 +1219,15 @@ namespace storm { } } + stateActionRewards.get().exportToDot("prismrew.dot"); + return storm::models::symbolic::StandardRewardModel<Type, ValueType>(stateRewards, stateActionRewards, transitionRewards); } - template <storm::dd::DdType Type, typename ValueType> - storm::prism::Program const& DdPrismModelBuilder<Type, ValueType>::getTranslatedProgram() const { - return preparedProgram.get(); - } - template <storm::dd::DdType Type, typename ValueType> std::shared_ptr<storm::models::symbolic::Model<Type, ValueType>> DdPrismModelBuilder<Type, ValueType>::build(storm::prism::Program const& program, Options const& options) { - preparedProgram = program; - - if (preparedProgram->hasUndefinedConstants()) { - std::vector<std::reference_wrapper<storm::prism::Constant const>> undefinedConstants = preparedProgram->getUndefinedConstants(); + if (program.hasUndefinedConstants()) { + std::vector<std::reference_wrapper<storm::prism::Constant const>> undefinedConstants = program.getUndefinedConstants(); std::stringstream stream; bool printComma = false; for (auto const& constant : undefinedConstants) { @@ -1247,13 +1242,11 @@ namespace storm { STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Program still contains these undefined constants: " + stream.str()); } - preparedProgram = preparedProgram->substituteConstants(); - - STORM_LOG_DEBUG("Building representation of program:" << std::endl << *preparedProgram << std::endl); + STORM_LOG_DEBUG("Building representation of program:" << std::endl << program << std::endl); // Start by initializing the structure used for storing all information needed during the model generation. // In particular, this creates the meta variables used to encode the model. - GenerationInformation generationInfo(*preparedProgram); + GenerationInformation generationInfo(program); SystemResult system = createSystemDecisionDiagram(generationInfo); storm::dd::Add<Type, ValueType> transitionMatrix = system.allTransitionsDd; @@ -1264,7 +1257,7 @@ namespace storm { // If we were asked to treat some states as terminal states, we cut away their transitions now. storm::dd::Bdd<Type> terminalStatesBdd = generationInfo.manager->getBddZero(); if (options.terminalStates || options.negatedTerminalStates) { - std::map<storm::expressions::Variable, storm::expressions::Expression> constantsSubstitution = preparedProgram->getConstantsSubstitution(); + std::map<storm::expressions::Variable, storm::expressions::Expression> constantsSubstitution = program.getConstantsSubstitution(); if (options.terminalStates) { storm::expressions::Expression terminalExpression; @@ -1273,7 +1266,7 @@ namespace storm { } else { std::string const& labelName = boost::get<std::string>(options.terminalStates.get()); if (program.hasLabel(labelName)) { - terminalExpression = preparedProgram->getLabelExpression(labelName); + terminalExpression = program.getLabelExpression(labelName); } else { STORM_LOG_THROW(labelName == "init" || labelName == "deadlock", storm::exceptions::InvalidArgumentException, "Terminal states refer to illegal label '" << labelName << "'."); } @@ -1294,7 +1287,7 @@ namespace storm { } else { std::string const& labelName = boost::get<std::string>(options.negatedTerminalStates.get()); if (program.hasLabel(labelName)) { - negatedTerminalExpression = preparedProgram->getLabelExpression(labelName); + negatedTerminalExpression = program.getLabelExpression(labelName); } else { STORM_LOG_THROW(labelName == "init" || labelName == "deadlock", storm::exceptions::InvalidArgumentException, "Terminal states refer to illegal label '" << labelName << "'."); } @@ -1377,18 +1370,18 @@ namespace storm { // First, we make sure that all selected reward models actually exist. for (auto const& rewardModelName : options.rewardModelsToBuild) { - STORM_LOG_THROW(rewardModelName.empty() || preparedProgram->hasRewardModel(rewardModelName), storm::exceptions::InvalidArgumentException, "Model does not possess a reward model with the name '" << rewardModelName << "'."); + STORM_LOG_THROW(rewardModelName.empty() || program.hasRewardModel(rewardModelName), storm::exceptions::InvalidArgumentException, "Model does not possess a reward model with the name '" << rewardModelName << "'."); } - for (auto const& rewardModel : preparedProgram->getRewardModels()) { + for (auto const& rewardModel : program.getRewardModels()) { if (options.buildAllRewardModels || options.rewardModelsToBuild.find(rewardModel.getName()) != options.rewardModelsToBuild.end()) { selectedRewardModels.push_back(rewardModel); } } // If no reward model was selected until now and a referenced reward model appears to be unique, we build // the only existing reward model (given that no explicit name was given for the referenced reward model). - if (selectedRewardModels.empty() && preparedProgram->getNumberOfRewardModels() == 1 && options.rewardModelsToBuild.size() == 1 && *options.rewardModelsToBuild.begin() == "") { - selectedRewardModels.push_back(preparedProgram->getRewardModel(0)); + if (selectedRewardModels.empty() && program.getNumberOfRewardModels() == 1 && options.rewardModelsToBuild.size() == 1 && *options.rewardModelsToBuild.begin() == "") { + selectedRewardModels.push_back(program.getRewardModel(0)); } std::unordered_map<std::string, storm::models::symbolic::StandardRewardModel<Type, ValueType>> rewardModels; @@ -1398,7 +1391,7 @@ namespace storm { // Build the labels that can be accessed as a shortcut. std::map<std::string, storm::expressions::Expression> labelToExpressionMapping; - for (auto const& label : preparedProgram->getLabels()) { + for (auto const& label : program.getLabels()) { labelToExpressionMapping.emplace(label.getName(), label.getStatePredicateExpression()); } @@ -1415,7 +1408,7 @@ namespace storm { template <storm::dd::DdType Type, typename ValueType> storm::dd::Bdd<Type> DdPrismModelBuilder<Type, ValueType>::createInitialStatesDecisionDiagram(GenerationInformation& generationInfo) { - storm::dd::Bdd<Type> initialStates = generationInfo.rowExpressionAdapter->translateExpression(generationInfo.program.getInitialConstruct().getInitialStatesExpression()).toBdd(); + storm::dd::Bdd<Type> initialStates = generationInfo.rowExpressionAdapter->translateExpression(generationInfo.program.getInitialStatesExpression()).toBdd(); for (auto const& metaVariable : generationInfo.rowMetaVariables) { initialStates &= generationInfo.manager->getRange(metaVariable); diff --git a/src/builder/DdPrismModelBuilder.h b/src/builder/DdPrismModelBuilder.h index 6db0c4639..464a0a071 100644 --- a/src/builder/DdPrismModelBuilder.h +++ b/src/builder/DdPrismModelBuilder.h @@ -98,14 +98,6 @@ namespace storm { */ std::shared_ptr<storm::models::symbolic::Model<Type, ValueType>> build(storm::prism::Program const& program, Options const& options = Options()); - /*! - * Retrieves the program that was actually translated (i.e. including constant substitutions etc.). Note - * that this function may only be called after a succesful translation. - * - * @return The translated program. - */ - storm::prism::Program const& getTranslatedProgram() const; - private: // This structure can store the decision diagrams representing a particular action. struct UpdateDecisionDiagram { @@ -243,9 +235,6 @@ namespace storm { static SystemResult createSystemDecisionDiagram(GenerationInformation& generationInfo); static storm::dd::Bdd<Type> createInitialStatesDecisionDiagram(GenerationInformation& generationInfo); - - // This member holds the program that was most recently translated (if any). - boost::optional<storm::prism::Program> preparedProgram; }; } // namespace adapters diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 148c8d357..755674cd1 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -3,6 +3,8 @@ #include "../utility/storm.h" +#include "src/storage/SymbolicModelDescription.h" + #include "src/settings/modules/DebugSettings.h" #include "src/settings/modules/IOSettings.h" #include "src/settings/modules/CoreSettings.h" @@ -206,42 +208,46 @@ namespace storm { storm::utility::initializeFileLogging(); } - if (storm::settings::getModule<storm::settings::modules::IOSettings>().isSymbolicSet()) { - // If we have to build the model from a symbolic representation, we need to parse the representation first. - storm::prism::Program program = storm::parseProgram(storm::settings::getModule<storm::settings::modules::IOSettings>().getSymbolicModelFilename()); + auto ioSettings = storm::settings::getModule<storm::settings::modules::IOSettings>(); + if (ioSettings.isPrismOrJaniInputSet()) { + storm::storage::SymbolicModelDescription model; + if (ioSettings.isPrismInputSet()) { + model = storm::parseProgram(ioSettings.getPrismInputFilename()); + if (ioSettings.isPrismToJaniSet()) { + model = model.toJani(true); + } + } else if (ioSettings.isJaniInputSet()) { + model = storm::parseJaniModel(ioSettings.getJaniInputFilename()).first; + } // Get the string that assigns values to the unknown currently undefined constants in the model. - std::string constantDefinitionString = storm::settings::getModule<storm::settings::modules::IOSettings>().getConstantDefinitionString(); - storm::prism::Program preprocessedProgram = storm::utility::prism::preprocess(program, constantDefinitionString); - std::map<storm::expressions::Variable, storm::expressions::Expression> constantsSubstitution = preprocessedProgram.getConstantsSubstitution(); + std::string constantDefinitionString = ioSettings.getConstantDefinitionString(); + model = model.preprocess(constantDefinitionString); // Then proceed to parsing the properties (if given), since the model we are building may depend on the property. std::vector<std::shared_ptr<storm::logic::Formula const>> formulas; if (storm::settings::getModule<storm::settings::modules::GeneralSettings>().isPropertySet()) { - formulas = storm::parseFormulasForProgram(storm::settings::getModule<storm::settings::modules::GeneralSettings>().getProperty(), preprocessedProgram); - } - - // There may be constants of the model appearing in the formulas, so we replace all their occurrences - // by their definitions in the translated program. - std::vector<std::shared_ptr<storm::logic::Formula const>> preprocessedFormulas; - for (auto const& formula : formulas) { - preprocessedFormulas.emplace_back(formula->substitute(constantsSubstitution)); + if (model.isJaniModel()) { + formulas = storm::parseFormulasForJaniModel(storm::settings::getModule<storm::settings::modules::GeneralSettings>().getProperty(), model.asJaniModel()); + } else { + formulas = storm::parseFormulasForPrismProgram(storm::settings::getModule<storm::settings::modules::GeneralSettings>().getProperty(), model.asPrismProgram()); + } } if (storm::settings::getModule<storm::settings::modules::GeneralSettings>().isParametricSet()) { #ifdef STORM_HAVE_CARL - buildAndCheckSymbolicModel<storm::RationalFunction>(preprocessedProgram, preprocessedFormulas, true); + buildAndCheckSymbolicModel<storm::RationalFunction>(model, formulas, true); #else STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "No parameters are supported in this build."); #endif } else if (storm::settings::getModule<storm::settings::modules::GeneralSettings>().isExactSet()) { #ifdef STORM_HAVE_CARL - buildAndCheckSymbolicModel<storm::RationalNumber>(preprocessedProgram, preprocessedFormulas, true); + buildAndCheckSymbolicModel<storm::RationalNumber>(model, formulas, true); #else STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "No exact numbers are supported in this build."); #endif } else { - buildAndCheckSymbolicModel<double>(preprocessedProgram, preprocessedFormulas, true); + buildAndCheckSymbolicModel<double>(model, formulas, true); } } else if (storm::settings::getModule<storm::settings::modules::IOSettings>().isExplicitSet()) { STORM_LOG_THROW(storm::settings::getModule<storm::settings::modules::CoreSettings>().getEngine() == storm::settings::modules::CoreSettings::Engine::Sparse, storm::exceptions::InvalidSettingsException, "Only the sparse engine supports explicit model input."); diff --git a/src/cli/entrypoints.h b/src/cli/entrypoints.h index b41a0f07f..f4b009b7e 100644 --- a/src/cli/entrypoints.h +++ b/src/cli/entrypoints.h @@ -3,7 +3,10 @@ #include "src/utility/storm.h" +#include "src/storage/SymbolicModelDescription.h" + #include "src/exceptions/NotImplementedException.h" +#include "src/exceptions/InvalidSettingsException.h" namespace storm { namespace cli { @@ -49,12 +52,14 @@ namespace storm { #endif template<storm::dd::DdType DdType> - void verifySymbolicModelWithAbstractionRefinementEngine(storm::prism::Program const& program, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, bool onlyInitialStatesRelevant = false) { + void verifySymbolicModelWithAbstractionRefinementEngine(storm::storage::SymbolicModelDescription const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, bool onlyInitialStatesRelevant = false) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Abstraction Refinement is not yet implemented."); } template<typename ValueType> - void verifySymbolicModelWithExplorationEngine(storm::prism::Program const& program, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, bool onlyInitialStatesRelevant = false) { + void verifySymbolicModelWithExplorationEngine(storm::storage::SymbolicModelDescription const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, bool onlyInitialStatesRelevant = false) { + STORM_LOG_THROW(model.isPrismProgram(), storm::exceptions::InvalidSettingsException, "Exploration engine is currently only applicable to PRISM models."); + storm::prism::Program const& program = model.asPrismProgram(); STORM_LOG_THROW(program.getModelType() == storm::prism::Program::ModelType::DTMC || program.getModelType() == storm::prism::Program::ModelType::MDP, storm::exceptions::InvalidSettingsException, "Currently exploration-based verification is only available for DTMCs and MDPs."); for (auto const& formula : formulas) { @@ -98,7 +103,7 @@ namespace storm { #ifdef STORM_HAVE_CARL template<> - void verifySymbolicModelWithExplorationEngine<storm::RationalFunction>(storm::prism::Program const& program, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, bool onlyInitialStatesRelevant) { + void verifySymbolicModelWithExplorationEngine<storm::RationalFunction>(storm::storage::SymbolicModelDescription const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, bool onlyInitialStatesRelevant) { STORM_LOG_THROW(false, storm::exceptions::InvalidSettingsException, "Exploration-based verification does currently not support parametric models."); } #endif @@ -177,81 +182,80 @@ namespace storm { } template<storm::dd::DdType LibraryType> - void buildAndCheckSymbolicModelWithSymbolicEngine(bool hybrid, storm::prism::Program const& program, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, bool onlyInitialStatesRelevant = false) { + void buildAndCheckSymbolicModelWithSymbolicEngine(bool hybrid, storm::storage::SymbolicModelDescription const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, bool onlyInitialStatesRelevant = false) { // Start by building the model. - auto model = buildSymbolicModel<double, LibraryType>(program, formulas); + auto markovModel = buildSymbolicModel<double, LibraryType>(model, formulas); // Print some information about the model. - model->printModelInformationToStream(std::cout); + markovModel->printModelInformationToStream(std::cout); // Then select the correct engine. if (hybrid) { - verifySymbolicModelWithHybridEngine(model, formulas, onlyInitialStatesRelevant); + verifySymbolicModelWithHybridEngine(markovModel, formulas, onlyInitialStatesRelevant); } else { - verifySymbolicModelWithDdEngine(model, formulas, onlyInitialStatesRelevant); + verifySymbolicModelWithDdEngine(markovModel, formulas, onlyInitialStatesRelevant); } } template<typename ValueType> - void buildAndCheckSymbolicModelWithSparseEngine(storm::prism::Program const& program, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, bool onlyInitialStatesRelevant = false) { + void buildAndCheckSymbolicModelWithSparseEngine(storm::storage::SymbolicModelDescription const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, bool onlyInitialStatesRelevant = false) { // Start by building the model. - std::shared_ptr<storm::models::ModelBase> model = buildSparseModel<ValueType>(program, formulas); + std::shared_ptr<storm::models::ModelBase> markovModel = buildSparseModel<ValueType>(model, formulas); // Print some information about the model. - model->printModelInformationToStream(std::cout); + markovModel->printModelInformationToStream(std::cout); // Preprocess the model. - BRANCH_ON_SPARSE_MODELTYPE(model, model, ValueType, preprocessModel, formulas); + BRANCH_ON_SPARSE_MODELTYPE(markovModel, markovModel, ValueType, preprocessModel, formulas); - std::shared_ptr<storm::models::sparse::Model<ValueType>> sparseModel = model->template as<storm::models::sparse::Model<ValueType>>(); + std::shared_ptr<storm::models::sparse::Model<ValueType>> sparseModel = markovModel->template as<storm::models::sparse::Model<ValueType>>(); // Finally, treat the formulas. if (storm::settings::getModule<storm::settings::modules::CoreSettings>().isCounterexampleSet()) { - generateCounterexamples<ValueType>(program, sparseModel, formulas); + generateCounterexamples<ValueType>(model, sparseModel, formulas); } else { verifySparseModel<ValueType>(sparseModel, formulas, onlyInitialStatesRelevant); } } template<typename ValueType> - void buildAndCheckSymbolicModel(storm::prism::Program const& program, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, bool onlyInitialStatesRelevant = false) { + void buildAndCheckSymbolicModel(storm::storage::SymbolicModelDescription const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, bool onlyInitialStatesRelevant = false) { if (storm::settings::getModule<storm::settings::modules::CoreSettings>().getEngine() == storm::settings::modules::CoreSettings::Engine::AbstractionRefinement) { auto ddlib = storm::settings::getModule<storm::settings::modules::CoreSettings>().getDdLibraryType(); if (ddlib == storm::dd::DdType::CUDD) { - verifySymbolicModelWithAbstractionRefinementEngine<storm::dd::DdType::CUDD>(program, formulas, onlyInitialStatesRelevant); + verifySymbolicModelWithAbstractionRefinementEngine<storm::dd::DdType::CUDD>(model, formulas, onlyInitialStatesRelevant); } else { - verifySymbolicModelWithAbstractionRefinementEngine<storm::dd::DdType::Sylvan>(program, formulas, onlyInitialStatesRelevant); + verifySymbolicModelWithAbstractionRefinementEngine<storm::dd::DdType::Sylvan>(model, formulas, onlyInitialStatesRelevant); } } else if (storm::settings::getModule<storm::settings::modules::CoreSettings>().getEngine() == storm::settings::modules::CoreSettings::Engine::Exploration) { - verifySymbolicModelWithExplorationEngine<ValueType>(program, formulas, onlyInitialStatesRelevant); + verifySymbolicModelWithExplorationEngine<ValueType>(model, formulas, onlyInitialStatesRelevant); } else { auto engine = storm::settings::getModule<storm::settings::modules::CoreSettings>().getEngine(); if (engine == storm::settings::modules::CoreSettings::Engine::Dd || engine == storm::settings::modules::CoreSettings::Engine::Hybrid) { auto ddlib = storm::settings::getModule<storm::settings::modules::CoreSettings>().getDdLibraryType(); if (ddlib == storm::dd::DdType::CUDD) { - buildAndCheckSymbolicModelWithSymbolicEngine<storm::dd::DdType::CUDD>(engine == storm::settings::modules::CoreSettings::Engine::Hybrid, program, formulas, onlyInitialStatesRelevant); + buildAndCheckSymbolicModelWithSymbolicEngine<storm::dd::DdType::CUDD>(engine == storm::settings::modules::CoreSettings::Engine::Hybrid, model, formulas, onlyInitialStatesRelevant); } else { - buildAndCheckSymbolicModelWithSymbolicEngine<storm::dd::DdType::Sylvan>(engine == storm::settings::modules::CoreSettings::Engine::Hybrid, program, formulas, onlyInitialStatesRelevant); + buildAndCheckSymbolicModelWithSymbolicEngine<storm::dd::DdType::Sylvan>(engine == storm::settings::modules::CoreSettings::Engine::Hybrid, model, formulas, onlyInitialStatesRelevant); } } else { STORM_LOG_THROW(engine == storm::settings::modules::CoreSettings::Engine::Sparse, storm::exceptions::InvalidSettingsException, "Illegal engine."); - - buildAndCheckSymbolicModelWithSparseEngine<ValueType>(program, formulas, onlyInitialStatesRelevant); + buildAndCheckSymbolicModelWithSparseEngine<ValueType>(model, formulas, onlyInitialStatesRelevant); } } } #ifdef STORM_HAVE_CARL template<> - void buildAndCheckSymbolicModel<storm::RationalNumber>(storm::prism::Program const& program, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, bool onlyInitialStatesRelevant) { + void buildAndCheckSymbolicModel<storm::RationalNumber>(storm::storage::SymbolicModelDescription const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, bool onlyInitialStatesRelevant) { STORM_LOG_THROW(storm::settings::getModule<storm::settings::modules::CoreSettings>().getEngine() == storm::settings::modules::CoreSettings::Engine::Sparse, storm::exceptions::InvalidSettingsException, "Cannot use this data type with an engine different than the sparse one."); - buildAndCheckSymbolicModelWithSparseEngine<storm::RationalNumber>(program, formulas, onlyInitialStatesRelevant); + buildAndCheckSymbolicModelWithSparseEngine<storm::RationalNumber>(model, formulas, onlyInitialStatesRelevant); } template<> - void buildAndCheckSymbolicModel<storm::RationalFunction>(storm::prism::Program const& program, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, bool onlyInitialStatesRelevant) { + void buildAndCheckSymbolicModel<storm::RationalFunction>(storm::storage::SymbolicModelDescription const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, bool onlyInitialStatesRelevant) { STORM_LOG_THROW(storm::settings::getModule<storm::settings::modules::CoreSettings>().getEngine() == storm::settings::modules::CoreSettings::Engine::Sparse, storm::exceptions::InvalidSettingsException, "Cannot use this data type with an engine different than the sparse one."); - buildAndCheckSymbolicModelWithSparseEngine<storm::RationalFunction>(program, formulas, onlyInitialStatesRelevant); + buildAndCheckSymbolicModelWithSparseEngine<storm::RationalFunction>(model, formulas, onlyInitialStatesRelevant); } #endif diff --git a/src/counterexamples/SMTMinimalCommandSetGenerator.h b/src/counterexamples/SMTMinimalCommandSetGenerator.h index 44ebb71ec..64665a8fc 100644 --- a/src/counterexamples/SMTMinimalCommandSetGenerator.h +++ b/src/counterexamples/SMTMinimalCommandSetGenerator.h @@ -628,7 +628,7 @@ namespace storm { } // Construct an expression that exactly characterizes the initial state. - storm::expressions::Expression initialStateExpression = program.getInitialConstruct().getInitialStatesExpression(); + storm::expressions::Expression initialStateExpression = program.getInitialStatesExpression(); // Store the found implications in a container similar to the preceding label sets. std::map<boost::container::flat_set<uint_fast64_t>, std::set<boost::container::flat_set<uint_fast64_t>>> backwardImplications; diff --git a/src/generator/JaniNextStateGenerator.cpp b/src/generator/JaniNextStateGenerator.cpp index cfb08fb79..5b5f54151 100644 --- a/src/generator/JaniNextStateGenerator.cpp +++ b/src/generator/JaniNextStateGenerator.cpp @@ -11,6 +11,7 @@ #include "src/utility/solver.h" #include "src/exceptions/InvalidSettingsException.h" #include "src/exceptions/WrongFormatException.h" +#include "src/exceptions/InvalidArgumentException.h" namespace storm { namespace generator { @@ -21,11 +22,39 @@ namespace storm { } template<typename ValueType, typename StateType> - JaniNextStateGenerator<ValueType, StateType>::JaniNextStateGenerator(storm::jani::Model const& model, NextStateGeneratorOptions const& options, bool flag) : NextStateGenerator<ValueType, StateType>(model.getExpressionManager(), VariableInformation(model), options), model(model) { + JaniNextStateGenerator<ValueType, StateType>::JaniNextStateGenerator(storm::jani::Model const& model, NextStateGeneratorOptions const& options, bool flag) : NextStateGenerator<ValueType, StateType>(model.getExpressionManager(), VariableInformation(model), options), model(model), rewardVariables() { STORM_LOG_THROW(model.hasDefaultComposition(), storm::exceptions::WrongFormatException, "The explicit next-state generator currently does not support custom system compositions."); - STORM_LOG_THROW(!this->options.isBuildAllRewardModelsSet() && this->options.getRewardModelNames().empty(), storm::exceptions::InvalidSettingsException, "The explicit next-state generator currently does not support building reward models."); + STORM_LOG_THROW(!model.hasNonGlobalTransientVariable(), storm::exceptions::InvalidSettingsException, "The explicit next-state generator currently does not support automata-local transient variables."); STORM_LOG_THROW(!this->options.isBuildChoiceLabelsSet(), storm::exceptions::InvalidSettingsException, "JANI next-state generator cannot generate choice labels."); + if (this->options.isBuildAllRewardModelsSet()) { + for (auto const& variable : model.getGlobalVariables()) { + if (variable.isTransient()) { + rewardVariables.push_back(variable.getExpressionVariable()); + } + } + } else { + // Extract the reward models from the program based on the names we were given. + auto const& globalVariables = model.getGlobalVariables(); + for (auto const& rewardModelName : this->options.getRewardModelNames()) { + if (globalVariables.hasVariable(rewardModelName)) { + rewardVariables.push_back(globalVariables.getVariable(rewardModelName).getExpressionVariable()); + } else { + STORM_LOG_THROW(rewardModelName.empty(), storm::exceptions::InvalidArgumentException, "Cannot build unknown reward model '" << rewardModelName << "'."); + STORM_LOG_THROW(globalVariables.getNumberOfTransientVariables() == 1, storm::exceptions::InvalidArgumentException, "Reference to standard reward model is ambiguous."); + } + } + + // If no reward model was yet added, but there was one that was given in the options, we try to build the + // standard reward model. + if (rewardVariables.empty() && !this->options.getRewardModelNames().empty()) { + rewardVariables.push_back(globalVariables.getTransientVariables().front()->getExpressionVariable()); + } + } + + // Build the information structs for the reward models. + buildRewardModelInformation(); + // If there are terminal states we need to handle, we now need to translate all labels to expressions. if (this->options.hasTerminalStates()) { for (auto const& expressionOrLabelAndBool : this->options.getTerminalStates()) { @@ -161,6 +190,9 @@ namespace storm { } // Block the current initial state to search for the next one. + if (!blockingExpression.isInitialized()) { + break; + } solver->add(blockingExpression); } @@ -171,13 +203,8 @@ namespace storm { CompressedState JaniNextStateGenerator<ValueType, StateType>::applyUpdate(CompressedState const& state, storm::jani::EdgeDestination const& destination) { CompressedState newState(state); - // NOTE: the following process assumes that the assignments of the destination are ordered in such a way - // that the assignments to boolean variables precede the assignments to all integer variables and that - // within the types, the assignments to variables are ordered (in ascending order) by the expression variables. - // This is guaranteed for JANI models, by sorting the assignments as soon as an edge destination is created. - - auto assignmentIt = destination.getAssignments().begin(); - auto assignmentIte = destination.getAssignments().end(); + auto assignmentIt = destination.getNonTransientAssignments().begin(); + auto assignmentIte = destination.getNonTransientAssignments().end(); // Iterate over all boolean assignments and carry them out. auto boolIt = this->variableInformation.booleanVariables.begin(); @@ -211,6 +238,22 @@ namespace storm { // Prepare the result, in case we return early. StateBehavior<ValueType, StateType> result; + // Retrieve the locations from the state. + std::vector<uint64_t> locations = getLocations(*this->state); + + // First, construct the state rewards, as we may return early if there are no choices later and we already + // need the state rewards then. + std::vector<ValueType> stateRewards(this->rewardVariables.size(), storm::utility::zero<ValueType>()); + uint64_t automatonIndex = 0; + for (auto const& automaton : model.getAutomata()) { + uint64_t currentLocationIndex = locations[automatonIndex]; + storm::jani::Location const& location = automaton.getLocation(currentLocationIndex); + auto valueIt = stateRewards.begin(); + performTransientAssignments(location.getAssignments().getTransientAssignments(), [&valueIt] (ValueType const& value) { *valueIt += value; ++valueIt; } ); + ++automatonIndex; + } + result.addStateRewards(std::move(stateRewards)); + // If a terminal expression was set and we must not expand this state, return now. if (!this->terminalStates.empty()) { for (auto const& expressionBool : this->terminalStates) { @@ -220,9 +263,6 @@ namespace storm { } } - // Retrieve the locations from the state. - std::vector<uint64_t> locations = getLocations(*this->state); - // Get all choices for the state. std::vector<Choice<ValueType>> allChoices = getSilentActionChoices(locations, *this->state, stateToIdCallback); std::vector<Choice<ValueType>> allLabeledChoices = getNonsilentActionChoices(locations, *this->state, stateToIdCallback); @@ -308,6 +348,9 @@ namespace storm { probabilitySum += probability; } + // Create the state-action reward for the newly created choice. + performTransientAssignments(edge.getAssignments().getTransientAssignments(), [&choice] (ValueType const& value) { choice.addChoiceReward(value); } ); + // Check that the resulting distribution is in fact a distribution. STORM_LOG_THROW(!this->isDiscreteTimeModel() || this->comparator.isOne(probabilitySum), storm::exceptions::WrongFormatException, "Probabilities do not sum to one for edge (actually sum to " << probabilitySum << ")."); } @@ -477,13 +520,12 @@ namespace storm { template<typename ValueType, typename StateType> std::size_t JaniNextStateGenerator<ValueType, StateType>::getNumberOfRewardModels() const { - return 0; + return rewardVariables.size(); } template<typename ValueType, typename StateType> RewardModelInformation JaniNextStateGenerator<ValueType, StateType>::getRewardModelInformation(uint64_t const& index) const { - STORM_LOG_THROW(false, storm::exceptions::InvalidSettingsException, "Cannot retrieve reward model information."); - return RewardModelInformation("", false, false, false); + return rewardModelInformation[index]; } template<typename ValueType, typename StateType> @@ -491,6 +533,81 @@ namespace storm { return NextStateGenerator<ValueType, StateType>::label(states, initialStateIndices, deadlockStateIndices, {}); } + template<typename ValueType, typename StateType> + void JaniNextStateGenerator<ValueType, StateType>::performTransientAssignments(storm::jani::detail::ConstAssignments const& transientAssignments, std::function<void (ValueType const&)> const& callback) { + // If there are no reward variables, there is no need to iterate at all. + if (rewardVariables.empty()) { + return; + } + + // Otherwise, perform the callback for all selected reward variables. + auto rewardVariableIt = rewardVariables.begin(); + auto rewardVariableIte = rewardVariables.end(); + for (auto const& assignment : transientAssignments) { + while (rewardVariableIt != rewardVariableIte && *rewardVariableIt < assignment.getExpressionVariable()) { + callback(storm::utility::zero<ValueType>()); + ++rewardVariableIt; + } + if (rewardVariableIt == rewardVariableIte) { + break; + } else if (*rewardVariableIt == assignment.getExpressionVariable()) { + callback(ValueType(this->evaluator.asRational(assignment.getAssignedExpression()))); + ++rewardVariableIt; + } + } + // Add a value of zero for all variables that have no assignment. + for (; rewardVariableIt != rewardVariableIte; ++rewardVariableIt) { + callback(storm::utility::zero<ValueType>()); + } + } + + template<typename ValueType, typename StateType> + void JaniNextStateGenerator<ValueType, StateType>::buildRewardModelInformation() { + // Prepare all reward model information structs. + for (auto const& variable : rewardVariables) { + rewardModelInformation.emplace_back(variable.getName(), false, false, false); + } + + // Then fill them. + for (auto const& automaton : model.getAutomata()) { + for (auto const& location : automaton.getLocations()) { + auto rewardVariableIt = rewardVariables.begin(); + auto rewardVariableIte = rewardVariables.end(); + + for (auto const& assignment : location.getAssignments().getTransientAssignments()) { + while (rewardVariableIt != rewardVariableIte && *rewardVariableIt < assignment.getExpressionVariable()) { + ++rewardVariableIt; + } + if (rewardVariableIt == rewardVariableIte) { + break; + } + if (*rewardVariableIt == assignment.getExpressionVariable()) { + rewardModelInformation[std::distance(rewardVariables.begin(), rewardVariableIt)].setHasStateRewards(); + ++rewardVariableIt; + } + } + } + + for (auto const& edge : automaton.getEdges()) { + auto rewardVariableIt = rewardVariables.begin(); + auto rewardVariableIte = rewardVariables.end(); + + for (auto const& assignment : edge.getAssignments().getTransientAssignments()) { + while (rewardVariableIt != rewardVariableIte && *rewardVariableIt < assignment.getExpressionVariable()) { + ++rewardVariableIt; + } + if (rewardVariableIt == rewardVariableIte) { + break; + } + if (*rewardVariableIt == assignment.getExpressionVariable()) { + rewardModelInformation[std::distance(rewardVariables.begin(), rewardVariableIt)].setHasStateActionRewards(); + ++rewardVariableIt; + } + } + } + } + } + template class JaniNextStateGenerator<double>; #ifdef STORM_HAVE_CARL diff --git a/src/generator/JaniNextStateGenerator.h b/src/generator/JaniNextStateGenerator.h index 316d6e074..d9932f60a 100644 --- a/src/generator/JaniNextStateGenerator.h +++ b/src/generator/JaniNextStateGenerator.h @@ -89,8 +89,25 @@ namespace storm { */ void checkGlobalVariableWritesValid(std::vector<std::vector<storm::jani::Edge const*>> const& enabledEdges) const; + /*! + * Treats the given transient assignments by calling the callback function whenever a transient assignment + * to one of the reward variables of this generator is performed. + */ + void performTransientAssignments(storm::jani::detail::ConstAssignments const& transientAssignments, std::function<void (ValueType const&)> const& callback); + + /*! + * Builds the information structs for the reward models. + */ + void buildRewardModelInformation(); + /// The model used for the generation of next states. storm::jani::Model model; + + /// The transient variables of reward models that need to be considered. + std::vector<storm::expressions::Variable> rewardVariables; + + /// A vector storing information about the corresponding reward models (variables). + std::vector<RewardModelInformation> rewardModelInformation; }; } diff --git a/src/generator/NextStateGenerator.cpp b/src/generator/NextStateGenerator.cpp index 9c1eefbd5..fccf7b783 100644 --- a/src/generator/NextStateGenerator.cpp +++ b/src/generator/NextStateGenerator.cpp @@ -211,6 +211,18 @@ namespace storm { return transitionRewards; } + void RewardModelInformation::setHasStateRewards() { + stateRewards = true; + } + + void RewardModelInformation::setHasStateActionRewards() { + stateActionRewards = true; + } + + void RewardModelInformation::setHasTransitionRewards() { + transitionRewards = true; + } + template<typename ValueType, typename StateType> NextStateGenerator<ValueType, StateType>::NextStateGenerator(storm::expressions::ExpressionManager const& expressionManager, VariableInformation const& variableInformation, NextStateGeneratorOptions const& options) : options(options), expressionManager(expressionManager.getSharedPointer()), variableInformation(variableInformation), evaluator(expressionManager), state(nullptr) { // Intentionally left empty. diff --git a/src/generator/NextStateGenerator.h b/src/generator/NextStateGenerator.h index 30a4157cd..553e7871d 100644 --- a/src/generator/NextStateGenerator.h +++ b/src/generator/NextStateGenerator.h @@ -148,6 +148,9 @@ namespace storm { bool hasStateActionRewards() const; bool hasTransitionRewards() const; + void setHasStateRewards(); + void setHasStateActionRewards(); + void setHasTransitionRewards(); private: std::string name; bool stateRewards; diff --git a/src/generator/PrismNextStateGenerator.cpp b/src/generator/PrismNextStateGenerator.cpp index c0026a98d..1ecbec2ab 100644 --- a/src/generator/PrismNextStateGenerator.cpp +++ b/src/generator/PrismNextStateGenerator.cpp @@ -23,8 +23,9 @@ namespace storm { template<typename ValueType, typename StateType> PrismNextStateGenerator<ValueType, StateType>::PrismNextStateGenerator(storm::prism::Program const& program, NextStateGeneratorOptions const& options, bool flag) : NextStateGenerator<ValueType, StateType>(program.getManager(), options), program(program), rewardModels() { + STORM_LOG_TRACE("Creating next-state generator for PRISM program: " << program); STORM_LOG_THROW(!this->program.specifiesSystemComposition(), storm::exceptions::WrongFormatException, "The explicit next-state generator currently does not support custom system compositions."); - + // Only after checking validity of the program, we initialize the variable information. this->checkValid(program); this->variableInformation = VariableInformation(program); @@ -41,11 +42,10 @@ namespace storm { } else { STORM_LOG_THROW(rewardModelName.empty(), storm::exceptions::InvalidArgumentException, "Cannot build unknown reward model '" << rewardModelName << "'."); STORM_LOG_THROW(this->program.getNumberOfRewardModels() == 1, storm::exceptions::InvalidArgumentException, "Reference to standard reward model is ambiguous."); - STORM_LOG_THROW(this->program.getNumberOfRewardModels() > 0, storm::exceptions::InvalidArgumentException, "Reference to standard reward model is invalid, because there is no reward model."); } } - // If no reward model was yet added, but there was one that was given in the options, we try to build + // If no reward model was yet added, but there was one that was given in the options, we try to build the // standard reward model. if (rewardModels.empty() && !this->options.getRewardModelNames().empty()) { rewardModels.push_back(this->program.getRewardModel(0)); @@ -136,7 +136,7 @@ namespace storm { for (auto const& expression : rangeExpressions) { solver->add(expression); } - solver->add(program.getInitialConstruct().getInitialStatesExpression()); + solver->add(program.getInitialStatesExpression()); // Proceed ss long as the solver can still enumerate initial states. std::vector<StateType> initialStateIndices; @@ -166,6 +166,9 @@ namespace storm { initialStateIndices.push_back(id); // Block the current initial state to search for the next one. + if (!blockingExpression.isInitialized()) { + break; + } solver->add(blockingExpression); } @@ -314,7 +317,7 @@ namespace storm { template<typename ValueType, typename StateType> boost::optional<std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>>> PrismNextStateGenerator<ValueType, StateType>::getActiveCommandsByActionIndex(uint_fast64_t const& actionIndex) { boost::optional<std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>>> result((std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>>())); - + // Iterate over all modules. for (uint_fast64_t i = 0; i < program.getNumberOfModules(); ++i) { storm::prism::Module const& module = program.getModule(i); @@ -445,7 +448,6 @@ namespace storm { for (uint_fast64_t i = 0; i < iteratorList.size(); ++i) { storm::prism::Command const& command = *iteratorList[i]; - for (uint_fast64_t j = 0; j < command.getNumberOfUpdates(); ++j) { storm::prism::Update const& update = command.getUpdate(j); diff --git a/src/generator/StateBehavior.cpp b/src/generator/StateBehavior.cpp index 4e71c537f..8ad2c1e91 100644 --- a/src/generator/StateBehavior.cpp +++ b/src/generator/StateBehavior.cpp @@ -20,6 +20,11 @@ namespace storm { stateRewards.push_back(stateReward); } + template<typename ValueType, typename StateType> + void StateBehavior<ValueType, StateType>::addStateRewards(std::vector<ValueType>&& stateRewards) { + this->stateRewards = std::move(stateRewards); + } + template<typename ValueType, typename StateType> void StateBehavior<ValueType, StateType>::setExpanded(bool newValue) { this->expanded = newValue; diff --git a/src/generator/StateBehavior.h b/src/generator/StateBehavior.h index 8f81c8b30..1082e9045 100644 --- a/src/generator/StateBehavior.h +++ b/src/generator/StateBehavior.h @@ -25,7 +25,12 @@ namespace storm { * Adds the given state reward to the behavior of the state. */ void addStateReward(ValueType const& stateReward); - + + /*! + * Adds the given state rewards to the behavior of the state. + */ + void addStateRewards(std::vector<ValueType>&& stateRewards); + /*! * Sets whether the state was expanded. */ diff --git a/src/modelchecker/AbstractModelChecker.cpp b/src/modelchecker/AbstractModelChecker.cpp index 616dcf828..660906acb 100644 --- a/src/modelchecker/AbstractModelChecker.cpp +++ b/src/modelchecker/AbstractModelChecker.cpp @@ -17,6 +17,7 @@ #include "src/models/symbolic/Mdp.h" #include "src/models/sparse/MarkovAutomaton.h" #include "src/models/sparse/StandardRewardModel.h" +#include "src/models/symbolic/StandardRewardModel.h" #include "src/storage/dd/Add.h" #include "src/storage/dd/Bdd.h" diff --git a/src/modelchecker/prctl/helper/SymbolicDtmcPrctlHelper.cpp b/src/modelchecker/prctl/helper/SymbolicDtmcPrctlHelper.cpp index b5adb7e15..6266fbb73 100644 --- a/src/modelchecker/prctl/helper/SymbolicDtmcPrctlHelper.cpp +++ b/src/modelchecker/prctl/helper/SymbolicDtmcPrctlHelper.cpp @@ -172,7 +172,7 @@ namespace storm { // for solving the equation system (i.e. compute (I-A)). submatrix *= maybeStatesAdd.swapVariables(model.getRowColumnMetaVariablePairs()); submatrix = (model.getRowColumnIdentity() * maybeStatesAdd) - submatrix; - + // Solve the equation system. std::unique_ptr<storm::solver::SymbolicLinearEquationSolver<DdType, ValueType>> solver = linearEquationSolverFactory.create(submatrix, maybeStates, model.getRowVariables(), model.getColumnVariables(), model.getRowColumnMetaVariablePairs()); storm::dd::Add<DdType, ValueType> result = solver->solveEquations(model.getManager().getConstant(0.5) * maybeStatesAdd, subvector); diff --git a/src/parser/ExpressionParser.cpp b/src/parser/ExpressionParser.cpp index 20982bd20..3054f2fe0 100644 --- a/src/parser/ExpressionParser.cpp +++ b/src/parser/ExpressionParser.cpp @@ -3,6 +3,34 @@ #include "src/exceptions/InvalidTypeException.h" #include "src/exceptions/WrongFormatException.h" +#include "src/utility/constants.h" + +namespace boost { + namespace spirit { + namespace traits { + template<> + bool scale(int exp, storm::RationalNumber& r, storm::RationalNumber acc) { + if (exp >= 0) { + r = acc * storm::utility::pow(storm::RationalNumber(10), static_cast<uint_fast64_t>(exp)); + } else { + r = acc / storm::utility::pow(storm::RationalNumber(10), static_cast<uint_fast64_t>(-exp)); + } + return true; + } + + template<> + bool is_equal_to_one(storm::RationalNumber const& value) { + return storm::utility::isOne(value); + } + + template<> + storm::RationalNumber negate(bool neg, storm::RationalNumber const& number) { + return neg ? storm::RationalNumber(-number) : number; + } + } + } +} + namespace storm { namespace parser { ExpressionParser::ExpressionParser(storm::expressions::ExpressionManager const& manager, qi::symbols<char, uint_fast64_t> const& invalidIdentifiers_, bool enableErrorHandling, bool allowBacktracking) : ExpressionParser::base_type(expression), orOperator_(), andOperator_(), equalityOperator_(), relationalOperator_(), plusOperator_(), multiplicationOperator_(), infixPowerOperator_(), unaryOperator_(), floorCeilOperator_(), minMaxOperator_(), prefixPowerOperator_(), trueFalse_(manager), manager(manager.getSharedPointer()), createExpressions(false), acceptDoubleLiterals(true), identifiers_(nullptr), invalidIdentifiers_(invalidIdentifiers_) { @@ -33,7 +61,7 @@ namespace storm { identifierExpression = identifier[qi::_val = phoenix::bind(&ExpressionParser::getIdentifierExpression, phoenix::ref(*this), qi::_1, allowBacktracking, qi::_pass)]; identifierExpression.name("identifier expression"); - literalExpression = trueFalse_[qi::_val = qi::_1] | strict_double[qi::_val = phoenix::bind(&ExpressionParser::createDoubleLiteralExpression, phoenix::ref(*this), qi::_1, qi::_pass)] | qi::int_[qi::_val = phoenix::bind(&ExpressionParser::createIntegerLiteralExpression, phoenix::ref(*this), qi::_1, qi::_pass)]; + literalExpression = trueFalse_[qi::_val = qi::_1] | rationalLiteral_[qi::_val = phoenix::bind(&ExpressionParser::createRationalLiteralExpression, phoenix::ref(*this), qi::_1, qi::_pass)] | qi::int_[qi::_val = phoenix::bind(&ExpressionParser::createIntegerLiteralExpression, phoenix::ref(*this), qi::_1, qi::_pass)]; literalExpression.name("literal expression"); atomicExpression = floorCeilExpression | prefixPowerExpression | minMaxExpression | (qi::lit("(") >> expression >> qi::lit(")")) | literalExpression | identifierExpression; @@ -295,7 +323,7 @@ namespace storm { return manager->boolean(false); } - storm::expressions::Expression ExpressionParser::createDoubleLiteralExpression(double value, bool& pass) const { + storm::expressions::Expression ExpressionParser::createRationalLiteralExpression(storm::RationalNumber const& value, bool& pass) const { // If we are not supposed to accept double expressions, we reject it by setting pass to false. if (!this->acceptDoubleLiterals) { pass = false; diff --git a/src/parser/ExpressionParser.h b/src/parser/ExpressionParser.h index 66d851707..077be8db2 100644 --- a/src/parser/ExpressionParser.h +++ b/src/parser/ExpressionParser.h @@ -8,8 +8,20 @@ #include "src/storage/expressions/Expression.h" #include "src/storage/expressions/ExpressionManager.h" +#include "src/adapters/CarlAdapter.h" + namespace storm { namespace parser { + template<typename NumberType> + struct RationalPolicies : boost::spirit::qi::strict_real_policies<NumberType> { + static const bool expect_dot = true; + + template <typename It, typename Attr> + static bool parse_nan(It&, It const&, Attr&) { return false; } + template <typename It, typename Attr> + static bool parse_inf(It&, It const&, Attr&) { return false; } + }; + class ExpressionParser : public qi::grammar<Iterator, storm::expressions::Expression(), Skipper> { public: /*! @@ -236,7 +248,7 @@ namespace storm { qi::rule<Iterator, std::string(), Skipper> identifier; // Parser that is used to recognize doubles only (as opposed to Spirit's double_ parser). - boost::spirit::qi::real_parser<double, boost::spirit::qi::strict_real_policies<double>> strict_double; + boost::spirit::qi::real_parser<storm::RationalNumber, RationalPolicies<storm::RationalNumber>> rationalLiteral_; // Helper functions to create expressions. storm::expressions::Expression createIteExpression(storm::expressions::Expression e1, storm::expressions::Expression e2, storm::expressions::Expression e3, bool& pass) const; @@ -248,7 +260,7 @@ namespace storm { storm::expressions::Expression createMultExpression(storm::expressions::Expression const& e1, storm::expressions::OperatorType const& operatorType, storm::expressions::Expression const& e2, bool& pass) const; storm::expressions::Expression createPowerExpression(storm::expressions::Expression const& e1, storm::expressions::OperatorType const& operatorType, storm::expressions::Expression const& e2, bool& pass) const; storm::expressions::Expression createUnaryExpression(boost::optional<storm::expressions::OperatorType> const& operatorType, storm::expressions::Expression const& e1, bool& pass) const; - storm::expressions::Expression createDoubleLiteralExpression(double value, bool& pass) const; + storm::expressions::Expression createRationalLiteralExpression(storm::RationalNumber const& value, bool& pass) const; storm::expressions::Expression createIntegerLiteralExpression(int value, bool& pass) const; storm::expressions::Expression createMinimumMaximumExpression(storm::expressions::Expression const& e1, storm::expressions::OperatorType const& operatorType, storm::expressions::Expression const& e2, bool& pass) const; storm::expressions::Expression createFloorCeilExpression(storm::expressions::OperatorType const& operatorType, storm::expressions::Expression const& e1, bool& pass) const; diff --git a/src/parser/FormulaParser.cpp b/src/parser/FormulaParser.cpp index 74dbf31cf..f6aaf82a4 100644 --- a/src/parser/FormulaParser.cpp +++ b/src/parser/FormulaParser.cpp @@ -4,6 +4,9 @@ #include "src/parser/SpiritErrorHandler.h" +#include "src/storage/prism/Program.h" +#include "src/storage/jani/Model.h" + // If the parser fails due to ill-formed data, this exception is thrown. #include "src/exceptions/WrongFormatException.h" @@ -176,7 +179,7 @@ namespace storm { phoenix::function<SpiritErrorHandler> handler; }; - FormulaParser::FormulaParser(std::shared_ptr<storm::expressions::ExpressionManager const> const& manager) : manager(manager->getSharedPointer()), grammar(new FormulaParserGrammar(manager)) { + FormulaParser::FormulaParser(std::shared_ptr<storm::expressions::ExpressionManager const> const& manager) : manager(manager), grammar(new FormulaParserGrammar(manager)) { // Intentionally left empty. } diff --git a/src/parser/FormulaParser.h b/src/parser/FormulaParser.h index 86570c4f6..f69fa6eb2 100644 --- a/src/parser/FormulaParser.h +++ b/src/parser/FormulaParser.h @@ -9,9 +9,11 @@ #include "src/storage/expressions/Expression.h" #include "src/utility/macros.h" -#include "src/storage/prism/Program.h" - namespace storm { + namespace prism { + class Program; + } + namespace parser { // Forward-declare grammar. diff --git a/src/parser/JaniParser.cpp b/src/parser/JaniParser.cpp index 806a26a1f..b4d67fce2 100644 --- a/src/parser/JaniParser.cpp +++ b/src/parser/JaniParser.cpp @@ -571,7 +571,7 @@ namespace storm { STORM_LOG_THROW(transientValueEntry.count("ref") == 1, storm::exceptions::InvalidJaniException, "Transient values in location " << locName << " need exactly one ref that is assigned to"); STORM_LOG_THROW(transientValueEntry.count("value") == 1, storm::exceptions::InvalidJaniException, "Transient values in location " << locName << " need exactly one assigned value"); storm::jani::Variable const& lhs = getLValue(transientValueEntry.at("ref"), parentModel.getGlobalVariables(), automaton.getVariables(), "LHS of assignment in location " + locName + " (automaton '" + name + "')"); - STORM_LOG_THROW(lhs.isTransientVariable(), storm::exceptions::InvalidJaniException, "Assigned non-transient variable " + lhs.getName() + " in location " + locName + " (automaton: '" + name + "')"); + STORM_LOG_THROW(lhs.isTransient(), storm::exceptions::InvalidJaniException, "Assigned non-transient variable " + lhs.getName() + " in location " + locName + " (automaton: '" + name + "')"); storm::expressions::Expression rhs = parseExpression(transientValueEntry.at("value"), "Assignment of variable " + lhs.getName() + " in location " + locName + " (automaton: '" + name + "')"); transientAssignments.emplace_back(lhs, rhs); } diff --git a/src/parser/JaniParser.h b/src/parser/JaniParser.h index 82165b5ef..80b4ca0d7 100644 --- a/src/parser/JaniParser.h +++ b/src/parser/JaniParser.h @@ -42,7 +42,8 @@ namespace storm { storm::jani::Property parseProperty(json const& propertyStructure); storm::jani::Automaton parseAutomaton(json const& automatonStructure, storm::jani::Model const& parentModel); std::shared_ptr<storm::jani::Variable> parseVariable(json const& variableStructure, std::string const& scopeDescription, bool prefWithScope = false); - storm::expressions::Expression parseExpression(json const& expressionStructure, std::string const& scopeDescription, std::unordered_map<std::string, std::shared_ptr<storm::jani::Variable>> const& localVars = {}); + storm::expressions::Expression parseExpression(json const& expressionStructure, std::string const& scopeDescription, std::unordered_map<std::string, std::shared_ptr<storm::jani::Variable>> const& localVars = std::unordered_map<std::string, std::shared_ptr<storm::jani::Variable>>()); + private: std::shared_ptr<storm::jani::Constant> parseConstant(json const& constantStructure, std::string const& scopeDescription = "global"); diff --git a/src/parser/PrismParser.cpp b/src/parser/PrismParser.cpp index 7d54f18c6..d3e5191e2 100644 --- a/src/parser/PrismParser.cpp +++ b/src/parser/PrismParser.cpp @@ -65,6 +65,8 @@ namespace storm { STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, "Parsing error in line " << lineNumber << " of file " << filename << "."); } + STORM_LOG_TRACE("Parsed PRISM input: " << result); + return result; } @@ -103,10 +105,10 @@ namespace storm { formulaDefinition = (qi::lit("formula") > identifier > qi::lit("=") > expressionParser > qi::lit(";"))[qi::_val = phoenix::bind(&PrismParser::createFormula, phoenix::ref(*this), qi::_1, qi::_2)]; formulaDefinition.name("formula definition"); - booleanVariableDefinition = ((identifier >> qi::lit(":") >> qi::lit("bool")) > ((qi::lit("init") > expressionParser) | qi::attr(manager->boolean(false))) > qi::lit(";"))[qi::_val = phoenix::bind(&PrismParser::createBooleanVariable, phoenix::ref(*this), qi::_1, qi::_2)]; + booleanVariableDefinition = ((identifier >> qi::lit(":") >> qi::lit("bool")) > -((qi::lit("init") > expressionParser[qi::_a = qi::_1]) | qi::attr(manager->boolean(false))) > qi::lit(";"))[qi::_val = phoenix::bind(&PrismParser::createBooleanVariable, phoenix::ref(*this), qi::_1, qi::_a)]; booleanVariableDefinition.name("boolean variable definition"); - integerVariableDefinition = ((identifier >> qi::lit(":") >> qi::lit("[")[phoenix::bind(&PrismParser::allowDoubleLiterals, phoenix::ref(*this), false)]) > expressionParser[qi::_a = qi::_1] > qi::lit("..") > expressionParser > qi::lit("]")[phoenix::bind(&PrismParser::allowDoubleLiterals, phoenix::ref(*this), true)] > -(qi::lit("init") > expressionParser[qi::_a = qi::_1]) > qi::lit(";"))[qi::_val = phoenix::bind(&PrismParser::createIntegerVariable, phoenix::ref(*this), qi::_1, qi::_2, qi::_3, qi::_a)]; + integerVariableDefinition = ((identifier >> qi::lit(":") >> qi::lit("[")[phoenix::bind(&PrismParser::allowDoubleLiterals, phoenix::ref(*this), false)]) > expressionParser > qi::lit("..") > expressionParser > qi::lit("]")[phoenix::bind(&PrismParser::allowDoubleLiterals, phoenix::ref(*this), true)] > -(qi::lit("init") > expressionParser[qi::_a = qi::_1]) > qi::lit(";"))[qi::_val = phoenix::bind(&PrismParser::createIntegerVariable, phoenix::ref(*this), qi::_1, qi::_2, qi::_3, qi::_a)]; integerVariableDefinition.name("integer variable definition"); variableDefinition = (booleanVariableDefinition[phoenix::push_back(qi::_r1, qi::_1)] | integerVariableDefinition[phoenix::push_back(qi::_r2, qi::_1)]); @@ -208,7 +210,7 @@ namespace storm { moduleDefinitionList %= +(moduleRenaming(qi::_r1) | moduleDefinition(qi::_r1))[phoenix::push_back(phoenix::bind(&GlobalProgramInformation::modules, qi::_r1), qi::_1)]; moduleDefinitionList.name("module list"); - start = (qi::eps + start = (qi::eps[phoenix::bind(&PrismParser::removeInitialConstruct, phoenix::ref(*this), qi::_a)] > modelTypeDefinition[phoenix::bind(&GlobalProgramInformation::modelType, qi::_a) = qi::_1] > *(definedConstantDefinition[phoenix::push_back(phoenix::bind(&GlobalProgramInformation::constants, qi::_a), qi::_1)] | undefinedConstantDefinition[phoenix::push_back(phoenix::bind(&GlobalProgramInformation::constants, qi::_a), qi::_1)] @@ -276,7 +278,7 @@ namespace storm { return true; } - bool PrismParser::addInitialStatesConstruct(storm::expressions::Expression initialStatesExpression, GlobalProgramInformation& globalProgramInformation) { + bool PrismParser::addInitialStatesConstruct(storm::expressions::Expression const& initialStatesExpression, GlobalProgramInformation& globalProgramInformation) { STORM_LOG_THROW(!globalProgramInformation.hasInitialConstruct, storm::exceptions::WrongFormatException, "Parsing error in " << this->getFilename() << ", line " << get_line(qi::_3) << ": Program must not define two initial constructs."); if (globalProgramInformation.hasInitialConstruct) { return false; @@ -585,7 +587,7 @@ namespace storm { auto const& renamingPair = renaming.find(variable.getName()); STORM_LOG_THROW(renamingPair != renaming.end(), storm::exceptions::WrongFormatException, "Parsing error in " << this->getFilename() << ", line " << get_line(qi::_3) << ": Boolean variable '" << variable.getName() << " was not renamed."); - booleanVariables.push_back(storm::prism::BooleanVariable(manager->getVariable(renamingPair->second), variable.getInitialValueExpression().substitute(expressionRenaming), this->getFilename(), get_line(qi::_1))); + booleanVariables.push_back(storm::prism::BooleanVariable(manager->getVariable(renamingPair->second), variable.hasInitialValue() ? variable.getInitialValueExpression().substitute(expressionRenaming) : variable.getInitialValueExpression(), this->getFilename(), get_line(qi::_1))); } // Rename the integer variables. @@ -594,7 +596,7 @@ namespace storm { auto const& renamingPair = renaming.find(variable.getName()); STORM_LOG_THROW(renamingPair != renaming.end(), storm::exceptions::WrongFormatException, "Parsing error in " << this->getFilename() << ", line " << get_line(qi::_3) << ": Integer variable '" << variable.getName() << " was not renamed."); - integerVariables.push_back(storm::prism::IntegerVariable(manager->getVariable(renamingPair->second), variable.getLowerBoundExpression().substitute(expressionRenaming), variable.getUpperBoundExpression().substitute(expressionRenaming), variable.getInitialValueExpression().substitute(expressionRenaming), this->getFilename(), get_line(qi::_1))); + integerVariables.push_back(storm::prism::IntegerVariable(manager->getVariable(renamingPair->second), variable.getLowerBoundExpression().substitute(expressionRenaming), variable.getUpperBoundExpression().substitute(expressionRenaming), variable.hasInitialValue() ? variable.getInitialValueExpression().substitute(expressionRenaming) : variable.getInitialValueExpression(), this->getFilename(), get_line(qi::_1))); } // Rename commands. @@ -640,7 +642,11 @@ namespace storm { } storm::prism::Program PrismParser::createProgram(GlobalProgramInformation const& globalProgramInformation) const { - return storm::prism::Program(manager, globalProgramInformation.modelType, globalProgramInformation.constants, globalProgramInformation.globalBooleanVariables, globalProgramInformation.globalIntegerVariables, globalProgramInformation.formulas, globalProgramInformation.modules, globalProgramInformation.actionIndices, globalProgramInformation.rewardModels, globalProgramInformation.labels, secondRun && !globalProgramInformation.hasInitialConstruct ? boost::none : boost::optional<storm::prism::InitialConstruct>(storm::prism::InitialConstruct(manager->boolean(false))), globalProgramInformation.systemCompositionConstruct, this->getFilename(), 1, this->secondRun); + return storm::prism::Program(manager, globalProgramInformation.modelType, globalProgramInformation.constants, globalProgramInformation.globalBooleanVariables, globalProgramInformation.globalIntegerVariables, globalProgramInformation.formulas, globalProgramInformation.modules, globalProgramInformation.actionIndices, globalProgramInformation.rewardModels, globalProgramInformation.labels, secondRun && !globalProgramInformation.hasInitialConstruct ? boost::none : boost::make_optional(globalProgramInformation.initialConstruct), globalProgramInformation.systemCompositionConstruct, this->getFilename(), 1, this->secondRun); + } + + void PrismParser::removeInitialConstruct(GlobalProgramInformation& globalProgramInformation) const { + globalProgramInformation.hasInitialConstruct = false; } } // namespace parser } // namespace storm diff --git a/src/parser/PrismParser.h b/src/parser/PrismParser.h index 84a5b601e..2a83e3193 100644 --- a/src/parser/PrismParser.h +++ b/src/parser/PrismParser.h @@ -188,7 +188,7 @@ namespace storm { // Rules for variable definitions. qi::rule<Iterator, qi::unused_type(std::vector<storm::prism::BooleanVariable>&, std::vector<storm::prism::IntegerVariable>&), Skipper> variableDefinition; - qi::rule<Iterator, storm::prism::BooleanVariable(), Skipper> booleanVariableDefinition; + qi::rule<Iterator, storm::prism::BooleanVariable(), qi::locals<storm::expressions::Expression>, Skipper> booleanVariableDefinition; qi::rule<Iterator, storm::prism::IntegerVariable(), qi::locals<storm::expressions::Expression>, Skipper> integerVariableDefinition; // Rules for command definitions. @@ -241,7 +241,7 @@ namespace storm { // Helper methods used in the grammar. bool isValidIdentifier(std::string const& identifier); - bool addInitialStatesConstruct(storm::expressions::Expression initialStatesExpression, GlobalProgramInformation& globalProgramInformation); + bool addInitialStatesConstruct(storm::expressions::Expression const& initialStatesExpression, GlobalProgramInformation& globalProgramInformation); bool addSystemCompositionConstruct(std::shared_ptr<storm::prism::Composition> const& composition, GlobalProgramInformation& globalProgramInformation); @@ -274,6 +274,8 @@ namespace storm { storm::prism::Module createRenamedModule(std::string const& newModuleName, std::string const& oldModuleName, std::map<std::string, std::string> const& renaming, GlobalProgramInformation& globalProgramInformation) const; storm::prism::Program createProgram(GlobalProgramInformation const& globalProgramInformation) const; + void removeInitialConstruct(GlobalProgramInformation& globalProgramInformation) const; + // An error handler function. phoenix::function<SpiritErrorHandler> handler; }; diff --git a/src/settings/modules/CoreSettings.cpp b/src/settings/modules/CoreSettings.cpp index b42a301c8..5a6dfc9c0 100644 --- a/src/settings/modules/CoreSettings.cpp +++ b/src/settings/modules/CoreSettings.cpp @@ -30,7 +30,6 @@ namespace storm { const std::string CoreSettings::engineOptionShortName = "e"; const std::string CoreSettings::ddLibraryOptionName = "ddlib"; const std::string CoreSettings::cudaOptionName = "cuda"; - const std::string CoreSettings::minMaxEquationSolvingTechniqueOptionName = "ndmethod"; CoreSettings::CoreSettings() : ModuleSettings(moduleName), engine(CoreSettings::Engine::Sparse) { this->addOption(storm::settings::OptionBuilder(moduleName, counterexampleOptionName, false, "Generates a counterexample for the given PRCTL formulas if not satisfied by the model") @@ -57,10 +56,6 @@ namespace storm { .addArgument(storm::settings::ArgumentBuilder::createStringArgument("name", "The name of an SMT solver. Available are: z3 and mathsat.").addValidationFunctionString(storm::settings::ArgumentValidators::stringInListValidator(smtSolvers)).setDefaultValueString("z3").build()).build()); this->addOption(storm::settings::OptionBuilder(moduleName, statisticsOptionName, false, "Sets whether to display statistics if available.").setShortName(statisticsOptionShortName).build()); this->addOption(storm::settings::OptionBuilder(moduleName, cudaOptionName, false, "Sets whether to use CUDA to speed up computation time.").build()); - - std::vector<std::string> minMaxSolvingTechniques = {"vi", "value-iteration", "pi", "policy-iteration"}; - this->addOption(storm::settings::OptionBuilder(moduleName, minMaxEquationSolvingTechniqueOptionName, false, "Sets which min/max linear equation solving technique is preferred.") - .addArgument(storm::settings::ArgumentBuilder::createStringArgument("name", "The name of a min/max linear equation solving technique. Available are: value-iteration (vi) and policy-iteration (pi).").addValidationFunctionString(storm::settings::ArgumentValidators::stringInListValidator(minMaxSolvingTechniques)).setDefaultValueString("vi").build()).build()); } bool CoreSettings::isCounterexampleSet() const { diff --git a/src/settings/modules/CoreSettings.h b/src/settings/modules/CoreSettings.h index 9b7189e2c..f6c8fbdf2 100644 --- a/src/settings/modules/CoreSettings.h +++ b/src/settings/modules/CoreSettings.h @@ -151,7 +151,6 @@ namespace storm { static const std::string engineOptionShortName; static const std::string ddLibraryOptionName; static const std::string cudaOptionName; - static const std::string minMaxEquationSolvingTechniqueOptionName; }; } // namespace modules diff --git a/src/settings/modules/CounterexampleGeneratorSettings.cpp b/src/settings/modules/CounterexampleGeneratorSettings.cpp index b2dbb0ddc..540892a44 100644 --- a/src/settings/modules/CounterexampleGeneratorSettings.cpp +++ b/src/settings/modules/CounterexampleGeneratorSettings.cpp @@ -48,7 +48,7 @@ namespace storm { bool CounterexampleGeneratorSettings::check() const { // Ensure that the model was given either symbolically or explicitly. - STORM_LOG_THROW(!isMinimalCommandSetGenerationSet() || storm::settings::getModule<storm::settings::modules::IOSettings>().isSymbolicSet(), storm::exceptions::InvalidSettingsException, "For the generation of a minimal command set, the model has to be specified symbolically."); + STORM_LOG_THROW(!isMinimalCommandSetGenerationSet() || storm::settings::getModule<storm::settings::modules::IOSettings>().isPrismInputSet(), storm::exceptions::InvalidSettingsException, "For the generation of a minimal command set, the model has to be specified in the PRISM format."); if (isMinimalCommandSetGenerationSet()) { STORM_LOG_WARN_COND(isUseMaxSatBasedMinimalCommandSetGenerationSet() || !isEncodeReachabilitySet(), "Encoding reachability is only available for the MaxSat-based minimal command set generation, so selecting it has no effect."); diff --git a/src/settings/modules/IOSettings.cpp b/src/settings/modules/IOSettings.cpp index 80f9b3439..c174f1f51 100644 --- a/src/settings/modules/IOSettings.cpp +++ b/src/settings/modules/IOSettings.cpp @@ -17,8 +17,9 @@ namespace storm { const std::string IOSettings::exportMatOptionName = "exportmat"; const std::string IOSettings::explicitOptionName = "explicit"; const std::string IOSettings::explicitOptionShortName = "exp"; - const std::string IOSettings::symbolicOptionName = "symbolic"; - const std::string IOSettings::symbolicOptionShortName = "s"; + const std::string IOSettings::prismInputOptionName = "prism"; + const std::string IOSettings::janiInputOptionName = "jani"; + const std::string IOSettings::prismToJaniOptionName = "prism2jani"; const std::string IOSettings::explorationOrderOptionName = "explorder"; const std::string IOSettings::explorationOrderOptionShortName = "eo"; const std::string IOSettings::transitionRewardsOptionName = "transrew"; @@ -38,8 +39,11 @@ namespace storm { this->addOption(storm::settings::OptionBuilder(moduleName, explicitOptionName, false, "Parses the model given in an explicit (sparse) representation.").setShortName(explicitOptionShortName) .addArgument(storm::settings::ArgumentBuilder::createStringArgument("transition filename", "The name of the file from which to read the transitions.").addValidationFunctionString(storm::settings::ArgumentValidators::existingReadableFileValidator()).build()) .addArgument(storm::settings::ArgumentBuilder::createStringArgument("labeling filename", "The name of the file from which to read the state labeling.").addValidationFunctionString(storm::settings::ArgumentValidators::existingReadableFileValidator()).build()).build()); - this->addOption(storm::settings::OptionBuilder(moduleName, symbolicOptionName, false, "Parses the model given in a symbolic representation.").setShortName(symbolicOptionShortName) - .addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "The name of the file from which to read the symbolic model.").addValidationFunctionString(storm::settings::ArgumentValidators::existingReadableFileValidator()).build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, prismInputOptionName, false, "Parses the model given in the PRISM format.") + .addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "The name of the file from which to read the PRISM input.").addValidationFunctionString(storm::settings::ArgumentValidators::existingReadableFileValidator()).build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, janiInputOptionName, false, "Parses the model given in the JANI format.") + .addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "The name of the file from which to read the JANI input.").addValidationFunctionString(storm::settings::ArgumentValidators::existingReadableFileValidator()).build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, prismToJaniOptionName, false, "If set, the input PRISM model is transformed to JANI.").build()); std::vector<std::string> explorationOrders = {"dfs", "bfs"}; this->addOption(storm::settings::OptionBuilder(moduleName, explorationOrderOptionName, false, "Sets which exploration order to use.").setShortName(explorationOrderOptionShortName) @@ -51,7 +55,7 @@ namespace storm { .addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "The file from which to read the state rewards.").addValidationFunctionString(storm::settings::ArgumentValidators::existingReadableFileValidator()).build()).build()); this->addOption(storm::settings::OptionBuilder(moduleName, choiceLabelingOptionName, false, "If given, the choice labels are read from this file and added to the explicit model. Note that this requires the model to be given as an explicit model (i.e., via --" + explicitOptionName + ").") .addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "The file from which to read the choice labels.").addValidationFunctionString(storm::settings::ArgumentValidators::existingReadableFileValidator()).build()).build()); - this->addOption(storm::settings::OptionBuilder(moduleName, constantsOptionName, false, "Specifies the constant replacements to use in symbolic models. Note that Note that this requires the model to be given as an symbolic model (i.e., via --" + symbolicOptionName + ").").setShortName(constantsOptionShortName) + this->addOption(storm::settings::OptionBuilder(moduleName, constantsOptionName, false, "Specifies the constant replacements to use in symbolic models. Note that this requires the model to be given as an symbolic model (i.e., via --" + prismInputOptionName + " or --" + janiInputOptionName + ").").setShortName(constantsOptionShortName) .addArgument(storm::settings::ArgumentBuilder::createStringArgument("values", "A comma separated list of constants and their value, e.g. a=1,b=2,c=3.").setDefaultValueString("").build()).build()); } @@ -75,14 +79,30 @@ namespace storm { return this->getOption(explicitOptionName).getArgumentByName("labeling filename").getValueAsString(); } - bool IOSettings::isSymbolicSet() const { - return this->getOption(symbolicOptionName).getHasOptionBeenSet(); + bool IOSettings::isPrismInputSet() const { + return this->getOption(prismInputOptionName).getHasOptionBeenSet(); } - std::string IOSettings::getSymbolicModelFilename() const { - return this->getOption(symbolicOptionName).getArgumentByName("filename").getValueAsString(); + bool IOSettings::isPrismOrJaniInputSet() const { + return isJaniInputSet() || isPrismInputSet(); } + bool IOSettings::isPrismToJaniSet() const { + return this->getOption(prismToJaniOptionName).getHasOptionBeenSet(); + } + + std::string IOSettings::getPrismInputFilename() const { + return this->getOption(prismInputOptionName).getArgumentByName("filename").getValueAsString(); + } + + bool IOSettings::isJaniInputSet() const { + return this->getOption(janiInputOptionName).getHasOptionBeenSet(); + } + + std::string IOSettings::getJaniInputFilename() const { + return this->getOption(janiInputOptionName).getArgumentByName("filename").getValueAsString(); + } + bool IOSettings::isExplorationOrderSet() const { return this->getOption(explorationOrderOptionName).getHasOptionBeenSet(); } @@ -141,8 +161,15 @@ namespace storm { } bool IOSettings::check() const { + // Ensure that not two symbolic input models were given. + STORM_LOG_THROW(!isJaniInputSet() || !isPrismInputSet(), storm::exceptions::InvalidSettingsException, "Symbolic model "); + // Ensure that the model was given either symbolically or explicitly. - STORM_LOG_THROW(!isSymbolicSet() || !isExplicitSet(), storm::exceptions::InvalidSettingsException, "The model may be either given in an explicit or a symbolic format, but not both."); + STORM_LOG_THROW(!isJaniInputSet() || !isPrismInputSet() || !isExplicitSet(), storm::exceptions::InvalidSettingsException, "The model may be either given in an explicit or a symbolic format (PRISM or JANI), but not both."); + + // Make sure PRISM-to-JANI conversion is only set if the actual input is in PRISM format. + STORM_LOG_THROW(!isPrismToJaniSet() || isPrismInputSet(), storm::exceptions::InvalidSettingsException, "For the transformation from PRISM to JANI, the input model must be given in the prism format."); + return true; } diff --git a/src/settings/modules/IOSettings.h b/src/settings/modules/IOSettings.h index eb1ced407..ae6ce6831 100644 --- a/src/settings/modules/IOSettings.h +++ b/src/settings/modules/IOSettings.h @@ -59,20 +59,49 @@ namespace storm { std::string getLabelingFilename() const; /*! - * Retrieves whether the symbolic option was set. + * Retrieves whether the PRISM language option was set. * - * @return True if the symbolic option was set. + * @return True if the PRISM input option was set. */ - bool isSymbolicSet() const; + bool isPrismInputSet() const; + + /*! + * Retrieves whether the JANI input option was set. + * + * @return True if the JANI input option was set. + */ + bool isJaniInputSet() const; /*! - * Retrieves the name of the file that contains the symbolic model specification if the model was given - * using the symbolic option. + * Retrieves whether the JANI or PRISM input option was set. + * + * @return True if either of the two options was set. + */ + bool isPrismOrJaniInputSet() const; + + /*! + * Retrieves whether the option to convert PRISM to JANI input was set. * - * @return The name of the file that contains the symbolic model specification. + * @return True if the option was set. */ - std::string getSymbolicModelFilename() const; + bool isPrismToJaniSet() const; + /*! + * Retrieves the name of the file that contains the PRISM model specification if the model was given + * using the PRISM input option. + * + * @return The name of the file that contains the PRISM model specification. + */ + std::string getPrismInputFilename() const; + + /*! + * Retrieves the name of the file that contains the JANI model specification if the model was given + * using the JANI input option. + * + * @return The name of the file that contains the JANI model specification. + */ + std::string getJaniInputFilename() const; + /*! * Retrieves whether the model exploration order was set. * @@ -174,8 +203,9 @@ namespace storm { static const std::string exportMatOptionName; static const std::string explicitOptionName; static const std::string explicitOptionShortName; - static const std::string symbolicOptionName; - static const std::string symbolicOptionShortName; + static const std::string prismInputOptionName; + static const std::string janiInputOptionName; + static const std::string prismToJaniOptionName; static const std::string explorationOrderOptionName; static const std::string explorationOrderOptionShortName; static const std::string transitionRewardsOptionName; diff --git a/src/storage/SymbolicModelDescription.cpp b/src/storage/SymbolicModelDescription.cpp new file mode 100644 index 000000000..1058e464f --- /dev/null +++ b/src/storage/SymbolicModelDescription.cpp @@ -0,0 +1,83 @@ +#include "src/storage/SymbolicModelDescription.h" + +#include "src/utility/prism.h" +#include "src/utility/jani.h" + +#include "src/utility/macros.h" +#include "src/exceptions/InvalidOperationException.h" + +namespace storm { + namespace storage { + + SymbolicModelDescription::SymbolicModelDescription(storm::jani::Model const& model) : modelDescription(model) { + // Intentionally left empty. + } + + SymbolicModelDescription::SymbolicModelDescription(storm::prism::Program const& program) : modelDescription(program) { + // Intentionally left empty. + } + + SymbolicModelDescription& SymbolicModelDescription::operator=(storm::jani::Model const& model) { + this->modelDescription = model; + return *this; + } + + SymbolicModelDescription& SymbolicModelDescription::operator=(storm::prism::Program const& program) { + this->modelDescription = program; + return *this; + } + + bool SymbolicModelDescription::hasModel() const { + return static_cast<bool>(modelDescription); + } + + bool SymbolicModelDescription::isJaniModel() const { + return modelDescription.get().which() == 0; + } + + bool SymbolicModelDescription::isPrismProgram() const { + return modelDescription.get().which() == 1; + } + + void SymbolicModelDescription::setModel(storm::jani::Model const& model) { + modelDescription = model; + } + + void SymbolicModelDescription::setModel(storm::prism::Program const& program) { + modelDescription = program; + } + + storm::jani::Model const& SymbolicModelDescription::asJaniModel() const { + STORM_LOG_THROW(isJaniModel(), storm::exceptions::InvalidOperationException, "Cannot retrieve JANI model, because the symbolic description has a different type."); + return boost::get<storm::jani::Model>(modelDescription.get()); + } + + storm::prism::Program const& SymbolicModelDescription::asPrismProgram() const { + STORM_LOG_THROW(isPrismProgram(), storm::exceptions::InvalidOperationException, "Cannot retrieve JANI model, because the symbolic description has a different type."); + return boost::get<storm::prism::Program>(modelDescription.get()); + } + + SymbolicModelDescription SymbolicModelDescription::toJani(bool makeVariablesGlobal) const { + if (this->isJaniModel()) { + return *this; + } + if (this->isPrismProgram()) { + return SymbolicModelDescription(this->asPrismProgram().toJani(makeVariablesGlobal)); + } else { + STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Cannot transform model description to the JANI format."); + } + } + + SymbolicModelDescription SymbolicModelDescription::preprocess(std::string const& constantDefinitionString) const { + if (this->isJaniModel()) { + std::map<storm::expressions::Variable, storm::expressions::Expression> substitution = storm::utility::jani::parseConstantDefinitionString(this->asJaniModel(), constantDefinitionString); + return SymbolicModelDescription(this->asJaniModel().defineUndefinedConstants(substitution).substituteConstants()); + } else if (this->isPrismProgram()) { + std::map<storm::expressions::Variable, storm::expressions::Expression> substitution = storm::utility::prism::parseConstantDefinitionString(this->asPrismProgram(), constantDefinitionString); + return SymbolicModelDescription(this->asPrismProgram().defineUndefinedConstants(substitution).substituteConstants()); + } + return *this; + } + + } +} \ No newline at end of file diff --git a/src/storage/SymbolicModelDescription.h b/src/storage/SymbolicModelDescription.h new file mode 100644 index 000000000..8b5b85d88 --- /dev/null +++ b/src/storage/SymbolicModelDescription.h @@ -0,0 +1,39 @@ +#pragma once + +#include <boost/variant.hpp> + +#include "src/storage/jani/Model.h" +#include "src/storage/prism/Program.h" + +namespace storm { + namespace storage { + + class SymbolicModelDescription { + public: + SymbolicModelDescription() = default; + SymbolicModelDescription(storm::jani::Model const& model); + SymbolicModelDescription(storm::prism::Program const& program); + + SymbolicModelDescription& operator=(storm::jani::Model const& model); + SymbolicModelDescription& operator=(storm::prism::Program const& program); + + bool hasModel() const; + bool isJaniModel() const; + bool isPrismProgram() const; + + void setModel(storm::jani::Model const& model); + void setModel(storm::prism::Program const& program); + + storm::jani::Model const& asJaniModel() const; + storm::prism::Program const& asPrismProgram() const; + + SymbolicModelDescription toJani(bool makeVariablesGlobal = true) const; + + SymbolicModelDescription preprocess(std::string const& constantDefinitionString = "") const; + + private: + boost::optional<boost::variant<storm::jani::Model, storm::prism::Program>> modelDescription; + }; + + } +} \ No newline at end of file diff --git a/src/storage/expressions/BaseExpression.cpp b/src/storage/expressions/BaseExpression.cpp index 9d44215d0..d37b61e50 100644 --- a/src/storage/expressions/BaseExpression.cpp +++ b/src/storage/expressions/BaseExpression.cpp @@ -4,6 +4,8 @@ #include "src/exceptions/InvalidTypeException.h" #include "src/exceptions/InvalidAccessException.h" +#include "src/storage/expressions/Expressions.h" + namespace storm { namespace expressions { BaseExpression::BaseExpression(ExpressionManager const& manager, Type const& type) : manager(manager), type(type) { @@ -94,6 +96,86 @@ namespace storm { return this->shared_from_this(); } + bool BaseExpression::isIfThenElseExpression() const { + return false; + } + + IfThenElseExpression const& BaseExpression::asIfThenElseExpression() const { + return static_cast<IfThenElseExpression const&>(*this); + } + + bool BaseExpression::isBinaryBooleanFunctionExpression() const { + return false; + } + + BinaryBooleanFunctionExpression const& BaseExpression::asBinaryBooleanFunctionExpression() const { + return static_cast<BinaryBooleanFunctionExpression const&>(*this); + } + + bool BaseExpression::isBinaryNumericalFunctionExpression() const { + return false; + } + + BinaryNumericalFunctionExpression const& BaseExpression::asBinaryNumericalFunctionExpression() const { + return static_cast<BinaryNumericalFunctionExpression const&>(*this); + } + + bool BaseExpression::isBinaryRelationExpression() const { + return false; + } + + BinaryRelationExpression const& BaseExpression::asBinaryRelationExpression() const { + return static_cast<BinaryRelationExpression const&>(*this); + } + + bool BaseExpression::isBooleanLiteralExpression() const { + return false; + } + + BooleanLiteralExpression const& BaseExpression::asBooleanLiteralExpression() const { + return static_cast<BooleanLiteralExpression const&>(*this); + } + + bool BaseExpression::isIntegerLiteralExpression() const { + return false; + } + + IntegerLiteralExpression const& BaseExpression::asIntegerLiteralExpression() const { + return static_cast<IntegerLiteralExpression const&>(*this); + } + + bool BaseExpression::isRationalLiteralExpression() const { + return false; + } + + RationalLiteralExpression const& BaseExpression::asRationalLiteralExpression() const { + return static_cast<RationalLiteralExpression const&>(*this); + } + + bool BaseExpression::isUnaryBooleanFunctionExpression() const { + return false; + } + + UnaryBooleanFunctionExpression const& BaseExpression::asUnaryBooleanFunctionExpression() const { + return static_cast<UnaryBooleanFunctionExpression const&>(*this); + } + + bool BaseExpression::isUnaryNumericalFunctionExpression() const { + return false; + } + + UnaryNumericalFunctionExpression const& BaseExpression::asUnaryNumericalFunctionExpression() const { + return static_cast<UnaryNumericalFunctionExpression const&>(*this); + } + + bool BaseExpression::isVariableExpression() const { + return false; + } + + VariableExpression const& BaseExpression::asVariableExpression() const { + return static_cast<VariableExpression const&>(*this); + } + std::ostream& operator<<(std::ostream& stream, BaseExpression const& expression) { expression.printToStream(stream); return stream; diff --git a/src/storage/expressions/BaseExpression.h b/src/storage/expressions/BaseExpression.h index f9b2792f4..7b5b2d973 100644 --- a/src/storage/expressions/BaseExpression.h +++ b/src/storage/expressions/BaseExpression.h @@ -13,12 +13,24 @@ namespace storm { namespace expressions { - // Forward-declare expression manager. + // Forward-declare expression classes. class ExpressionManager; class Variable; class Valuation; class ExpressionVisitor; enum struct OperatorType; + + class IfThenElseExpression; + class BinaryBooleanFunctionExpression; + class BinaryNumericalFunctionExpression; + class BinaryRelationExpression; + class BooleanLiteralExpression; + class IntegerLiteralExpression; + class RationalLiteralExpression; + class UnaryBooleanFunctionExpression; + class UnaryNumericalFunctionExpression; + class VariableExpression; + /*! * The base class of all expression classes. */ @@ -164,7 +176,7 @@ namespace storm { * * @param visitor The visitor that is to be accepted. */ - virtual boost::any accept(ExpressionVisitor& visitor) const = 0; + virtual boost::any accept(ExpressionVisitor& visitor, boost::any const& data) const = 0; /*! * Retrieves whether the expression has a numerical type, i.e., integer or double. @@ -224,6 +236,36 @@ namespace storm { friend std::ostream& operator<<(std::ostream& stream, BaseExpression const& expression); + virtual bool isIfThenElseExpression() const; + IfThenElseExpression const& asIfThenElseExpression() const; + + virtual bool isBinaryBooleanFunctionExpression() const; + BinaryBooleanFunctionExpression const& asBinaryBooleanFunctionExpression() const; + + virtual bool isBinaryNumericalFunctionExpression() const; + BinaryNumericalFunctionExpression const& asBinaryNumericalFunctionExpression() const; + + virtual bool isBinaryRelationExpression() const; + BinaryRelationExpression const& asBinaryRelationExpression() const; + + virtual bool isBooleanLiteralExpression() const; + BooleanLiteralExpression const& asBooleanLiteralExpression() const; + + virtual bool isIntegerLiteralExpression() const; + IntegerLiteralExpression const& asIntegerLiteralExpression() const; + + virtual bool isRationalLiteralExpression() const; + RationalLiteralExpression const& asRationalLiteralExpression() const; + + virtual bool isUnaryBooleanFunctionExpression() const; + UnaryBooleanFunctionExpression const& asUnaryBooleanFunctionExpression() const; + + virtual bool isUnaryNumericalFunctionExpression() const; + UnaryNumericalFunctionExpression const& asUnaryNumericalFunctionExpression() const; + + virtual bool isVariableExpression() const; + VariableExpression const& asVariableExpression() const; + protected: /*! * Prints the expression to the given stream. diff --git a/src/storage/expressions/BinaryBooleanFunctionExpression.cpp b/src/storage/expressions/BinaryBooleanFunctionExpression.cpp index 6cb550609..0b832db09 100644 --- a/src/storage/expressions/BinaryBooleanFunctionExpression.cpp +++ b/src/storage/expressions/BinaryBooleanFunctionExpression.cpp @@ -119,8 +119,8 @@ namespace storm { } } - boost::any BinaryBooleanFunctionExpression::accept(ExpressionVisitor& visitor) const { - return visitor.visit(*this); + boost::any BinaryBooleanFunctionExpression::accept(ExpressionVisitor& visitor, boost::any const& data) const { + return visitor.visit(*this, data); } void BinaryBooleanFunctionExpression::printToStream(std::ostream& stream) const { diff --git a/src/storage/expressions/BinaryBooleanFunctionExpression.h b/src/storage/expressions/BinaryBooleanFunctionExpression.h index d49658c7e..27a035cca 100644 --- a/src/storage/expressions/BinaryBooleanFunctionExpression.h +++ b/src/storage/expressions/BinaryBooleanFunctionExpression.h @@ -37,7 +37,7 @@ namespace storm { virtual storm::expressions::OperatorType getOperator() const override; virtual bool evaluateAsBool(Valuation const* valuation = nullptr) const override; virtual std::shared_ptr<BaseExpression const> simplify() const override; - virtual boost::any accept(ExpressionVisitor& visitor) const override; + virtual boost::any accept(ExpressionVisitor& visitor, boost::any const& data) const override; /*! * Retrieves the operator associated with the expression. diff --git a/src/storage/expressions/BinaryNumericalFunctionExpression.cpp b/src/storage/expressions/BinaryNumericalFunctionExpression.cpp index 95a326de1..eda2d0377 100644 --- a/src/storage/expressions/BinaryNumericalFunctionExpression.cpp +++ b/src/storage/expressions/BinaryNumericalFunctionExpression.cpp @@ -3,7 +3,7 @@ #include "src/storage/expressions/BinaryNumericalFunctionExpression.h" #include "src/storage/expressions/IntegerLiteralExpression.h" -#include "src/storage/expressions/DoubleLiteralExpression.h" +#include "src/storage/expressions/RationalLiteralExpression.h" #include "src/storage/expressions/ExpressionVisitor.h" #include "src/utility/macros.h" #include "src/exceptions/InvalidTypeException.h" @@ -102,7 +102,7 @@ namespace storm { case OperatorType::Power: newValue = static_cast<int_fast64_t>(std::pow(firstOperandEvaluation, secondOperandEvaluation)); break; case OperatorType::Divide: STORM_LOG_THROW(false, storm::exceptions::InvalidStateException, "Unable to simplify division."); break; } - return std::shared_ptr<BaseExpression>(new DoubleLiteralExpression(this->getManager(), newValue)); + return std::shared_ptr<BaseExpression>(new RationalLiteralExpression(this->getManager(), newValue)); } } @@ -113,8 +113,8 @@ namespace storm { } } - boost::any BinaryNumericalFunctionExpression::accept(ExpressionVisitor& visitor) const { - return visitor.visit(*this); + boost::any BinaryNumericalFunctionExpression::accept(ExpressionVisitor& visitor, boost::any const& data) const { + return visitor.visit(*this, data); } void BinaryNumericalFunctionExpression::printToStream(std::ostream& stream) const { diff --git a/src/storage/expressions/BinaryNumericalFunctionExpression.h b/src/storage/expressions/BinaryNumericalFunctionExpression.h index e3c7d227a..1320ab56b 100644 --- a/src/storage/expressions/BinaryNumericalFunctionExpression.h +++ b/src/storage/expressions/BinaryNumericalFunctionExpression.h @@ -38,7 +38,7 @@ namespace storm { virtual int_fast64_t evaluateAsInt(Valuation const* valuation = nullptr) const override; virtual double evaluateAsDouble(Valuation const* valuation = nullptr) const override; virtual std::shared_ptr<BaseExpression const> simplify() const override; - virtual boost::any accept(ExpressionVisitor& visitor) const override; + virtual boost::any accept(ExpressionVisitor& visitor, boost::any const& data) const override; /*! * Retrieves the operator associated with the expression. diff --git a/src/storage/expressions/BinaryRelationExpression.cpp b/src/storage/expressions/BinaryRelationExpression.cpp index 858536d0c..04a8f7895 100644 --- a/src/storage/expressions/BinaryRelationExpression.cpp +++ b/src/storage/expressions/BinaryRelationExpression.cpp @@ -81,8 +81,8 @@ namespace storm { } } - boost::any BinaryRelationExpression::accept(ExpressionVisitor& visitor) const { - return visitor.visit(*this); + boost::any BinaryRelationExpression::accept(ExpressionVisitor& visitor, boost::any const& data) const { + return visitor.visit(*this, data); } BinaryRelationExpression::RelationType BinaryRelationExpression::getRelationType() const { diff --git a/src/storage/expressions/BinaryRelationExpression.h b/src/storage/expressions/BinaryRelationExpression.h index 079a935a6..6dd73179c 100644 --- a/src/storage/expressions/BinaryRelationExpression.h +++ b/src/storage/expressions/BinaryRelationExpression.h @@ -37,7 +37,7 @@ namespace storm { virtual storm::expressions::OperatorType getOperator() const override; virtual bool evaluateAsBool(Valuation const* valuation = nullptr) const override; virtual std::shared_ptr<BaseExpression const> simplify() const override; - virtual boost::any accept(ExpressionVisitor& visitor) const override; + virtual boost::any accept(ExpressionVisitor& visitor, boost::any const& data) const override; /*! * Retrieves the relation associated with the expression. diff --git a/src/storage/expressions/BooleanLiteralExpression.cpp b/src/storage/expressions/BooleanLiteralExpression.cpp index 9e4986b04..086d22abd 100644 --- a/src/storage/expressions/BooleanLiteralExpression.cpp +++ b/src/storage/expressions/BooleanLiteralExpression.cpp @@ -32,8 +32,8 @@ namespace storm { return this->shared_from_this(); } - boost::any BooleanLiteralExpression::accept(ExpressionVisitor& visitor) const { - return visitor.visit(*this); + boost::any BooleanLiteralExpression::accept(ExpressionVisitor& visitor, boost::any const& data) const { + return visitor.visit(*this, data); } bool BooleanLiteralExpression::getValue() const { diff --git a/src/storage/expressions/BooleanLiteralExpression.h b/src/storage/expressions/BooleanLiteralExpression.h index f5dcb78a1..cea09a295 100644 --- a/src/storage/expressions/BooleanLiteralExpression.h +++ b/src/storage/expressions/BooleanLiteralExpression.h @@ -32,7 +32,7 @@ namespace storm { virtual bool isFalse() const override; virtual void gatherVariables(std::set<storm::expressions::Variable>& variables) const override; virtual std::shared_ptr<BaseExpression const> simplify() const override; - virtual boost::any accept(ExpressionVisitor& visitor) const override; + virtual boost::any accept(ExpressionVisitor& visitor, boost::any const& data) const override; /*! * Retrieves the value of the boolean literal. diff --git a/src/storage/expressions/DoubleLiteralExpression.cpp b/src/storage/expressions/DoubleLiteralExpression.cpp deleted file mode 100644 index cab9fc31b..000000000 --- a/src/storage/expressions/DoubleLiteralExpression.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "src/storage/expressions/DoubleLiteralExpression.h" -#include "src/storage/expressions/ExpressionManager.h" -#include "src/storage/expressions/ExpressionVisitor.h" - -namespace storm { - namespace expressions { - DoubleLiteralExpression::DoubleLiteralExpression(ExpressionManager const& manager, double value) : BaseExpression(manager, manager.getRationalType()), value(value) { - // Intentionally left empty. - } - - double DoubleLiteralExpression::evaluateAsDouble(Valuation const* valuation) const { - return this->getValue(); - } - - bool DoubleLiteralExpression::isLiteral() const { - return true; - } - - void DoubleLiteralExpression::gatherVariables(std::set<storm::expressions::Variable>& variables) const { - return; - } - - std::shared_ptr<BaseExpression const> DoubleLiteralExpression::simplify() const { - return this->shared_from_this(); - } - - boost::any DoubleLiteralExpression::accept(ExpressionVisitor& visitor) const { - return visitor.visit(*this); - } - - double DoubleLiteralExpression::getValue() const { - return this->value; - } - - void DoubleLiteralExpression::printToStream(std::ostream& stream) const { - stream << this->getValue(); - } - } -} \ No newline at end of file diff --git a/src/storage/expressions/DoubleLiteralExpression.h b/src/storage/expressions/DoubleLiteralExpression.h deleted file mode 100644 index 676291a77..000000000 --- a/src/storage/expressions/DoubleLiteralExpression.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef STORM_STORAGE_EXPRESSIONS_DOUBLELITERALEXPRESSION_H_ -#define STORM_STORAGE_EXPRESSIONS_DOUBLELITERALEXPRESSION_H_ - -#include "src/storage/expressions/BaseExpression.h" -#include "src/utility/OsDetection.h" - -namespace storm { - namespace expressions { - class DoubleLiteralExpression : public BaseExpression { - public: - /*! - * Creates an double literal expression with the given value. - * - * @param manager The manager responsible for this expression. - * @param value The value of the double literal. - */ - DoubleLiteralExpression(ExpressionManager const& manager, double value); - - // Instantiate constructors and assignments with their default implementations. - DoubleLiteralExpression(DoubleLiteralExpression const& other) = default; - DoubleLiteralExpression& operator=(DoubleLiteralExpression const& other) = delete; -#ifndef WINDOWS - DoubleLiteralExpression(DoubleLiteralExpression&&) = default; - DoubleLiteralExpression& operator=(DoubleLiteralExpression&&) = delete; -#endif - virtual ~DoubleLiteralExpression() = default; - - // Override base class methods. - virtual double evaluateAsDouble(Valuation const* valuation = nullptr) const override; - virtual bool isLiteral() const override; - virtual void gatherVariables(std::set<storm::expressions::Variable>& variables) const override; - virtual std::shared_ptr<BaseExpression const> simplify() const override; - virtual boost::any accept(ExpressionVisitor& visitor) const override; - - /*! - * Retrieves the value of the double literal. - * - * @return The value of the double literal. - */ - double getValue() const; - - protected: - // Override base class method. - virtual void printToStream(std::ostream& stream) const override; - - private: - // The value of the double literal. - double value; - }; - } -} - -#endif /* STORM_STORAGE_EXPRESSIONS_DOUBLELITERALEXPRESSION_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/Expression.cpp b/src/storage/expressions/Expression.cpp index 410b613ec..7493e5ee1 100644 --- a/src/storage/expressions/Expression.cpp +++ b/src/storage/expressions/Expression.cpp @@ -5,6 +5,7 @@ #include "src/storage/expressions/ExpressionManager.h" #include "src/storage/expressions/SubstitutionVisitor.h" #include "src/storage/expressions/LinearityCheckVisitor.h" +#include "src/storage/expressions/SyntacticalEqualityCheckVisitor.h" #include "src/storage/expressions/Expressions.h" #include "src/exceptions/InvalidTypeException.h" #include "src/exceptions/InvalidArgumentException.h" @@ -158,8 +159,8 @@ namespace storm { return this->getBaseExpression().hasBitVectorType(); } - boost::any Expression::accept(ExpressionVisitor& visitor) const { - return this->getBaseExpression().accept(visitor); + boost::any Expression::accept(ExpressionVisitor& visitor, boost::any const& data) const { + return this->getBaseExpression().accept(visitor, data); } bool Expression::isInitialized() const { @@ -168,6 +169,14 @@ namespace storm { } return false; } + + bool Expression::isSyntacticallyEqual(storm::expressions::Expression const& other) const { + if (this->getBaseExpressionPointer() == other.getBaseExpressionPointer()) { + return true; + } + SyntacticalEqualityCheckVisitor checker; + return checker.isSyntaticallyEqual(*this, other); + } std::string Expression::toString() const { std::stringstream stream; diff --git a/src/storage/expressions/Expression.h b/src/storage/expressions/Expression.h index 176d9316d..334a4f829 100644 --- a/src/storage/expressions/Expression.h +++ b/src/storage/expressions/Expression.h @@ -302,7 +302,7 @@ namespace storm { * * @param visitor The visitor to accept. */ - boost::any accept(ExpressionVisitor& visitor) const; + boost::any accept(ExpressionVisitor& visitor, boost::any const& data) const; /*! * Converts the expression into a string. @@ -311,11 +311,16 @@ namespace storm { */ std::string toString() const; - /** + /*! * Checks whether the object encapsulates a base-expression. */ bool isInitialized() const; + /*! + * Checks whether the two expressions are syntatically the same. + */ + bool isSyntacticallyEqual(storm::expressions::Expression const& other) const; + friend std::ostream& operator<<(std::ostream& stream, Expression const& expression); private: diff --git a/src/storage/expressions/ExpressionManager.cpp b/src/storage/expressions/ExpressionManager.cpp index 36b32caaf..deed0e557 100644 --- a/src/storage/expressions/ExpressionManager.cpp +++ b/src/storage/expressions/ExpressionManager.cpp @@ -65,7 +65,11 @@ namespace storm { } Expression ExpressionManager::rational(double value) const { - return Expression(std::shared_ptr<BaseExpression>(new DoubleLiteralExpression(*this, value))); + return Expression(std::shared_ptr<BaseExpression>(new RationalLiteralExpression(*this, value))); + } + + Expression ExpressionManager::rational(storm::RationalNumber const& value) const { + return Expression(std::shared_ptr<BaseExpression>(new RationalLiteralExpression(*this, value))); } bool ExpressionManager::operator==(ExpressionManager const& other) const { diff --git a/src/storage/expressions/ExpressionManager.h b/src/storage/expressions/ExpressionManager.h index 3e39369ba..31cef2942 100644 --- a/src/storage/expressions/ExpressionManager.h +++ b/src/storage/expressions/ExpressionManager.h @@ -10,6 +10,7 @@ #include "src/storage/expressions/Variable.h" #include "src/storage/expressions/Expression.h" +#include "src/adapters/CarlAdapter.h" #include "src/utility/OsDetection.h" namespace storm { @@ -104,6 +105,14 @@ namespace storm { * @return The resulting expression. */ Expression rational(double value) const; + + /*! + * Creates an expression that characterizes the given rational literal. + * + * @param value The value of the rational literal. + * @return The resulting expression. + */ + Expression rational(storm::RationalNumber const& value) const; /*! * Compares the two expression managers for equality, which holds iff they are the very same object. diff --git a/src/storage/expressions/ExpressionVisitor.h b/src/storage/expressions/ExpressionVisitor.h index 5fdb486c2..cfe2ce9b6 100644 --- a/src/storage/expressions/ExpressionVisitor.h +++ b/src/storage/expressions/ExpressionVisitor.h @@ -15,20 +15,20 @@ namespace storm { class UnaryNumericalFunctionExpression; class BooleanLiteralExpression; class IntegerLiteralExpression; - class DoubleLiteralExpression; + class RationalLiteralExpression; class ExpressionVisitor { public: - virtual boost::any visit(IfThenElseExpression const& expression) = 0; - virtual boost::any visit(BinaryBooleanFunctionExpression const& expression) = 0; - virtual boost::any visit(BinaryNumericalFunctionExpression const& expression) = 0; - virtual boost::any visit(BinaryRelationExpression const& expression) = 0; - virtual boost::any visit(VariableExpression const& expression) = 0; - virtual boost::any visit(UnaryBooleanFunctionExpression const& expression) = 0; - virtual boost::any visit(UnaryNumericalFunctionExpression const& expression) = 0; - virtual boost::any visit(BooleanLiteralExpression const& expression) = 0; - virtual boost::any visit(IntegerLiteralExpression const& expression) = 0; - virtual boost::any visit(DoubleLiteralExpression const& expression) = 0; + virtual boost::any visit(IfThenElseExpression const& expression, boost::any const& data) = 0; + virtual boost::any visit(BinaryBooleanFunctionExpression const& expression, boost::any const& data) = 0; + virtual boost::any visit(BinaryNumericalFunctionExpression const& expression, boost::any const& data) = 0; + virtual boost::any visit(BinaryRelationExpression const& expression, boost::any const& data) = 0; + virtual boost::any visit(VariableExpression const& expression, boost::any const& data) = 0; + virtual boost::any visit(UnaryBooleanFunctionExpression const& expression, boost::any const& data) = 0; + virtual boost::any visit(UnaryNumericalFunctionExpression const& expression, boost::any const& data) = 0; + virtual boost::any visit(BooleanLiteralExpression const& expression, boost::any const& data) = 0; + virtual boost::any visit(IntegerLiteralExpression const& expression, boost::any const& data) = 0; + virtual boost::any visit(RationalLiteralExpression const& expression, boost::any const& data) = 0; }; } } diff --git a/src/storage/expressions/Expressions.h b/src/storage/expressions/Expressions.h index d670c29e3..655cc4aab 100644 --- a/src/storage/expressions/Expressions.h +++ b/src/storage/expressions/Expressions.h @@ -4,7 +4,7 @@ #include "src/storage/expressions/BinaryRelationExpression.h" #include "src/storage/expressions/BooleanLiteralExpression.h" #include "src/storage/expressions/IntegerLiteralExpression.h" -#include "src/storage/expressions/DoubleLiteralExpression.h" +#include "src/storage/expressions/RationalLiteralExpression.h" #include "src/storage/expressions/UnaryBooleanFunctionExpression.h" #include "src/storage/expressions/UnaryNumericalFunctionExpression.h" #include "src/storage/expressions/VariableExpression.h" diff --git a/src/storage/expressions/IfThenElseExpression.cpp b/src/storage/expressions/IfThenElseExpression.cpp index adde41a2d..8441fbeb0 100644 --- a/src/storage/expressions/IfThenElseExpression.cpp +++ b/src/storage/expressions/IfThenElseExpression.cpp @@ -88,8 +88,8 @@ namespace storm { } } - boost::any IfThenElseExpression::accept(ExpressionVisitor& visitor) const { - return visitor.visit(*this); + boost::any IfThenElseExpression::accept(ExpressionVisitor& visitor, boost::any const& data) const { + return visitor.visit(*this, data); } std::shared_ptr<BaseExpression const> IfThenElseExpression::getCondition() const { diff --git a/src/storage/expressions/IfThenElseExpression.h b/src/storage/expressions/IfThenElseExpression.h index d2503116b..347feba12 100644 --- a/src/storage/expressions/IfThenElseExpression.h +++ b/src/storage/expressions/IfThenElseExpression.h @@ -38,7 +38,7 @@ namespace storm { virtual double evaluateAsDouble(Valuation const* valuation = nullptr) const override; virtual void gatherVariables(std::set<storm::expressions::Variable>& variables) const override; virtual std::shared_ptr<BaseExpression const> simplify() const override; - virtual boost::any accept(ExpressionVisitor& visitor) const override; + virtual boost::any accept(ExpressionVisitor& visitor, boost::any const& data) const override; /*! * Retrieves the condition expression of the if-then-else expression. diff --git a/src/storage/expressions/IntegerLiteralExpression.cpp b/src/storage/expressions/IntegerLiteralExpression.cpp index 35ae07ee7..d8b7077b5 100644 --- a/src/storage/expressions/IntegerLiteralExpression.cpp +++ b/src/storage/expressions/IntegerLiteralExpression.cpp @@ -29,8 +29,8 @@ namespace storm { return this->shared_from_this(); } - boost::any IntegerLiteralExpression::accept(ExpressionVisitor& visitor) const { - return visitor.visit(*this); + boost::any IntegerLiteralExpression::accept(ExpressionVisitor& visitor, boost::any const& data) const { + return visitor.visit(*this, data); } int_fast64_t IntegerLiteralExpression::getValue() const { diff --git a/src/storage/expressions/IntegerLiteralExpression.h b/src/storage/expressions/IntegerLiteralExpression.h index b4f732b83..3dc06e4ef 100644 --- a/src/storage/expressions/IntegerLiteralExpression.h +++ b/src/storage/expressions/IntegerLiteralExpression.h @@ -31,7 +31,7 @@ namespace storm { virtual bool isLiteral() const override; virtual void gatherVariables(std::set<storm::expressions::Variable>& variables) const override; virtual std::shared_ptr<BaseExpression const> simplify() const override; - virtual boost::any accept(ExpressionVisitor& visitor) const override; + virtual boost::any accept(ExpressionVisitor& visitor, boost::any const& data) const override; /*! * Retrieves the value of the integer literal. diff --git a/src/storage/expressions/LinearCoefficientVisitor.cpp b/src/storage/expressions/LinearCoefficientVisitor.cpp index 8d7f39278..6a8701610 100644 --- a/src/storage/expressions/LinearCoefficientVisitor.cpp +++ b/src/storage/expressions/LinearCoefficientVisitor.cpp @@ -85,20 +85,20 @@ namespace storm { } LinearCoefficientVisitor::VariableCoefficients LinearCoefficientVisitor::getLinearCoefficients(Expression const& expression) { - return boost::any_cast<VariableCoefficients>(expression.getBaseExpression().accept(*this)); + return boost::any_cast<VariableCoefficients>(expression.getBaseExpression().accept(*this, boost::none)); } - boost::any LinearCoefficientVisitor::visit(IfThenElseExpression const& expression) { + boost::any LinearCoefficientVisitor::visit(IfThenElseExpression const& expression, boost::any const& data) { STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); } - boost::any LinearCoefficientVisitor::visit(BinaryBooleanFunctionExpression const& expression) { + boost::any LinearCoefficientVisitor::visit(BinaryBooleanFunctionExpression const& expression, boost::any const& data) { STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); } - boost::any LinearCoefficientVisitor::visit(BinaryNumericalFunctionExpression const& expression) { - VariableCoefficients leftResult = boost::any_cast<VariableCoefficients>(expression.getFirstOperand()->accept(*this)); - VariableCoefficients rightResult = boost::any_cast<VariableCoefficients>(expression.getSecondOperand()->accept(*this)); + boost::any LinearCoefficientVisitor::visit(BinaryNumericalFunctionExpression const& expression, boost::any const& data) { + VariableCoefficients leftResult = boost::any_cast<VariableCoefficients>(expression.getFirstOperand()->accept(*this, data)); + VariableCoefficients rightResult = boost::any_cast<VariableCoefficients>(expression.getSecondOperand()->accept(*this, data)); if (expression.getOperatorType() == BinaryNumericalFunctionExpression::OperatorType::Plus) { leftResult += std::move(rightResult); @@ -114,11 +114,11 @@ namespace storm { return leftResult; } - boost::any LinearCoefficientVisitor::visit(BinaryRelationExpression const& expression) { + boost::any LinearCoefficientVisitor::visit(BinaryRelationExpression const& expression, boost::any const& data) { STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); } - boost::any LinearCoefficientVisitor::visit(VariableExpression const& expression) { + boost::any LinearCoefficientVisitor::visit(VariableExpression const& expression, boost::any const& data) { VariableCoefficients coefficients; if (expression.getType().isNumericalType()) { coefficients.setCoefficient(expression.getVariable(), 1); @@ -128,12 +128,12 @@ namespace storm { return coefficients; } - boost::any LinearCoefficientVisitor::visit(UnaryBooleanFunctionExpression const& expression) { + boost::any LinearCoefficientVisitor::visit(UnaryBooleanFunctionExpression const& expression, boost::any const& data) { STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); } - boost::any LinearCoefficientVisitor::visit(UnaryNumericalFunctionExpression const& expression) { - VariableCoefficients childResult = boost::any_cast<VariableCoefficients>(expression.getOperand()->accept(*this)); + boost::any LinearCoefficientVisitor::visit(UnaryNumericalFunctionExpression const& expression, boost::any const& data) { + VariableCoefficients childResult = boost::any_cast<VariableCoefficients>(expression.getOperand()->accept(*this, data)); if (expression.getOperatorType() == UnaryNumericalFunctionExpression::OperatorType::Minus) { childResult.negate(); @@ -143,16 +143,16 @@ namespace storm { } } - boost::any LinearCoefficientVisitor::visit(BooleanLiteralExpression const& expression) { + boost::any LinearCoefficientVisitor::visit(BooleanLiteralExpression const& expression, boost::any const& data) { STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); } - boost::any LinearCoefficientVisitor::visit(IntegerLiteralExpression const& expression) { + boost::any LinearCoefficientVisitor::visit(IntegerLiteralExpression const& expression, boost::any const& data) { return VariableCoefficients(static_cast<double>(expression.getValue())); } - boost::any LinearCoefficientVisitor::visit(DoubleLiteralExpression const& expression) { - return VariableCoefficients(expression.getValue()); + boost::any LinearCoefficientVisitor::visit(RationalLiteralExpression const& expression, boost::any const& data) { + return VariableCoefficients(expression.getValueAsDouble()); } } } \ No newline at end of file diff --git a/src/storage/expressions/LinearCoefficientVisitor.h b/src/storage/expressions/LinearCoefficientVisitor.h index b48c53d09..b0c85a800 100644 --- a/src/storage/expressions/LinearCoefficientVisitor.h +++ b/src/storage/expressions/LinearCoefficientVisitor.h @@ -66,16 +66,16 @@ namespace storm { */ VariableCoefficients getLinearCoefficients(Expression const& expression); - virtual boost::any visit(IfThenElseExpression const& expression) override; - virtual boost::any visit(BinaryBooleanFunctionExpression const& expression) override; - virtual boost::any visit(BinaryNumericalFunctionExpression const& expression) override; - virtual boost::any visit(BinaryRelationExpression const& expression) override; - virtual boost::any visit(VariableExpression const& expression) override; - virtual boost::any visit(UnaryBooleanFunctionExpression const& expression) override; - virtual boost::any visit(UnaryNumericalFunctionExpression const& expression) override; - virtual boost::any visit(BooleanLiteralExpression const& expression) override; - virtual boost::any visit(IntegerLiteralExpression const& expression) override; - virtual boost::any visit(DoubleLiteralExpression const& expression) override; + virtual boost::any visit(IfThenElseExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BinaryBooleanFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BinaryNumericalFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BinaryRelationExpression const& expression, boost::any const& data) override; + virtual boost::any visit(VariableExpression const& expression, boost::any const& data) override; + virtual boost::any visit(UnaryBooleanFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(UnaryNumericalFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BooleanLiteralExpression const& expression, boost::any const& data) override; + virtual boost::any visit(IntegerLiteralExpression const& expression, boost::any const& data) override; + virtual boost::any visit(RationalLiteralExpression const& expression, boost::any const& data) override; }; } } diff --git a/src/storage/expressions/LinearityCheckVisitor.cpp b/src/storage/expressions/LinearityCheckVisitor.cpp index 35e1e5c76..b762f9cf1 100644 --- a/src/storage/expressions/LinearityCheckVisitor.cpp +++ b/src/storage/expressions/LinearityCheckVisitor.cpp @@ -12,27 +12,27 @@ namespace storm { } bool LinearityCheckVisitor::check(Expression const& expression) { - LinearityStatus result = boost::any_cast<LinearityStatus>(expression.getBaseExpression().accept(*this)); + LinearityStatus result = boost::any_cast<LinearityStatus>(expression.getBaseExpression().accept(*this, boost::none)); return result == LinearityStatus::LinearWithoutVariables || result == LinearityStatus::LinearContainsVariables; } - boost::any LinearityCheckVisitor::visit(IfThenElseExpression const& expression) { + boost::any LinearityCheckVisitor::visit(IfThenElseExpression const& expression, boost::any const& data) { // An if-then-else expression is never linear. return LinearityStatus::NonLinear; } - boost::any LinearityCheckVisitor::visit(BinaryBooleanFunctionExpression const& expression) { + boost::any LinearityCheckVisitor::visit(BinaryBooleanFunctionExpression const& expression, boost::any const& data) { // Boolean function applications are not allowed in linear expressions. return LinearityStatus::NonLinear; } - boost::any LinearityCheckVisitor::visit(BinaryNumericalFunctionExpression const& expression) { - LinearityStatus leftResult = boost::any_cast<LinearityStatus>(expression.getFirstOperand()->accept(*this)); + boost::any LinearityCheckVisitor::visit(BinaryNumericalFunctionExpression const& expression, boost::any const& data) { + LinearityStatus leftResult = boost::any_cast<LinearityStatus>(expression.getFirstOperand()->accept(*this, data)); if (leftResult == LinearityStatus::NonLinear) { return LinearityStatus::NonLinear; } - LinearityStatus rightResult = boost::any_cast<LinearityStatus>(expression.getSecondOperand()->accept(*this)); + LinearityStatus rightResult = boost::any_cast<LinearityStatus>(expression.getSecondOperand()->accept(*this, data)); if (rightResult == LinearityStatus::NonLinear) { return LinearityStatus::NonLinear; } @@ -55,37 +55,37 @@ namespace storm { STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Illegal binary numerical expression operator."); } - boost::any LinearityCheckVisitor::visit(BinaryRelationExpression const& expression) { + boost::any LinearityCheckVisitor::visit(BinaryRelationExpression const& expression, boost::any const& data) { return LinearityStatus::NonLinear; } - boost::any LinearityCheckVisitor::visit(VariableExpression const& expression) { + boost::any LinearityCheckVisitor::visit(VariableExpression const& expression, boost::any const& data) { return LinearityStatus::LinearContainsVariables; } - boost::any LinearityCheckVisitor::visit(UnaryBooleanFunctionExpression const& expression) { + boost::any LinearityCheckVisitor::visit(UnaryBooleanFunctionExpression const& expression, boost::any const& data) { // Boolean function applications are not allowed in linear expressions. return LinearityStatus::NonLinear; } - boost::any LinearityCheckVisitor::visit(UnaryNumericalFunctionExpression const& expression) { + boost::any LinearityCheckVisitor::visit(UnaryNumericalFunctionExpression const& expression, boost::any const& data) { switch (expression.getOperatorType()) { - case UnaryNumericalFunctionExpression::OperatorType::Minus: return expression.getOperand()->accept(*this); + case UnaryNumericalFunctionExpression::OperatorType::Minus: return expression.getOperand()->accept(*this, data); case UnaryNumericalFunctionExpression::OperatorType::Floor: case UnaryNumericalFunctionExpression::OperatorType::Ceil: return LinearityStatus::NonLinear; } STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Illegal unary numerical expression operator."); } - boost::any LinearityCheckVisitor::visit(BooleanLiteralExpression const& expression) { + boost::any LinearityCheckVisitor::visit(BooleanLiteralExpression const& expression, boost::any const& data) { return LinearityStatus::NonLinear; } - boost::any LinearityCheckVisitor::visit(IntegerLiteralExpression const& expression) { + boost::any LinearityCheckVisitor::visit(IntegerLiteralExpression const& expression, boost::any const& data) { return LinearityStatus::LinearWithoutVariables; } - boost::any LinearityCheckVisitor::visit(DoubleLiteralExpression const& expression) { + boost::any LinearityCheckVisitor::visit(RationalLiteralExpression const& expression, boost::any const& data) { return LinearityStatus::LinearWithoutVariables; } } diff --git a/src/storage/expressions/LinearityCheckVisitor.h b/src/storage/expressions/LinearityCheckVisitor.h index 2df8f8084..b2a465871 100644 --- a/src/storage/expressions/LinearityCheckVisitor.h +++ b/src/storage/expressions/LinearityCheckVisitor.h @@ -20,16 +20,16 @@ namespace storm { */ bool check(Expression const& expression); - virtual boost::any visit(IfThenElseExpression const& expression) override; - virtual boost::any visit(BinaryBooleanFunctionExpression const& expression) override; - virtual boost::any visit(BinaryNumericalFunctionExpression const& expression) override; - virtual boost::any visit(BinaryRelationExpression const& expression) override; - virtual boost::any visit(VariableExpression const& expression) override; - virtual boost::any visit(UnaryBooleanFunctionExpression const& expression) override; - virtual boost::any visit(UnaryNumericalFunctionExpression const& expression) override; - virtual boost::any visit(BooleanLiteralExpression const& expression) override; - virtual boost::any visit(IntegerLiteralExpression const& expression) override; - virtual boost::any visit(DoubleLiteralExpression const& expression) override; + virtual boost::any visit(IfThenElseExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BinaryBooleanFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BinaryNumericalFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BinaryRelationExpression const& expression, boost::any const& data) override; + virtual boost::any visit(VariableExpression const& expression, boost::any const& data) override; + virtual boost::any visit(UnaryBooleanFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(UnaryNumericalFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BooleanLiteralExpression const& expression, boost::any const& data) override; + virtual boost::any visit(IntegerLiteralExpression const& expression, boost::any const& data) override; + virtual boost::any visit(RationalLiteralExpression const& expression, boost::any const& data) override; private: enum class LinearityStatus { NonLinear, LinearContainsVariables, LinearWithoutVariables }; diff --git a/src/storage/expressions/RationalLiteralExpression.cpp b/src/storage/expressions/RationalLiteralExpression.cpp new file mode 100644 index 000000000..6c6ee88ff --- /dev/null +++ b/src/storage/expressions/RationalLiteralExpression.cpp @@ -0,0 +1,53 @@ +#include "src/storage/expressions/RationalLiteralExpression.h" +#include "src/storage/expressions/ExpressionManager.h" +#include "src/storage/expressions/ExpressionVisitor.h" + +#include "src/utility/constants.h" + +namespace storm { + namespace expressions { + RationalLiteralExpression::RationalLiteralExpression(ExpressionManager const& manager, double value) : BaseExpression(manager, manager.getRationalType()), value(storm::utility::convertNumber<storm::RationalNumber>(value)) { + // Intentionally left empty. + } + + RationalLiteralExpression::RationalLiteralExpression(ExpressionManager const& manager, std::string const& valueAsString) : BaseExpression(manager, manager.getRationalType()), value(storm::utility::convertNumber<storm::RationalNumber>(valueAsString)) { + // Intentionally left empty. + } + + RationalLiteralExpression::RationalLiteralExpression(ExpressionManager const& manager, storm::RationalNumber const& value) : BaseExpression(manager, manager.getRationalType()), value(value) { + // Intentionally left empty. + } + + double RationalLiteralExpression::evaluateAsDouble(Valuation const* valuation) const { + return this->getValueAsDouble(); + } + + bool RationalLiteralExpression::isLiteral() const { + return true; + } + + void RationalLiteralExpression::gatherVariables(std::set<storm::expressions::Variable>& variables) const { + return; + } + + std::shared_ptr<BaseExpression const> RationalLiteralExpression::simplify() const { + return this->shared_from_this(); + } + + boost::any RationalLiteralExpression::accept(ExpressionVisitor& visitor, boost::any const& data) const { + return visitor.visit(*this, data); + } + + double RationalLiteralExpression::getValueAsDouble() const { + return storm::utility::convertNumber<double>(this->value); + } + + storm::RationalNumber RationalLiteralExpression::getValue() const { + return this->value; + } + + void RationalLiteralExpression::printToStream(std::ostream& stream) const { + stream << this->getValue(); + } + } +} \ No newline at end of file diff --git a/src/storage/expressions/RationalLiteralExpression.h b/src/storage/expressions/RationalLiteralExpression.h new file mode 100644 index 000000000..e18cf8c43 --- /dev/null +++ b/src/storage/expressions/RationalLiteralExpression.h @@ -0,0 +1,78 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_RationalLiteralExpression_H_ +#define STORM_STORAGE_EXPRESSIONS_RationalLiteralExpression_H_ + +#include "src/storage/expressions/BaseExpression.h" +#include "src/utility/OsDetection.h" + +#include "src/adapters/CarlAdapter.h" + +namespace storm { + namespace expressions { + class RationalLiteralExpression : public BaseExpression { + public: + /*! + * Creates an double literal expression with the given value. + * + * @param manager The manager responsible for this expression. + * @param value The value of the double literal. + */ + RationalLiteralExpression(ExpressionManager const& manager, double value); + + /*! + * Creates an double literal expression with the value given as a string. + * + * @param manager The manager responsible for this expression. + * @param value The string representation of the value of the literal. + */ + RationalLiteralExpression(ExpressionManager const& manager, std::string const& valueAsString); + + /*! + * Creates an double literal expression with the rational value. + * + * @param manager The manager responsible for this expression. + * @param value The rational number that is the value of this literal expression. + */ + RationalLiteralExpression(ExpressionManager const& manager, storm::RationalNumber const& value); + + // Instantiate constructors and assignments with their default implementations. + RationalLiteralExpression(RationalLiteralExpression const& other) = default; + RationalLiteralExpression& operator=(RationalLiteralExpression const& other) = delete; +#ifndef WINDOWS + RationalLiteralExpression(RationalLiteralExpression&&) = default; + RationalLiteralExpression& operator=(RationalLiteralExpression&&) = delete; +#endif + virtual ~RationalLiteralExpression() = default; + + // Override base class methods. + virtual double evaluateAsDouble(Valuation const* valuation = nullptr) const override; + virtual bool isLiteral() const override; + virtual void gatherVariables(std::set<storm::expressions::Variable>& variables) const override; + virtual std::shared_ptr<BaseExpression const> simplify() const override; + virtual boost::any accept(ExpressionVisitor& visitor, boost::any const& data) const override; + + /*! + * Retrieves the value of the double literal. + * + * @return The value of the double literal. + */ + double getValueAsDouble() const; + + /*! + * Retrieves the value of the double literal. + * + * @return The value of the double literal. + */ + storm::RationalNumber getValue() const; + + protected: + // Override base class method. + virtual void printToStream(std::ostream& stream) const override; + + private: + // The value of the literal. + storm::RationalNumber value; + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_RationalLiteralExpression_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/SubstitutionVisitor.cpp b/src/storage/expressions/SubstitutionVisitor.cpp index c736de156..c2b4ff419 100644 --- a/src/storage/expressions/SubstitutionVisitor.cpp +++ b/src/storage/expressions/SubstitutionVisitor.cpp @@ -14,14 +14,14 @@ namespace storm { template<typename MapType> Expression SubstitutionVisitor<MapType>::substitute(Expression const& expression) { - return Expression(boost::any_cast<std::shared_ptr<BaseExpression const>>(expression.getBaseExpression().accept(*this))); + return Expression(boost::any_cast<std::shared_ptr<BaseExpression const>>(expression.getBaseExpression().accept(*this, boost::none))); } template<typename MapType> - boost::any SubstitutionVisitor<MapType>::visit(IfThenElseExpression const& expression) { - std::shared_ptr<BaseExpression const> conditionExpression = boost::any_cast<std::shared_ptr<BaseExpression const>>(expression.getCondition()->accept(*this)); - std::shared_ptr<BaseExpression const> thenExpression = boost::any_cast<std::shared_ptr<BaseExpression const>>(expression.getThenExpression()->accept(*this)); - std::shared_ptr<BaseExpression const> elseExpression = boost::any_cast<std::shared_ptr<BaseExpression const>>(expression.getElseExpression()->accept(*this)); + boost::any SubstitutionVisitor<MapType>::visit(IfThenElseExpression const& expression, boost::any const& data) { + std::shared_ptr<BaseExpression const> conditionExpression = boost::any_cast<std::shared_ptr<BaseExpression const>>(expression.getCondition()->accept(*this, data)); + std::shared_ptr<BaseExpression const> thenExpression = boost::any_cast<std::shared_ptr<BaseExpression const>>(expression.getThenExpression()->accept(*this, data)); + std::shared_ptr<BaseExpression const> elseExpression = boost::any_cast<std::shared_ptr<BaseExpression const>>(expression.getElseExpression()->accept(*this, data)); // If the arguments did not change, we simply push the expression itself. if (conditionExpression.get() == expression.getCondition().get() && thenExpression.get() == expression.getThenExpression().get() && elseExpression.get() == expression.getElseExpression().get()) { @@ -32,9 +32,9 @@ namespace storm { } template<typename MapType> - boost::any SubstitutionVisitor<MapType>::visit(BinaryBooleanFunctionExpression const& expression) { - std::shared_ptr<BaseExpression const> firstExpression = boost::any_cast<std::shared_ptr<BaseExpression const>>(expression.getFirstOperand()->accept(*this)); - std::shared_ptr<BaseExpression const> secondExpression = boost::any_cast<std::shared_ptr<BaseExpression const>>(expression.getSecondOperand()->accept(*this)); + boost::any SubstitutionVisitor<MapType>::visit(BinaryBooleanFunctionExpression const& expression, boost::any const& data) { + std::shared_ptr<BaseExpression const> firstExpression = boost::any_cast<std::shared_ptr<BaseExpression const>>(expression.getFirstOperand()->accept(*this, data)); + std::shared_ptr<BaseExpression const> secondExpression = boost::any_cast<std::shared_ptr<BaseExpression const>>(expression.getSecondOperand()->accept(*this, data)); // If the arguments did not change, we simply push the expression itself. if (firstExpression.get() == expression.getFirstOperand().get() && secondExpression.get() == expression.getSecondOperand().get()) { @@ -45,9 +45,9 @@ namespace storm { } template<typename MapType> - boost::any SubstitutionVisitor<MapType>::visit(BinaryNumericalFunctionExpression const& expression) { - std::shared_ptr<BaseExpression const> firstExpression = boost::any_cast<std::shared_ptr<BaseExpression const>>(expression.getFirstOperand()->accept(*this)); - std::shared_ptr<BaseExpression const> secondExpression = boost::any_cast<std::shared_ptr<BaseExpression const>>(expression.getSecondOperand()->accept(*this)); + boost::any SubstitutionVisitor<MapType>::visit(BinaryNumericalFunctionExpression const& expression, boost::any const& data) { + std::shared_ptr<BaseExpression const> firstExpression = boost::any_cast<std::shared_ptr<BaseExpression const>>(expression.getFirstOperand()->accept(*this, data)); + std::shared_ptr<BaseExpression const> secondExpression = boost::any_cast<std::shared_ptr<BaseExpression const>>(expression.getSecondOperand()->accept(*this, data)); // If the arguments did not change, we simply push the expression itself. if (firstExpression.get() == expression.getFirstOperand().get() && secondExpression.get() == expression.getSecondOperand().get()) { @@ -58,9 +58,9 @@ namespace storm { } template<typename MapType> - boost::any SubstitutionVisitor<MapType>::visit(BinaryRelationExpression const& expression) { - std::shared_ptr<BaseExpression const> firstExpression = boost::any_cast<std::shared_ptr<BaseExpression const>>(expression.getFirstOperand()->accept(*this)); - std::shared_ptr<BaseExpression const> secondExpression = boost::any_cast<std::shared_ptr<BaseExpression const>>(expression.getSecondOperand()->accept(*this)); + boost::any SubstitutionVisitor<MapType>::visit(BinaryRelationExpression const& expression, boost::any const& data) { + std::shared_ptr<BaseExpression const> firstExpression = boost::any_cast<std::shared_ptr<BaseExpression const>>(expression.getFirstOperand()->accept(*this, data)); + std::shared_ptr<BaseExpression const> secondExpression = boost::any_cast<std::shared_ptr<BaseExpression const>>(expression.getSecondOperand()->accept(*this, data)); // If the arguments did not change, we simply push the expression itself. if (firstExpression.get() == expression.getFirstOperand().get() && secondExpression.get() == expression.getSecondOperand().get()) { @@ -71,7 +71,7 @@ namespace storm { } template<typename MapType> - boost::any SubstitutionVisitor<MapType>::visit(VariableExpression const& expression) { + boost::any SubstitutionVisitor<MapType>::visit(VariableExpression const& expression, boost::any const& data) { // If the variable is in the key set of the substitution, we need to replace it. auto const& nameExpressionPair = this->variableToExpressionMapping.find(expression.getVariable()); if (nameExpressionPair != this->variableToExpressionMapping.end()) { @@ -82,8 +82,8 @@ namespace storm { } template<typename MapType> - boost::any SubstitutionVisitor<MapType>::visit(UnaryBooleanFunctionExpression const& expression) { - std::shared_ptr<BaseExpression const> operandExpression = boost::any_cast<std::shared_ptr<BaseExpression const>>(expression.getOperand()->accept(*this)); + boost::any SubstitutionVisitor<MapType>::visit(UnaryBooleanFunctionExpression const& expression, boost::any const& data) { + std::shared_ptr<BaseExpression const> operandExpression = boost::any_cast<std::shared_ptr<BaseExpression const>>(expression.getOperand()->accept(*this, data)); // If the argument did not change, we simply push the expression itself. if (operandExpression.get() == expression.getOperand().get()) { @@ -94,8 +94,8 @@ namespace storm { } template<typename MapType> - boost::any SubstitutionVisitor<MapType>::visit(UnaryNumericalFunctionExpression const& expression) { - std::shared_ptr<BaseExpression const> operandExpression = boost::any_cast<std::shared_ptr<BaseExpression const>>(expression.getOperand()->accept(*this)); + boost::any SubstitutionVisitor<MapType>::visit(UnaryNumericalFunctionExpression const& expression, boost::any const& data) { + std::shared_ptr<BaseExpression const> operandExpression = boost::any_cast<std::shared_ptr<BaseExpression const>>(expression.getOperand()->accept(*this, data)); // If the argument did not change, we simply push the expression itself. if (operandExpression.get() == expression.getOperand().get()) { @@ -106,17 +106,17 @@ namespace storm { } template<typename MapType> - boost::any SubstitutionVisitor<MapType>::visit(BooleanLiteralExpression const& expression) { + boost::any SubstitutionVisitor<MapType>::visit(BooleanLiteralExpression const& expression, boost::any const& data) { return expression.getSharedPointer(); } template<typename MapType> - boost::any SubstitutionVisitor<MapType>::visit(IntegerLiteralExpression const& expression) { + boost::any SubstitutionVisitor<MapType>::visit(IntegerLiteralExpression const& expression, boost::any const& data) { return expression.getSharedPointer(); } template<typename MapType> - boost::any SubstitutionVisitor<MapType>::visit(DoubleLiteralExpression const& expression) { + boost::any SubstitutionVisitor<MapType>::visit(RationalLiteralExpression const& expression, boost::any const& data) { return expression.getSharedPointer(); } diff --git a/src/storage/expressions/SubstitutionVisitor.h b/src/storage/expressions/SubstitutionVisitor.h index 343521335..2131696c4 100644 --- a/src/storage/expressions/SubstitutionVisitor.h +++ b/src/storage/expressions/SubstitutionVisitor.h @@ -28,16 +28,16 @@ namespace storm { */ Expression substitute(Expression const& expression); - virtual boost::any visit(IfThenElseExpression const& expression) override; - virtual boost::any visit(BinaryBooleanFunctionExpression const& expression) override; - virtual boost::any visit(BinaryNumericalFunctionExpression const& expression) override; - virtual boost::any visit(BinaryRelationExpression const& expression) override; - virtual boost::any visit(VariableExpression const& expression) override; - virtual boost::any visit(UnaryBooleanFunctionExpression const& expression) override; - virtual boost::any visit(UnaryNumericalFunctionExpression const& expression) override; - virtual boost::any visit(BooleanLiteralExpression const& expression) override; - virtual boost::any visit(IntegerLiteralExpression const& expression) override; - virtual boost::any visit(DoubleLiteralExpression const& expression) override; + virtual boost::any visit(IfThenElseExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BinaryBooleanFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BinaryNumericalFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BinaryRelationExpression const& expression, boost::any const& data) override; + virtual boost::any visit(VariableExpression const& expression, boost::any const& data) override; + virtual boost::any visit(UnaryBooleanFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(UnaryNumericalFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BooleanLiteralExpression const& expression, boost::any const& data) override; + virtual boost::any visit(IntegerLiteralExpression const& expression, boost::any const& data) override; + virtual boost::any visit(RationalLiteralExpression const& expression, boost::any const& data) override; private: // A mapping of variables to expressions with which they shall be replaced. diff --git a/src/storage/expressions/SyntacticalEqualityCheckVisitor.cpp b/src/storage/expressions/SyntacticalEqualityCheckVisitor.cpp new file mode 100644 index 000000000..4277a7dbb --- /dev/null +++ b/src/storage/expressions/SyntacticalEqualityCheckVisitor.cpp @@ -0,0 +1,155 @@ +#include "src/storage/expressions/SyntacticalEqualityCheckVisitor.h" + +#include "src/storage/expressions/Expressions.h" + +namespace storm { + namespace expressions { + + bool SyntacticalEqualityCheckVisitor::isSyntaticallyEqual(storm::expressions::Expression const& expression1, storm::expressions::Expression const& expression2) { + return boost::any_cast<bool>(expression1.accept(*this, std::ref(expression2.getBaseExpression()))); + } + + boost::any SyntacticalEqualityCheckVisitor::visit(IfThenElseExpression const& expression, boost::any const& data) { + BaseExpression const& otherBaseExpression = boost::any_cast<std::reference_wrapper<BaseExpression const>>(data).get(); + if (otherBaseExpression.isIfThenElseExpression()) { + IfThenElseExpression const& otherExpression = otherBaseExpression.asIfThenElseExpression(); + + bool result = boost::any_cast<bool>(expression.getCondition()->accept(*this, std::ref(*otherExpression.getCondition()))); + if (result) { + result = boost::any_cast<bool>(expression.getThenExpression()->accept(*this, std::ref(*otherExpression.getThenExpression()))); + } + if (result) { + result = boost::any_cast<bool>(expression.getElseExpression()->accept(*this, std::ref(*otherExpression.getElseExpression()))); + } + return result; + } else { + return false; + } + } + + boost::any SyntacticalEqualityCheckVisitor::visit(BinaryBooleanFunctionExpression const& expression, boost::any const& data) { + BaseExpression const& otherBaseExpression = boost::any_cast<std::reference_wrapper<BaseExpression const>>(data).get(); + if (otherBaseExpression.isBinaryBooleanFunctionExpression()) { + BinaryBooleanFunctionExpression const& otherExpression = otherBaseExpression.asBinaryBooleanFunctionExpression(); + + bool result = expression.getOperatorType() == otherExpression.getOperatorType(); + if (result) { + result = boost::any_cast<bool>(expression.getFirstOperand()->accept(*this, std::ref(*otherExpression.getFirstOperand()))); + } + if (result) { + result = boost::any_cast<bool>(expression.getSecondOperand()->accept(*this, std::ref(*otherExpression.getSecondOperand()))); + } + return result; + } else { + return false; + } + } + + boost::any SyntacticalEqualityCheckVisitor::visit(BinaryNumericalFunctionExpression const& expression, boost::any const& data) { + BaseExpression const& otherBaseExpression = boost::any_cast<std::reference_wrapper<BaseExpression const>>(data).get(); + if (otherBaseExpression.isBinaryNumericalFunctionExpression()) { + BinaryNumericalFunctionExpression const& otherExpression = otherBaseExpression.asBinaryNumericalFunctionExpression(); + + bool result = expression.getOperatorType() == otherExpression.getOperatorType(); + if (result) { + result = boost::any_cast<bool>(expression.getFirstOperand()->accept(*this, std::ref(*otherExpression.getFirstOperand()))); + } + if (result) { + result = boost::any_cast<bool>(expression.getSecondOperand()->accept(*this, std::ref(*otherExpression.getSecondOperand()))); + } + return result; + } else { + return false; + } + } + + boost::any SyntacticalEqualityCheckVisitor::visit(BinaryRelationExpression const& expression, boost::any const& data) { + BaseExpression const& otherBaseExpression = boost::any_cast<std::reference_wrapper<BaseExpression const>>(data).get(); + if (otherBaseExpression.isBinaryRelationExpression()) { + BinaryRelationExpression const& otherExpression = otherBaseExpression.asBinaryRelationExpression(); + + bool result = expression.getRelationType() == otherExpression.getRelationType(); + if (result) { + result = boost::any_cast<bool>(expression.getFirstOperand()->accept(*this, std::ref(*otherExpression.getFirstOperand()))); + } + if (result) { + result = boost::any_cast<bool>(expression.getSecondOperand()->accept(*this, std::ref(*otherExpression.getSecondOperand()))); + } + return result; + } else { + return false; + } + } + + boost::any SyntacticalEqualityCheckVisitor::visit(VariableExpression const& expression, boost::any const& data) { + BaseExpression const& otherBaseExpression = boost::any_cast<std::reference_wrapper<BaseExpression const>>(data).get(); + if (otherBaseExpression.isVariableExpression()) { + VariableExpression const& otherExpression = otherBaseExpression.asVariableExpression(); + return expression.getVariable() == otherExpression.getVariable(); + } else { + return false; + } + } + + boost::any SyntacticalEqualityCheckVisitor::visit(UnaryBooleanFunctionExpression const& expression, boost::any const& data) { + BaseExpression const& otherBaseExpression = boost::any_cast<std::reference_wrapper<BaseExpression const>>(data).get(); + if (otherBaseExpression.isBinaryBooleanFunctionExpression()) { + UnaryBooleanFunctionExpression const& otherExpression = otherBaseExpression.asUnaryBooleanFunctionExpression(); + + bool result = expression.getOperatorType() == otherExpression.getOperatorType(); + if (result) { + result = boost::any_cast<bool>(expression.getOperand()->accept(*this, std::ref(*otherExpression.getOperand()))); + } + return result; + } else { + return false; + } + } + + boost::any SyntacticalEqualityCheckVisitor::visit(UnaryNumericalFunctionExpression const& expression, boost::any const& data) { + BaseExpression const& otherBaseExpression = boost::any_cast<std::reference_wrapper<BaseExpression const>>(data).get(); + if (otherBaseExpression.isBinaryBooleanFunctionExpression()) { + UnaryNumericalFunctionExpression const& otherExpression = otherBaseExpression.asUnaryNumericalFunctionExpression(); + + bool result = expression.getOperatorType() == otherExpression.getOperatorType(); + if (result) { + result = boost::any_cast<bool>(expression.getOperand()->accept(*this, std::ref(*otherExpression.getOperand()))); + } + return result; + } else { + return false; + } + } + + boost::any SyntacticalEqualityCheckVisitor::visit(BooleanLiteralExpression const& expression, boost::any const& data) { + BaseExpression const& otherBaseExpression = boost::any_cast<std::reference_wrapper<BaseExpression const>>(data).get(); + if (otherBaseExpression.isBooleanLiteralExpression()) { + BooleanLiteralExpression const& otherExpression = otherBaseExpression.asBooleanLiteralExpression(); + return expression.getValue() == otherExpression.getValue(); + } else { + return false; + } + } + + boost::any SyntacticalEqualityCheckVisitor::visit(IntegerLiteralExpression const& expression, boost::any const& data) { + BaseExpression const& otherBaseExpression = boost::any_cast<std::reference_wrapper<BaseExpression const>>(data).get(); + if (otherBaseExpression.isIntegerLiteralExpression()) { + IntegerLiteralExpression const& otherExpression = otherBaseExpression.asIntegerLiteralExpression(); + return expression.getValue() == otherExpression.getValue(); + } else { + return false; + } + } + + boost::any SyntacticalEqualityCheckVisitor::visit(RationalLiteralExpression const& expression, boost::any const& data) { + BaseExpression const& otherBaseExpression = boost::any_cast<std::reference_wrapper<BaseExpression const>>(data).get(); + if (otherBaseExpression.isRationalLiteralExpression()) { + RationalLiteralExpression const& otherExpression = otherBaseExpression.asRationalLiteralExpression(); + return expression.getValue() == otherExpression.getValue(); + } else { + return false; + } + } + + } +} \ No newline at end of file diff --git a/src/storage/expressions/SyntacticalEqualityCheckVisitor.h b/src/storage/expressions/SyntacticalEqualityCheckVisitor.h new file mode 100644 index 000000000..82366e507 --- /dev/null +++ b/src/storage/expressions/SyntacticalEqualityCheckVisitor.h @@ -0,0 +1,27 @@ +#pragma once + +#include "src/storage/expressions/ExpressionVisitor.h" + +namespace storm { + namespace expressions { + + class Expression; + + class SyntacticalEqualityCheckVisitor : public ExpressionVisitor { + public: + bool isSyntaticallyEqual(storm::expressions::Expression const& expression1, storm::expressions::Expression const& expression2); + + virtual boost::any visit(IfThenElseExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BinaryBooleanFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BinaryNumericalFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BinaryRelationExpression const& expression, boost::any const& data) override; + virtual boost::any visit(VariableExpression const& expression, boost::any const& data) override; + virtual boost::any visit(UnaryBooleanFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(UnaryNumericalFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BooleanLiteralExpression const& expression, boost::any const& data) override; + virtual boost::any visit(IntegerLiteralExpression const& expression, boost::any const& data) override; + virtual boost::any visit(RationalLiteralExpression const& expression, boost::any const& data) override; + }; + + } +} \ No newline at end of file diff --git a/src/storage/expressions/ToExprtkStringVisitor.cpp b/src/storage/expressions/ToExprtkStringVisitor.cpp index 51d9c1f34..88ab1132a 100644 --- a/src/storage/expressions/ToExprtkStringVisitor.cpp +++ b/src/storage/expressions/ToExprtkStringVisitor.cpp @@ -9,210 +9,210 @@ namespace storm { std::string ToExprtkStringVisitor::toString(BaseExpression const* expression) { stream.str(""); stream.clear(); - expression->accept(*this); + expression->accept(*this, boost::none); return stream.str(); } - boost::any ToExprtkStringVisitor::visit(IfThenElseExpression const& expression) { + boost::any ToExprtkStringVisitor::visit(IfThenElseExpression const& expression, boost::any const& data) { stream << "if("; - expression.getCondition()->accept(*this); + expression.getCondition()->accept(*this, data); stream << ","; - expression.getThenExpression()->accept(*this); + expression.getThenExpression()->accept(*this, data); stream << ","; - expression.getElseExpression()->accept(*this); + expression.getElseExpression()->accept(*this, data); stream << ")"; return boost::any(); } - boost::any ToExprtkStringVisitor::visit(BinaryBooleanFunctionExpression const& expression) { + boost::any ToExprtkStringVisitor::visit(BinaryBooleanFunctionExpression const& expression, boost::any const& data) { switch (expression.getOperatorType()) { case BinaryBooleanFunctionExpression::OperatorType::And: stream << "("; - expression.getFirstOperand()->accept(*this); + expression.getFirstOperand()->accept(*this, data); stream << " and "; - expression.getSecondOperand()->accept(*this); + expression.getSecondOperand()->accept(*this, data); stream << ")"; break; case BinaryBooleanFunctionExpression::OperatorType::Or: stream << "("; - expression.getFirstOperand()->accept(*this); + expression.getFirstOperand()->accept(*this, data); stream << " or "; - expression.getSecondOperand()->accept(*this); + expression.getSecondOperand()->accept(*this, data); stream << ")"; break; case BinaryBooleanFunctionExpression::OperatorType::Xor: stream << "("; - expression.getFirstOperand()->accept(*this); + expression.getFirstOperand()->accept(*this, data); stream << " xor "; - expression.getSecondOperand()->accept(*this); + expression.getSecondOperand()->accept(*this, data); stream << ")"; break; case BinaryBooleanFunctionExpression::OperatorType::Implies: stream << "(not("; - expression.getFirstOperand()->accept(*this); + expression.getFirstOperand()->accept(*this, data); stream << ") or "; - expression.getSecondOperand()->accept(*this); + expression.getSecondOperand()->accept(*this, data); stream << ")"; break; case BinaryBooleanFunctionExpression::OperatorType::Iff: - expression.getFirstOperand()->accept(*this); + expression.getFirstOperand()->accept(*this, data); stream << "=="; - expression.getSecondOperand()->accept(*this); + expression.getSecondOperand()->accept(*this, data); break; } return boost::any(); } - boost::any ToExprtkStringVisitor::visit(BinaryNumericalFunctionExpression const& expression) { + boost::any ToExprtkStringVisitor::visit(BinaryNumericalFunctionExpression const& expression, boost::any const& data) { switch (expression.getOperatorType()) { case BinaryNumericalFunctionExpression::OperatorType::Plus: stream << "("; - expression.getFirstOperand()->accept(*this); + expression.getFirstOperand()->accept(*this, data); stream << "+"; - expression.getSecondOperand()->accept(*this); + expression.getSecondOperand()->accept(*this, data); stream << ")"; break; case BinaryNumericalFunctionExpression::OperatorType::Minus: stream << "("; - expression.getFirstOperand()->accept(*this); + expression.getFirstOperand()->accept(*this, data); stream << "-"; - expression.getSecondOperand()->accept(*this); + expression.getSecondOperand()->accept(*this, data); stream << ")"; break; case BinaryNumericalFunctionExpression::OperatorType::Times: stream << "("; - expression.getFirstOperand()->accept(*this); + expression.getFirstOperand()->accept(*this, data); stream << "*"; - expression.getSecondOperand()->accept(*this); + expression.getSecondOperand()->accept(*this, data); stream << ")"; break; case BinaryNumericalFunctionExpression::OperatorType::Divide: stream << "("; - expression.getFirstOperand()->accept(*this); + expression.getFirstOperand()->accept(*this, data); stream << "/"; - expression.getSecondOperand()->accept(*this); + expression.getSecondOperand()->accept(*this, data); stream << ")"; break; case BinaryNumericalFunctionExpression::OperatorType::Power: stream << "("; - expression.getFirstOperand()->accept(*this); + expression.getFirstOperand()->accept(*this, data); stream << "^"; - expression.getSecondOperand()->accept(*this); + expression.getSecondOperand()->accept(*this, data); stream << ")"; break; case BinaryNumericalFunctionExpression::OperatorType::Max: stream << "max("; - expression.getFirstOperand()->accept(*this); + expression.getFirstOperand()->accept(*this, data); stream << ","; - expression.getSecondOperand()->accept(*this); + expression.getSecondOperand()->accept(*this, data); stream << ")"; break; case BinaryNumericalFunctionExpression::OperatorType::Min: stream << "min("; - expression.getFirstOperand()->accept(*this); + expression.getFirstOperand()->accept(*this, data); stream << ","; - expression.getSecondOperand()->accept(*this); + expression.getSecondOperand()->accept(*this, data); stream << ")"; break; } return boost::any(); } - boost::any ToExprtkStringVisitor::visit(BinaryRelationExpression const& expression) { + boost::any ToExprtkStringVisitor::visit(BinaryRelationExpression const& expression, boost::any const& data) { switch (expression.getRelationType()) { case BinaryRelationExpression::RelationType::Equal: stream << "("; - expression.getFirstOperand()->accept(*this); + expression.getFirstOperand()->accept(*this, data); stream << "=="; - expression.getSecondOperand()->accept(*this); + expression.getSecondOperand()->accept(*this, data); stream << ")"; break; case BinaryRelationExpression::RelationType::NotEqual: stream << "("; - expression.getFirstOperand()->accept(*this); + expression.getFirstOperand()->accept(*this, data); stream << "!="; - expression.getSecondOperand()->accept(*this); + expression.getSecondOperand()->accept(*this, data); stream << ")"; break; case BinaryRelationExpression::RelationType::Less: stream << "("; - expression.getFirstOperand()->accept(*this); + expression.getFirstOperand()->accept(*this, data); stream << "<"; - expression.getSecondOperand()->accept(*this); + expression.getSecondOperand()->accept(*this, data); stream << ")"; break; case BinaryRelationExpression::RelationType::LessOrEqual: stream << "("; - expression.getFirstOperand()->accept(*this); + expression.getFirstOperand()->accept(*this, data); stream << "<="; - expression.getSecondOperand()->accept(*this); + expression.getSecondOperand()->accept(*this, data); stream << ")"; break; case BinaryRelationExpression::RelationType::Greater: stream << "("; - expression.getFirstOperand()->accept(*this); + expression.getFirstOperand()->accept(*this, data); stream << ">"; - expression.getSecondOperand()->accept(*this); + expression.getSecondOperand()->accept(*this, data); stream << ")"; break; case BinaryRelationExpression::RelationType::GreaterOrEqual: stream << "("; - expression.getFirstOperand()->accept(*this); + expression.getFirstOperand()->accept(*this, data); stream << ">="; - expression.getSecondOperand()->accept(*this); + expression.getSecondOperand()->accept(*this, data); stream << ")"; break; } return boost::any(); } - boost::any ToExprtkStringVisitor::visit(VariableExpression const& expression) { + boost::any ToExprtkStringVisitor::visit(VariableExpression const& expression, boost::any const& data) { stream << expression.getVariableName(); return boost::any(); } - boost::any ToExprtkStringVisitor::visit(UnaryBooleanFunctionExpression const& expression) { + boost::any ToExprtkStringVisitor::visit(UnaryBooleanFunctionExpression const& expression, boost::any const& data) { switch (expression.getOperatorType()) { case UnaryBooleanFunctionExpression::OperatorType::Not: stream << "not("; - expression.getOperand()->accept(*this); + expression.getOperand()->accept(*this, data); stream << ")"; } return boost::any(); } - boost::any ToExprtkStringVisitor::visit(UnaryNumericalFunctionExpression const& expression) { + boost::any ToExprtkStringVisitor::visit(UnaryNumericalFunctionExpression const& expression, boost::any const& data) { switch (expression.getOperatorType()) { case UnaryNumericalFunctionExpression::OperatorType::Minus: stream << "-("; - expression.getOperand()->accept(*this); + expression.getOperand()->accept(*this, data); stream << ")"; break; case UnaryNumericalFunctionExpression::OperatorType::Floor: stream << "floor("; - expression.getOperand()->accept(*this); + expression.getOperand()->accept(*this, data); stream << ")"; break; case UnaryNumericalFunctionExpression::OperatorType::Ceil: stream << "ceil("; - expression.getOperand()->accept(*this); + expression.getOperand()->accept(*this, data); stream << ")"; break; } return boost::any(); } - boost::any ToExprtkStringVisitor::visit(BooleanLiteralExpression const& expression) { + boost::any ToExprtkStringVisitor::visit(BooleanLiteralExpression const& expression, boost::any const& data) { stream << expression.getValue(); return boost::any(); } - boost::any ToExprtkStringVisitor::visit(IntegerLiteralExpression const& expression) { + boost::any ToExprtkStringVisitor::visit(IntegerLiteralExpression const& expression, boost::any const& data) { stream << expression.getValue(); return boost::any(); } - boost::any ToExprtkStringVisitor::visit(DoubleLiteralExpression const& expression) { + boost::any ToExprtkStringVisitor::visit(RationalLiteralExpression const& expression, boost::any const& data) { stream << expression.getValue(); return boost::any(); } diff --git a/src/storage/expressions/ToExprtkStringVisitor.h b/src/storage/expressions/ToExprtkStringVisitor.h index 6c285ff28..efab0a176 100644 --- a/src/storage/expressions/ToExprtkStringVisitor.h +++ b/src/storage/expressions/ToExprtkStringVisitor.h @@ -16,16 +16,16 @@ namespace storm { std::string toString(Expression const& expression); std::string toString(BaseExpression const* expression); - virtual boost::any visit(IfThenElseExpression const& expression) override; - virtual boost::any visit(BinaryBooleanFunctionExpression const& expression) override; - virtual boost::any visit(BinaryNumericalFunctionExpression const& expression) override; - virtual boost::any visit(BinaryRelationExpression const& expression) override; - virtual boost::any visit(VariableExpression const& expression) override; - virtual boost::any visit(UnaryBooleanFunctionExpression const& expression) override; - virtual boost::any visit(UnaryNumericalFunctionExpression const& expression) override; - virtual boost::any visit(BooleanLiteralExpression const& expression) override; - virtual boost::any visit(IntegerLiteralExpression const& expression) override; - virtual boost::any visit(DoubleLiteralExpression const& expression) override; + virtual boost::any visit(IfThenElseExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BinaryBooleanFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BinaryNumericalFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BinaryRelationExpression const& expression, boost::any const& data) override; + virtual boost::any visit(VariableExpression const& expression, boost::any const& data) override; + virtual boost::any visit(UnaryBooleanFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(UnaryNumericalFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BooleanLiteralExpression const& expression, boost::any const& data) override; + virtual boost::any visit(IntegerLiteralExpression const& expression, boost::any const& data) override; + virtual boost::any visit(RationalLiteralExpression const& expression, boost::any const& data) override; private: std::stringstream stream; diff --git a/src/storage/expressions/ToRationalFunctionVisitor.cpp b/src/storage/expressions/ToRationalFunctionVisitor.cpp index 1a3dd0fc3..91a3cdc94 100644 --- a/src/storage/expressions/ToRationalFunctionVisitor.cpp +++ b/src/storage/expressions/ToRationalFunctionVisitor.cpp @@ -2,6 +2,7 @@ #include <sstream> +#include "src/utility/constants.h" #include "src/utility/macros.h" #include "src/exceptions/InvalidArgumentException.h" @@ -16,23 +17,24 @@ namespace storm { template<typename RationalFunctionType> RationalFunctionType ToRationalFunctionVisitor<RationalFunctionType>::toRationalFunction(Expression const& expression) { - return boost::any_cast<RationalFunctionType>(expression.accept(*this)); + return boost::any_cast<RationalFunctionType>(expression.accept(*this, boost::none)); } template<typename RationalFunctionType> - boost::any ToRationalFunctionVisitor<RationalFunctionType>::visit(IfThenElseExpression const& expression) { + boost::any ToRationalFunctionVisitor<RationalFunctionType>::visit(IfThenElseExpression const& expression, boost::any const& data) { STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression cannot be translated into a rational function."); } template<typename RationalFunctionType> - boost::any ToRationalFunctionVisitor<RationalFunctionType>::visit(BinaryBooleanFunctionExpression const& expression) { + boost::any ToRationalFunctionVisitor<RationalFunctionType>::visit(BinaryBooleanFunctionExpression const& expression, boost::any const& data) { STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression cannot be translated into a rational function."); } template<typename RationalFunctionType> - boost::any ToRationalFunctionVisitor<RationalFunctionType>::visit(BinaryNumericalFunctionExpression const& expression) { - RationalFunctionType firstOperandAsRationalFunction = boost::any_cast<RationalFunctionType>(expression.getFirstOperand()->accept(*this)); - RationalFunctionType secondOperandAsRationalFunction = boost::any_cast<RationalFunctionType>(expression.getSecondOperand()->accept(*this)); + boost::any ToRationalFunctionVisitor<RationalFunctionType>::visit(BinaryNumericalFunctionExpression const& expression, boost::any const& data) { + RationalFunctionType firstOperandAsRationalFunction = boost::any_cast<RationalFunctionType>(expression.getFirstOperand()->accept(*this, data)); + RationalFunctionType secondOperandAsRationalFunction = boost::any_cast<RationalFunctionType>(expression.getSecondOperand()->accept(*this, data)); + uint_fast64_t exponentAsInteger = 0; switch(expression.getOperatorType()) { case BinaryNumericalFunctionExpression::OperatorType::Plus: return firstOperandAsRationalFunction + secondOperandAsRationalFunction; @@ -46,6 +48,11 @@ namespace storm { case BinaryNumericalFunctionExpression::OperatorType::Divide: return firstOperandAsRationalFunction / secondOperandAsRationalFunction; break; + case BinaryNumericalFunctionExpression::OperatorType::Power: + STORM_LOG_THROW(storm::utility::isInteger(secondOperandAsRationalFunction), storm::exceptions::InvalidArgumentException, "Exponent of power operator must be a positive integer."); + exponentAsInteger = storm::utility::convertNumber<uint_fast64_t>(secondOperandAsRationalFunction); + return storm::utility::pow(firstOperandAsRationalFunction, exponentAsInteger); + break; default: STORM_LOG_ASSERT(false, "Illegal operator type."); } @@ -55,12 +62,12 @@ namespace storm { } template<typename RationalFunctionType> - boost::any ToRationalFunctionVisitor<RationalFunctionType>::visit(BinaryRelationExpression const& expression) { + boost::any ToRationalFunctionVisitor<RationalFunctionType>::visit(BinaryRelationExpression const& expression, boost::any const& data) { STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression cannot be translated into a rational function."); } template<typename RationalFunctionType> - boost::any ToRationalFunctionVisitor<RationalFunctionType>::visit(VariableExpression const& expression) { + boost::any ToRationalFunctionVisitor<RationalFunctionType>::visit(VariableExpression const& expression, boost::any const& data) { auto variablePair = variableToVariableMap.find(expression.getVariable()); if (variablePair != variableToVariableMap.end()) { return convertVariableToPolynomial(variablePair->second); @@ -72,28 +79,28 @@ namespace storm { } template<typename RationalFunctionType> - boost::any ToRationalFunctionVisitor<RationalFunctionType>::visit(UnaryBooleanFunctionExpression const& expression) { + boost::any ToRationalFunctionVisitor<RationalFunctionType>::visit(UnaryBooleanFunctionExpression const& expression, boost::any const& data) { STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression cannot be translated into a rational function."); } template<typename RationalFunctionType> - boost::any ToRationalFunctionVisitor<RationalFunctionType>::visit(UnaryNumericalFunctionExpression const& expression) { + boost::any ToRationalFunctionVisitor<RationalFunctionType>::visit(UnaryNumericalFunctionExpression const& expression, boost::any const& data) { STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression cannot be translated into a rational function."); } template<typename RationalFunctionType> - boost::any ToRationalFunctionVisitor<RationalFunctionType>::visit(BooleanLiteralExpression const& expression) { + boost::any ToRationalFunctionVisitor<RationalFunctionType>::visit(BooleanLiteralExpression const& expression, boost::any const& data) { STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression cannot be translated into a rational function."); } template<typename RationalFunctionType> - boost::any ToRationalFunctionVisitor<RationalFunctionType>::visit(IntegerLiteralExpression const& expression) { + boost::any ToRationalFunctionVisitor<RationalFunctionType>::visit(IntegerLiteralExpression const& expression, boost::any const& data) { return RationalFunctionType(carl::rationalize<storm::RationalNumber>(static_cast<size_t>(expression.getValue()))); } template<typename RationalFunctionType> - boost::any ToRationalFunctionVisitor<RationalFunctionType>::visit(DoubleLiteralExpression const& expression) { - return RationalFunctionType(carl::rationalize<storm::RationalNumber>(expression.getValue())); + boost::any ToRationalFunctionVisitor<RationalFunctionType>::visit(RationalLiteralExpression const& expression, boost::any const& data) { + return storm::utility::convertNumber<storm::RationalFunction>(expression.getValue()); } template class ToRationalFunctionVisitor<storm::RationalFunction>; diff --git a/src/storage/expressions/ToRationalFunctionVisitor.h b/src/storage/expressions/ToRationalFunctionVisitor.h index 12603f44e..0a30d7793 100644 --- a/src/storage/expressions/ToRationalFunctionVisitor.h +++ b/src/storage/expressions/ToRationalFunctionVisitor.h @@ -18,16 +18,16 @@ namespace storm { RationalFunctionType toRationalFunction(Expression const& expression); - virtual boost::any visit(IfThenElseExpression const& expression) override; - virtual boost::any visit(BinaryBooleanFunctionExpression const& expression) override; - virtual boost::any visit(BinaryNumericalFunctionExpression const& expression) override; - virtual boost::any visit(BinaryRelationExpression const& expression) override; - virtual boost::any visit(VariableExpression const& expression) override; - virtual boost::any visit(UnaryBooleanFunctionExpression const& expression) override; - virtual boost::any visit(UnaryNumericalFunctionExpression const& expression) override; - virtual boost::any visit(BooleanLiteralExpression const& expression) override; - virtual boost::any visit(IntegerLiteralExpression const& expression) override; - virtual boost::any visit(DoubleLiteralExpression const& expression) override; + virtual boost::any visit(IfThenElseExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BinaryBooleanFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BinaryNumericalFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BinaryRelationExpression const& expression, boost::any const& data) override; + virtual boost::any visit(VariableExpression const& expression, boost::any const& data) override; + virtual boost::any visit(UnaryBooleanFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(UnaryNumericalFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BooleanLiteralExpression const& expression, boost::any const& data) override; + virtual boost::any visit(IntegerLiteralExpression const& expression, boost::any const& data) override; + virtual boost::any visit(RationalLiteralExpression const& expression, boost::any const& data) override; private: template<typename TP = typename RationalFunctionType::PolyType, carl::EnableIf<carl::needs_cache<TP>> = carl::dummy> diff --git a/src/storage/expressions/ToRationalNumberVisitor.cpp b/src/storage/expressions/ToRationalNumberVisitor.cpp index 785f36950..cfe2cbb35 100644 --- a/src/storage/expressions/ToRationalNumberVisitor.cpp +++ b/src/storage/expressions/ToRationalNumberVisitor.cpp @@ -1,6 +1,7 @@ #include "src/storage/expressions/ToRationalNumberVisitor.h" #include "src/utility/macros.h" +#include "src/utility/constants.h" #include "src/exceptions/InvalidArgumentException.h" #include "src/exceptions/NotSupportedException.h" @@ -13,71 +14,81 @@ namespace storm { template<typename RationalNumberType> RationalNumberType ToRationalNumberVisitor<RationalNumberType>::toRationalNumber(Expression const& expression) { - return boost::any_cast<RationalNumberType>(expression.accept(*this)); + return boost::any_cast<RationalNumberType>(expression.accept(*this, boost::none)); } template<typename RationalNumberType> - boost::any ToRationalNumberVisitor<RationalNumberType>::visit(IfThenElseExpression const& expression) { + boost::any ToRationalNumberVisitor<RationalNumberType>::visit(IfThenElseExpression const& expression, boost::any const& data) { STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression cannot be translated into a rational number."); } template<typename RationalNumberType> - boost::any ToRationalNumberVisitor<RationalNumberType>::visit(BinaryBooleanFunctionExpression const& expression) { + boost::any ToRationalNumberVisitor<RationalNumberType>::visit(BinaryBooleanFunctionExpression const& expression, boost::any const& data) { STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression cannot be translated into a rational number."); } template<typename RationalNumberType> - boost::any ToRationalNumberVisitor<RationalNumberType>::visit(BinaryNumericalFunctionExpression const& expression) { - RationalNumberType firstOperandAsRationalFunction = boost::any_cast<RationalNumberType>(expression.getFirstOperand()->accept(*this)); - RationalNumberType secondOperandAsRationalFunction = boost::any_cast<RationalNumberType>(expression.getSecondOperand()->accept(*this)); + boost::any ToRationalNumberVisitor<RationalNumberType>::visit(BinaryNumericalFunctionExpression const& expression, boost::any const& data) { + RationalNumberType firstOperandAsRationalNumber = boost::any_cast<RationalNumberType>(expression.getFirstOperand()->accept(*this, data)); + RationalNumberType secondOperandAsRationalNumber = boost::any_cast<RationalNumberType>(expression.getSecondOperand()->accept(*this, data)); switch(expression.getOperatorType()) { case BinaryNumericalFunctionExpression::OperatorType::Plus: - return firstOperandAsRationalFunction + secondOperandAsRationalFunction; + return firstOperandAsRationalNumber + secondOperandAsRationalNumber; break; case BinaryNumericalFunctionExpression::OperatorType::Minus: - return firstOperandAsRationalFunction - secondOperandAsRationalFunction; + return firstOperandAsRationalNumber - secondOperandAsRationalNumber; break; case BinaryNumericalFunctionExpression::OperatorType::Times: - return firstOperandAsRationalFunction * secondOperandAsRationalFunction; + return firstOperandAsRationalNumber * secondOperandAsRationalNumber; break; case BinaryNumericalFunctionExpression::OperatorType::Divide: - return firstOperandAsRationalFunction / secondOperandAsRationalFunction; + return firstOperandAsRationalNumber / secondOperandAsRationalNumber; + break; + case BinaryNumericalFunctionExpression::OperatorType::Min: + return std::min(firstOperandAsRationalNumber, secondOperandAsRationalNumber); + break; + case BinaryNumericalFunctionExpression::OperatorType::Max: + return std::max(firstOperandAsRationalNumber, secondOperandAsRationalNumber); + break; + case BinaryNumericalFunctionExpression::OperatorType::Power: + STORM_LOG_THROW(storm::utility::isInteger(secondOperandAsRationalNumber), storm::exceptions::InvalidArgumentException, "Exponent of power operator must be a positive integer."); + uint_fast64_t exponentAsInteger = storm::utility::convertNumber<uint_fast64_t>(secondOperandAsRationalNumber); + return storm::utility::pow(firstOperandAsRationalNumber, exponentAsInteger); break; - default: - STORM_LOG_ASSERT(false, "Illegal operator type."); } // Return a dummy. This point must, however, never be reached. + STORM_LOG_ASSERT(false, "Illegal operator type."); return boost::any(); } template<typename RationalNumberType> - boost::any ToRationalNumberVisitor<RationalNumberType>::visit(BinaryRelationExpression const& expression) { + boost::any ToRationalNumberVisitor<RationalNumberType>::visit(BinaryRelationExpression const& expression, boost::any const& data) { STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression cannot be translated into a rational number."); } template<typename RationalNumberType> - boost::any ToRationalNumberVisitor<RationalNumberType>::visit(VariableExpression const& expression) { + boost::any ToRationalNumberVisitor<RationalNumberType>::visit(VariableExpression const& expression, boost::any const& data) { STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Cannot transform expressions containing variables to a rational number."); } template<typename RationalNumberType> - boost::any ToRationalNumberVisitor<RationalNumberType>::visit(UnaryBooleanFunctionExpression const& expression) { + boost::any ToRationalNumberVisitor<RationalNumberType>::visit(UnaryBooleanFunctionExpression const& expression, boost::any const& data) { STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression cannot be translated into a rational number."); } template<typename RationalNumberType> - boost::any ToRationalNumberVisitor<RationalNumberType>::visit(UnaryNumericalFunctionExpression const& expression) { + boost::any ToRationalNumberVisitor<RationalNumberType>::visit(UnaryNumericalFunctionExpression const& expression, boost::any const& data) { STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression cannot be translated into a rational number."); } template<typename RationalNumberType> - boost::any ToRationalNumberVisitor<RationalNumberType>::visit(BooleanLiteralExpression const& expression) { + boost::any ToRationalNumberVisitor<RationalNumberType>::visit(BooleanLiteralExpression const& expression, boost::any const& data) { STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression cannot be translated into a rational number."); } template<typename RationalNumberType> - boost::any ToRationalNumberVisitor<RationalNumberType>::visit(IntegerLiteralExpression const& expression) { + boost::any ToRationalNumberVisitor<RationalNumberType>::visit(IntegerLiteralExpression const& expression, boost::any const& data) { #ifdef STORM_HAVE_CARL return RationalNumberType(carl::rationalize<storm::RationalNumber>(static_cast<size_t>(expression.getValue()))); #else @@ -86,9 +97,9 @@ namespace storm { } template<typename RationalNumberType> - boost::any ToRationalNumberVisitor<RationalNumberType>::visit(DoubleLiteralExpression const& expression) { + boost::any ToRationalNumberVisitor<RationalNumberType>::visit(RationalLiteralExpression const& expression, boost::any const& data) { #ifdef STORM_HAVE_CARL - return RationalNumberType(carl::rationalize<storm::RationalNumber>(expression.getValue())); + return expression.getValue(); #else STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Rational numbers are not supported in this build."); #endif diff --git a/src/storage/expressions/ToRationalNumberVisitor.h b/src/storage/expressions/ToRationalNumberVisitor.h index 2254931f6..da803cf23 100644 --- a/src/storage/expressions/ToRationalNumberVisitor.h +++ b/src/storage/expressions/ToRationalNumberVisitor.h @@ -16,16 +16,16 @@ namespace storm { RationalNumberType toRationalNumber(Expression const& expression); - virtual boost::any visit(IfThenElseExpression const& expression) override; - virtual boost::any visit(BinaryBooleanFunctionExpression const& expression) override; - virtual boost::any visit(BinaryNumericalFunctionExpression const& expression) override; - virtual boost::any visit(BinaryRelationExpression const& expression) override; - virtual boost::any visit(VariableExpression const& expression) override; - virtual boost::any visit(UnaryBooleanFunctionExpression const& expression) override; - virtual boost::any visit(UnaryNumericalFunctionExpression const& expression) override; - virtual boost::any visit(BooleanLiteralExpression const& expression) override; - virtual boost::any visit(IntegerLiteralExpression const& expression) override; - virtual boost::any visit(DoubleLiteralExpression const& expression) override; + virtual boost::any visit(IfThenElseExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BinaryBooleanFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BinaryNumericalFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BinaryRelationExpression const& expression, boost::any const& data) override; + virtual boost::any visit(VariableExpression const& expression, boost::any const& data) override; + virtual boost::any visit(UnaryBooleanFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(UnaryNumericalFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BooleanLiteralExpression const& expression, boost::any const& data) override; + virtual boost::any visit(IntegerLiteralExpression const& expression, boost::any const& data) override; + virtual boost::any visit(RationalLiteralExpression const& expression, boost::any const& data) override; }; } } diff --git a/src/storage/expressions/UnaryBooleanFunctionExpression.cpp b/src/storage/expressions/UnaryBooleanFunctionExpression.cpp index 7c45e081d..825d3debc 100644 --- a/src/storage/expressions/UnaryBooleanFunctionExpression.cpp +++ b/src/storage/expressions/UnaryBooleanFunctionExpression.cpp @@ -49,8 +49,8 @@ namespace storm { } } - boost::any UnaryBooleanFunctionExpression::accept(ExpressionVisitor& visitor) const { - return visitor.visit(*this); + boost::any UnaryBooleanFunctionExpression::accept(ExpressionVisitor& visitor, boost::any const& data) const { + return visitor.visit(*this, data); } void UnaryBooleanFunctionExpression::printToStream(std::ostream& stream) const { diff --git a/src/storage/expressions/UnaryBooleanFunctionExpression.h b/src/storage/expressions/UnaryBooleanFunctionExpression.h index d13761c26..1f1974fa1 100644 --- a/src/storage/expressions/UnaryBooleanFunctionExpression.h +++ b/src/storage/expressions/UnaryBooleanFunctionExpression.h @@ -36,7 +36,7 @@ namespace storm { virtual storm::expressions::OperatorType getOperator() const override; virtual bool evaluateAsBool(Valuation const* valuation = nullptr) const override; virtual std::shared_ptr<BaseExpression const> simplify() const override; - virtual boost::any accept(ExpressionVisitor& visitor) const override; + virtual boost::any accept(ExpressionVisitor& visitor, boost::any const& data) const override; /*! * Retrieves the operator associated with this expression. diff --git a/src/storage/expressions/UnaryNumericalFunctionExpression.cpp b/src/storage/expressions/UnaryNumericalFunctionExpression.cpp index fa7cad7e3..988b17a12 100644 --- a/src/storage/expressions/UnaryNumericalFunctionExpression.cpp +++ b/src/storage/expressions/UnaryNumericalFunctionExpression.cpp @@ -4,7 +4,7 @@ #include "src/storage/expressions/UnaryNumericalFunctionExpression.h" #include "src/storage/expressions/IntegerLiteralExpression.h" -#include "src/storage/expressions/DoubleLiteralExpression.h" +#include "src/storage/expressions/RationalLiteralExpression.h" #include "ExpressionVisitor.h" #include "src/utility/macros.h" #include "src/exceptions/InvalidTypeException.h" @@ -87,7 +87,7 @@ namespace storm { case OperatorType::Floor: value = std::floor(boost::get<double>(operandEvaluation)); break; case OperatorType::Ceil: value = std::ceil(boost::get<double>(operandEvaluation)); break; } - return std::shared_ptr<BaseExpression>(new DoubleLiteralExpression(this->getManager(), value)); + return std::shared_ptr<BaseExpression>(new RationalLiteralExpression(this->getManager(), value)); } } @@ -98,8 +98,8 @@ namespace storm { } } - boost::any UnaryNumericalFunctionExpression::accept(ExpressionVisitor& visitor) const { - return visitor.visit(*this); + boost::any UnaryNumericalFunctionExpression::accept(ExpressionVisitor& visitor, boost::any const& data) const { + return visitor.visit(*this, data); } void UnaryNumericalFunctionExpression::printToStream(std::ostream& stream) const { diff --git a/src/storage/expressions/UnaryNumericalFunctionExpression.h b/src/storage/expressions/UnaryNumericalFunctionExpression.h index f9e2ab148..38d29d142 100644 --- a/src/storage/expressions/UnaryNumericalFunctionExpression.h +++ b/src/storage/expressions/UnaryNumericalFunctionExpression.h @@ -37,7 +37,7 @@ namespace storm { virtual int_fast64_t evaluateAsInt(Valuation const* valuation = nullptr) const override; virtual double evaluateAsDouble(Valuation const* valuation = nullptr) const override; virtual std::shared_ptr<BaseExpression const> simplify() const override; - virtual boost::any accept(ExpressionVisitor& visitor) const override; + virtual boost::any accept(ExpressionVisitor& visitor, boost::any const& data) const override; /*! * Retrieves the operator associated with this expression. diff --git a/src/storage/expressions/VariableExpression.cpp b/src/storage/expressions/VariableExpression.cpp index 8ac6cb8b2..a3fe0ae38 100644 --- a/src/storage/expressions/VariableExpression.cpp +++ b/src/storage/expressions/VariableExpression.cpp @@ -63,8 +63,8 @@ namespace storm { return this->shared_from_this(); } - boost::any VariableExpression::accept(ExpressionVisitor& visitor) const { - return visitor.visit(*this); + boost::any VariableExpression::accept(ExpressionVisitor& visitor, boost::any const& data) const { + return visitor.visit(*this, data); } void VariableExpression::printToStream(std::ostream& stream) const { diff --git a/src/storage/expressions/VariableExpression.h b/src/storage/expressions/VariableExpression.h index cfc65fd31..f1f5bca16 100644 --- a/src/storage/expressions/VariableExpression.h +++ b/src/storage/expressions/VariableExpression.h @@ -35,7 +35,7 @@ namespace storm { virtual bool isVariable() const override; virtual void gatherVariables(std::set<storm::expressions::Variable>& variables) const override; virtual std::shared_ptr<BaseExpression const> simplify() const override; - virtual boost::any accept(ExpressionVisitor& visitor) const override; + virtual boost::any accept(ExpressionVisitor& visitor, boost::any const& data) const override; /*! * Retrieves the name of the variable associated with this expression. diff --git a/src/storage/jani/Assignment.cpp b/src/storage/jani/Assignment.cpp index 469446729..ddf65afe4 100644 --- a/src/storage/jani/Assignment.cpp +++ b/src/storage/jani/Assignment.cpp @@ -1,10 +1,18 @@ #include "src/storage/jani/Assignment.h" +#include "src/utility/macros.h" +#include "src/exceptions/NotImplementedException.h" + namespace storm { namespace jani { - Assignment::Assignment(storm::jani::Variable const& variable, storm::expressions::Expression const& expression) : variable(variable), expression(expression) { - // Intentionally left empty. + Assignment::Assignment(storm::jani::Variable const& variable, storm::expressions::Expression const& expression, uint64_t level) : variable(variable), expression(expression), level(level) { + STORM_LOG_THROW(level == 0, storm::exceptions::NotImplementedException, "Assignment levels other than 0 are currently not supported."); + } + + bool Assignment::operator==(Assignment const& other) const { + // FIXME: the level is currently ignored as we do not support it + return this->isTransient() == other.isTransient() && this->getExpressionVariable() == other.getExpressionVariable() && this->getAssignedExpression().isSyntacticallyEqual(other.getAssignedExpression()); } storm::jani::Variable const& Assignment::getVariable() const { @@ -23,8 +31,16 @@ namespace storm { this->expression = expression; } - bool Assignment::isTransientAssignment() const { - return this->variable.get().isTransientVariable(); + bool Assignment::isTransient() const { + return this->variable.get().isTransient(); + } + + void Assignment::substitute(std::map<storm::expressions::Variable, storm::expressions::Expression> const& substitution) { + this->setAssignedExpression(this->getAssignedExpression().substitute(substitution)); + } + + uint64_t Assignment::getLevel() const { + return level; } std::ostream& operator<<(std::ostream& stream, Assignment const& assignment) { @@ -32,5 +48,20 @@ namespace storm { return stream; } + bool AssignmentPartialOrderByVariable::operator()(Assignment const& left, Assignment const& right) const { + return left.getExpressionVariable() < right.getExpressionVariable(); + } + + bool AssignmentPartialOrderByVariable::operator()(Assignment const& left, std::shared_ptr<Assignment> const& right) const { + return left.getExpressionVariable() < right->getExpressionVariable(); + } + + bool AssignmentPartialOrderByVariable::operator()(std::shared_ptr<Assignment> const& left, std::shared_ptr<Assignment> const& right) const { + return left->getExpressionVariable() < right->getExpressionVariable(); + } + + bool AssignmentPartialOrderByVariable::operator()(std::shared_ptr<Assignment> const& left, Assignment const& right) const { + return left->getExpressionVariable() < right.getExpressionVariable(); + } } } \ No newline at end of file diff --git a/src/storage/jani/Assignment.h b/src/storage/jani/Assignment.h index e4b562863..030c1f68f 100644 --- a/src/storage/jani/Assignment.h +++ b/src/storage/jani/Assignment.h @@ -13,7 +13,9 @@ namespace storm { /*! * Creates an assignment of the given expression to the given variable. */ - Assignment(storm::jani::Variable const& variable, storm::expressions::Expression const& expression); + Assignment(storm::jani::Variable const& variable, storm::expressions::Expression const& expression, uint64_t index = 0); + + bool operator==(Assignment const& other) const; /*! * Retrieves the expression variable that is written in this assignment. @@ -35,10 +37,20 @@ namespace storm { */ void setAssignedExpression(storm::expressions::Expression const& expression); + /*! + * Substitutes all variables in all expressions according to the given substitution. + */ + void substitute(std::map<storm::expressions::Variable, storm::expressions::Expression> const& substitution); + /** * Retrieves whether the assignment assigns to a transient variable. */ - bool isTransientAssignment() const; + bool isTransient() const; + + /*! + * Retrieves the level of the assignment. + */ + uint64_t getLevel() const; friend std::ostream& operator<<(std::ostream& stream, Assignment const& assignment); @@ -48,7 +60,20 @@ namespace storm { // The expression that is being assigned to the variable. storm::expressions::Expression expression; + + // The level of the assignment. + uint64_t level; }; + /*! + * This functor enables ordering the assignments by variable. Note that this is a partial order. + */ + struct AssignmentPartialOrderByVariable { + bool operator()(Assignment const& left, Assignment const& right) const; + bool operator()(Assignment const& left, std::shared_ptr<Assignment> const& right) const; + bool operator()(std::shared_ptr<Assignment> const& left, std::shared_ptr<Assignment> const& right) const; + bool operator()(std::shared_ptr<Assignment> const& left, Assignment const& right) const; + }; + } } \ No newline at end of file diff --git a/src/storage/jani/Automaton.cpp b/src/storage/jani/Automaton.cpp index c6d8600e3..9c618a838 100644 --- a/src/storage/jani/Automaton.cpp +++ b/src/storage/jani/Automaton.cpp @@ -61,26 +61,32 @@ namespace storm { Variable const& Automaton::addVariable(Variable const &variable) { if (variable.isBooleanVariable()) { - return addBooleanVariable(variable.asBooleanVariable()); + return addVariable(variable.asBooleanVariable()); } else if (variable.isBoundedIntegerVariable()) { - return addBoundedIntegerVariable(variable.asBoundedIntegerVariable()); + return addVariable(variable.asBoundedIntegerVariable()); } else if (variable.isUnboundedIntegerVariable()) { - return addUnboundedIntegerVariable(variable.asUnboundedIntegerVariable()); + return addVariable(variable.asUnboundedIntegerVariable()); + } else if (variable.isRealVariable()) { + return addVariable(variable.asRealVariable()); } else { STORM_LOG_THROW(false, storm::exceptions::InvalidTypeException, "Variable has invalid type."); } } - BooleanVariable const& Automaton::addBooleanVariable(BooleanVariable const& variable) { - return variables.addBooleanVariable(variable); + BooleanVariable const& Automaton::addVariable(BooleanVariable const& variable) { + return variables.addVariable(variable); } - BoundedIntegerVariable const& Automaton::addBoundedIntegerVariable(BoundedIntegerVariable const& variable) { - return variables.addBoundedIntegerVariable(variable); + BoundedIntegerVariable const& Automaton::addVariable(BoundedIntegerVariable const& variable) { + return variables.addVariable(variable); } - UnboundedIntegerVariable const& Automaton::addUnboundedIntegerVariable(UnboundedIntegerVariable const& variable) { - return variables.addUnboundedIntegerVariable(variable); + UnboundedIntegerVariable const& Automaton::addVariable(UnboundedIntegerVariable const& variable) { + return variables.addVariable(variable); + } + + RealVariable const& Automaton::addVariable(RealVariable const& variable) { + return variables.addVariable(variable); } VariableSet& Automaton::getVariables() { @@ -91,6 +97,10 @@ namespace storm { return variables; } + bool Automaton::hasTransientVariable() const { + return variables.hasTransientVariable(); + } + bool Automaton::hasLocation(std::string const& name) const { return locationToIndex.find(name) != locationToIndex.end(); } @@ -98,11 +108,19 @@ namespace storm { std::vector<Location> const& Automaton::getLocations() const { return locations; } - + + std::vector<Location>& Automaton::getLocations() { + return locations; + } + Location const& Automaton::getLocation(uint64_t index) const { return locations[index]; } - + + Location& Automaton::getLocation(uint64_t index) { + return locations[index]; + } + uint64_t Automaton::addLocation(Location const& location) { STORM_LOG_THROW(!this->hasLocation(location.getName()), storm::exceptions::WrongFormatException, "Cannot add location with name '" << location.getName() << "', because a location with this name already exists."); locationToIndex.emplace(location.getName(), locations.size()); @@ -352,6 +370,22 @@ namespace storm { return result; } + void Automaton::substitute(std::map<storm::expressions::Variable, storm::expressions::Expression> const& substitution) { + for (auto& variable : this->getVariables().getBoundedIntegerVariables()) { + variable.substitute(substitution); + } + + for (auto& location : this->getLocations()) { + location.substitute(substitution); + } + + this->setInitialStatesRestriction(this->getInitialStatesRestriction().substitute(substitution)); + + for (auto& edge : this->getEdges()) { + edge.substitute(substitution); + } + } + void Automaton::finalize(Model const& containingModel) { for (auto& edge : edges) { edge.finalize(containingModel); diff --git a/src/storage/jani/Automaton.h b/src/storage/jani/Automaton.h index 1a2ad61dc..c6219aa1e 100644 --- a/src/storage/jani/Automaton.h +++ b/src/storage/jani/Automaton.h @@ -108,17 +108,22 @@ namespace storm { /*! * Adds the given Boolean variable to this automaton. */ - BooleanVariable const& addBooleanVariable(BooleanVariable const& variable); + BooleanVariable const& addVariable(BooleanVariable const& variable); /*! * Adds the given bounded integer variable to this automaton. */ - BoundedIntegerVariable const& addBoundedIntegerVariable(BoundedIntegerVariable const& variable); + BoundedIntegerVariable const& addVariable(BoundedIntegerVariable const& variable); /*! * Adds the given unbounded integer variable to this automaton. */ - UnboundedIntegerVariable const& addUnboundedIntegerVariable(UnboundedIntegerVariable const& variable); + UnboundedIntegerVariable const& addVariable(UnboundedIntegerVariable const& variable); + + /*! + * Adds the given real variable to this automaton. + */ + RealVariable const& addVariable(RealVariable const& variable); /*! * Retrieves the variables of this automaton. @@ -130,6 +135,11 @@ namespace storm { */ VariableSet const& getVariables() const; + /*! + * Retrieves whether this automaton has a transient variable. + */ + bool hasTransientVariable() const; + /*! * Retrieves whether the automaton has a location with the given name. */ @@ -147,12 +157,22 @@ namespace storm { * Retrieves the locations of the automaton. */ std::vector<Location> const& getLocations() const; - + + /*! + * Retrieves the locations of the automaton. + */ + std::vector<Location>& getLocations(); + /*! * Retrieves the location with the given index. */ Location const& getLocation(uint64_t index) const; + /*! + * Retrieves the location with the given index. + */ + Location& getLocation(uint64_t index); + /*! * Adds the given location to the automaton. */ @@ -263,6 +283,11 @@ namespace storm { */ std::vector<storm::expressions::Expression> getAllRangeExpressions() const; + /*! + * Substitutes all variables in all expressions according to the given substitution. + */ + void substitute(std::map<storm::expressions::Variable, storm::expressions::Expression> const& substitution); + /*! * Finalizes the building of this automaton. Subsequent changes to the automaton require another call to this * method. Note that this method is invoked by a call to <code>finalize</code> to the containing model. diff --git a/src/storage/jani/BooleanVariable.cpp b/src/storage/jani/BooleanVariable.cpp index b7a4c4e19..3eb63780b 100644 --- a/src/storage/jani/BooleanVariable.cpp +++ b/src/storage/jani/BooleanVariable.cpp @@ -11,10 +11,17 @@ namespace storm { // Intentionally left empty. } - bool BooleanVariable::isBooleanVariable() const { return true; } + std::shared_ptr<BooleanVariable> makeBooleanVariable(std::string const& name, storm::expressions::Variable const& variable, boost::optional<storm::expressions::Expression> initValue, bool transient) { + if (initValue) { + return std::make_shared<BooleanVariable>(name, variable, initValue.get(), transient); + } else { + return std::make_shared<BooleanVariable>(name, variable, transient); + } + } + } } \ No newline at end of file diff --git a/src/storage/jani/BooleanVariable.h b/src/storage/jani/BooleanVariable.h index 7a8fe8b21..7fe9603e0 100644 --- a/src/storage/jani/BooleanVariable.h +++ b/src/storage/jani/BooleanVariable.h @@ -21,5 +21,10 @@ namespace storm { virtual bool isBooleanVariable() const override; }; + /** + * Convenience function to call the appropriate constructor and return a shared pointer to the variable. + */ + std::shared_ptr<BooleanVariable> makeBooleanVariable(std::string const& name, storm::expressions::Variable const& variable, boost::optional<storm::expressions::Expression> initValue, bool transient); + } } \ No newline at end of file diff --git a/src/storage/jani/BoundedIntegerVariable.cpp b/src/storage/jani/BoundedIntegerVariable.cpp index f0872a500..37b458ba4 100644 --- a/src/storage/jani/BoundedIntegerVariable.cpp +++ b/src/storage/jani/BoundedIntegerVariable.cpp @@ -21,7 +21,6 @@ namespace storm { // Intentionally left empty. } - storm::expressions::Expression const& BoundedIntegerVariable::getLowerBound() const { return lowerBound; } @@ -46,11 +45,15 @@ namespace storm { return true; } + void BoundedIntegerVariable::substitute(std::map<storm::expressions::Variable, storm::expressions::Expression> const& substitution) { + Variable::substitute(substitution); + this->setLowerBound(this->getLowerBound().substitute(substitution)); + this->setUpperBound(this->getUpperBound().substitute(substitution)); + } + std::shared_ptr<BoundedIntegerVariable> makeBoundedIntegerVariable(std::string const& name, storm::expressions::Variable const& variable, boost::optional<storm::expressions::Expression> initValue, bool transient, boost::optional<storm::expressions::Expression> lowerBound, boost::optional<storm::expressions::Expression> upperBound) { - if(!lowerBound || !upperBound) { - STORM_LOG_THROW(lowerBound && upperBound, storm::exceptions::NotImplementedException, "Jani Bounded Integer variables (for now) have to be bounded from both sides"); - } - if(initValue) { + STORM_LOG_THROW(lowerBound && upperBound, storm::exceptions::NotImplementedException, "Jani Bounded Integer variables (for now) have to be bounded from both sides"); + if (initValue) { return std::make_shared<BoundedIntegerVariable>(name, variable, initValue.get(), transient, lowerBound.get(), upperBound.get()); } else { return std::make_shared<BoundedIntegerVariable>(name, variable, transient, lowerBound.get(), upperBound.get()); diff --git a/src/storage/jani/BoundedIntegerVariable.h b/src/storage/jani/BoundedIntegerVariable.h index c9a42b177..733024b3d 100644 --- a/src/storage/jani/BoundedIntegerVariable.h +++ b/src/storage/jani/BoundedIntegerVariable.h @@ -52,6 +52,11 @@ namespace storm { virtual bool isBoundedIntegerVariable() const override; + /*! + * Substitutes all variables in all expressions according to the given substitution. + */ + virtual void substitute(std::map<storm::expressions::Variable, storm::expressions::Expression> const& substitution) override; + private: // The expression defining the lower bound of the variable. storm::expressions::Expression lowerBound; @@ -61,7 +66,7 @@ namespace storm { }; /** - * Convenience function to call the appropriate constructor and retur a shared pointer to the variable. + * Convenience function to call the appropriate constructor and return a shared pointer to the variable. */ std::shared_ptr<BoundedIntegerVariable> makeBoundedIntegerVariable(std::string const& name, storm::expressions::Variable const& variable, boost::optional<storm::expressions::Expression> initValue, bool transient, boost::optional<storm::expressions::Expression> lowerBound, boost::optional<storm::expressions::Expression> upperBound); } diff --git a/src/storage/jani/Edge.cpp b/src/storage/jani/Edge.cpp index 347140b1b..0a06f50a8 100644 --- a/src/storage/jani/Edge.cpp +++ b/src/storage/jani/Edge.cpp @@ -2,10 +2,13 @@ #include "src/storage/jani/Model.h" +#include "src/utility/macros.h" +#include "src/exceptions/InvalidArgumentException.h" + namespace storm { namespace jani { - Edge::Edge(uint64_t sourceLocationIndex, uint64_t actionIndex, boost::optional<storm::expressions::Expression> const& rate, storm::expressions::Expression const& guard, std::vector<EdgeDestination> destinations) : sourceLocationIndex(sourceLocationIndex), actionIndex(actionIndex), rate(rate), guard(guard), destinations(destinations) { + Edge::Edge(uint64_t sourceLocationIndex, uint64_t actionIndex, boost::optional<storm::expressions::Expression> const& rate, storm::expressions::Expression const& guard, std::vector<EdgeDestination> destinations) : sourceLocationIndex(sourceLocationIndex), actionIndex(actionIndex), rate(rate), guard(guard), destinations(destinations), assignments(), writtenGlobalVariables() { // Intentionally left empty. } @@ -49,6 +52,23 @@ namespace storm { destinations.push_back(destination); } + OrderedAssignments const& Edge::getAssignments() const { + return assignments; + } + + void Edge::substitute(std::map<storm::expressions::Variable, storm::expressions::Expression> const& substitution) { + this->setGuard(this->getGuard().substitute(substitution)); + if (this->hasRate()) { + this->setRate(this->getRate().substitute(substitution)); + } + for (auto& assignment : this->getAssignments()) { + assignment.substitute(substitution); + } + for (auto& destination : this->getDestinations()) { + destination.substitute(substitution); + } + } + void Edge::finalize(Model const& containingModel) { for (auto const& destination : getDestinations()) { for (auto const& assignment : destination.getAssignments()) { @@ -59,6 +79,36 @@ namespace storm { } } + bool Edge::addTransientAssignment(Assignment const& assignment) { + STORM_LOG_THROW(assignment.isTransient(), storm::exceptions::InvalidArgumentException, "Must not add non-transient assignment to location."); + return assignments.add(assignment); + } + + void Edge::liftTransientDestinationAssignments() { + if (!destinations.empty()) { + auto const& destination = *destinations.begin(); + + for (auto const& assignment : destination.getTransientAssignments()) { + // Check if we can lift the assignment to the edge. + bool canBeLifted = true; + for (auto const& destination : destinations) { + if (!destination.hasAssignment(assignment)) { + canBeLifted = false; + break; + } + } + + // If so, remove the assignment from all destinations. + if (canBeLifted) { + this->addTransientAssignment(assignment); + for (auto& destination : destinations) { + destination.removeAssignment(assignment); + } + } + } + } + } + boost::container::flat_set<storm::expressions::Variable> const& Edge::getWrittenGlobalVariables() const { return writtenGlobalVariables; } diff --git a/src/storage/jani/Edge.h b/src/storage/jani/Edge.h index cb872ebbe..6ce6ef127 100644 --- a/src/storage/jani/Edge.h +++ b/src/storage/jani/Edge.h @@ -4,6 +4,7 @@ #include <boost/container/flat_set.hpp> #include "src/storage/jani/EdgeDestination.h" +#include "src/storage/jani/OrderedAssignments.h" namespace storm { namespace jani { @@ -64,6 +65,11 @@ namespace storm { */ void addDestination(EdgeDestination const& destination); + /*! + * Substitutes all variables in all expressions according to the given substitution. + */ + void substitute(std::map<storm::expressions::Variable, storm::expressions::Expression> const& substitution); + /*! * Finalizes the building of this edge. Subsequent changes to the edge require another call to this * method. Note that this method is invoked by a call to <code>finalize</code> to the containing model. @@ -75,6 +81,25 @@ namespace storm { * that prior to calling this, the edge has to be finalized. */ boost::container::flat_set<storm::expressions::Variable> const& getWrittenGlobalVariables() const; + + /*! + * Adds a transient assignment to this edge. + * + * @param assignment The transient assignment to add. + * @return True if the assignment was added. + */ + bool addTransientAssignment(Assignment const& assignment); + + /*! + * Retrieves the assignments of this edge. + */ + OrderedAssignments const& getAssignments() const; + + /*! + * Finds the transient assignments common to all destinations and lifts them to the edge. Afterwards, these + * assignments are no longer contained in the destination. + */ + void liftTransientDestinationAssignments(); private: /// The index of the source location. @@ -93,6 +118,9 @@ namespace storm { /// The destinations of this edge. std::vector<EdgeDestination> destinations; + /// The assignments made when taking this edge. + OrderedAssignments assignments; + /// A set of global variables that is written by at least one of the edge's destinations. This set is /// initialized by the call to <code>finalize</code>. boost::container::flat_set<storm::expressions::Variable> writtenGlobalVariables; diff --git a/src/storage/jani/EdgeDestination.cpp b/src/storage/jani/EdgeDestination.cpp index c58a157e9..c20a5d520 100644 --- a/src/storage/jani/EdgeDestination.cpp +++ b/src/storage/jani/EdgeDestination.cpp @@ -7,33 +7,11 @@ namespace storm { namespace jani { EdgeDestination::EdgeDestination(uint64_t locationIndex, storm::expressions::Expression const& probability, std::vector<Assignment> const& assignments) : locationIndex(locationIndex), probability(probability), assignments(assignments) { - for (auto const& assignment : assignments) { - if (assignment.isTransientAssignment()) { - transientAssignments.push_back(assignment); - } else { - nonTransientAssignments.push_back(assignment); - } - } - sortAssignments(this->assignments); - sortAssignments(transientAssignments); - sortAssignments(nonTransientAssignments); + // Intentionally left empty. } void EdgeDestination::addAssignment(Assignment const& assignment) { - // We make sure that there are no two assignments to the same variable. - for (auto const& oldAssignment : assignments) { - STORM_LOG_THROW(oldAssignment.getExpressionVariable() != assignment.getExpressionVariable(), storm::exceptions::WrongFormatException, "Cannot add assignment '" << assignment << "', because another assignment '" << assignment << "' writes to the same target variable."); - } - assignments.push_back(assignment); - sortAssignments(assignments); - - if (assignment.isTransientAssignment()) { - transientAssignments.push_back(assignment); - sortAssignments(transientAssignments); - } else { - nonTransientAssignments.push_back(assignment); - sortAssignments(nonTransientAssignments); - } + assignments.add(assignment); } uint64_t EdgeDestination::getLocationIndex() const { @@ -48,38 +26,29 @@ namespace storm { this->probability = probability; } - std::vector<Assignment>& EdgeDestination::getAssignments() { - return assignments; + storm::jani::detail::ConstAssignments EdgeDestination::getAssignments() const { + return assignments.getAllAssignments(); } - - std::vector<Assignment> const& EdgeDestination::getAssignments() const { - return assignments; - } - - std::vector<Assignment>& EdgeDestination::getNonTransientAssignments() { - return nonTransientAssignments; + + storm::jani::detail::ConstAssignments EdgeDestination::getTransientAssignments() const { + return assignments.getTransientAssignments(); } - std::vector<Assignment> const& EdgeDestination::getNonTransientAssignments() const { - return nonTransientAssignments; + storm::jani::detail::ConstAssignments EdgeDestination::getNonTransientAssignments() const { + return assignments.getNonTransientAssignments(); } - std::vector<Assignment>& EdgeDestination::getTransientAssignments() { - return transientAssignments; + void EdgeDestination::substitute(std::map<storm::expressions::Variable, storm::expressions::Expression> const& substitution) { + this->setProbability(this->getProbability().substitute(substitution)); + assignments.substitute(substitution); } - std::vector<Assignment> const& EdgeDestination::getTransientAssignments() const { - return transientAssignments; + bool EdgeDestination::hasAssignment(Assignment const& assignment) const { + return assignments.contains(assignment); } - void EdgeDestination::sortAssignments(std::vector<Assignment>& assignments) { - std::sort(assignments.begin(), assignments.end(), [] (storm::jani::Assignment const& assignment1, storm::jani::Assignment const& assignment2) { - bool smaller = assignment1.getExpressionVariable().getType().isBooleanType() && !assignment2.getExpressionVariable().getType().isBooleanType(); - if (!smaller) { - smaller = assignment1.getExpressionVariable() < assignment2.getExpressionVariable(); - } - return smaller; - }); + bool EdgeDestination::removeAssignment(Assignment const& assignment) { + return assignments.remove(assignment); } } diff --git a/src/storage/jani/EdgeDestination.h b/src/storage/jani/EdgeDestination.h index d22e09ab3..a7e3fb672 100644 --- a/src/storage/jani/EdgeDestination.h +++ b/src/storage/jani/EdgeDestination.h @@ -4,7 +4,7 @@ #include "src/storage/expressions/Expression.h" -#include "src/storage/jani/Assignment.h" +#include "src/storage/jani/OrderedAssignments.h" namespace storm { namespace jani { @@ -39,55 +39,36 @@ namespace storm { /*! * Retrieves the assignments to make when choosing this destination. */ - std::vector<Assignment>& getAssignments(); + storm::jani::detail::ConstAssignments getAssignments() const; /*! - * Retrieves the assignments to make when choosing this destination. - */ - std::vector<Assignment> const& getAssignments() const; - - /*! - * Retrieves the non-transient assignments to make when choosing this destination. + * Retrieves the transient assignments to make when choosing this destination. */ - std::vector<Assignment>& getNonTransientAssignments(); + storm::jani::detail::ConstAssignments getTransientAssignments() const; /*! * Retrieves the non-transient assignments to make when choosing this destination. */ - std::vector<Assignment> const& getNonTransientAssignments() const; + storm::jani::detail::ConstAssignments getNonTransientAssignments() const; /*! - * Retrieves the non-transient assignments to make when choosing this destination. + * Substitutes all variables in all expressions according to the given substitution. */ - std::vector<Assignment>& getTransientAssignments(); + void substitute(std::map<storm::expressions::Variable, storm::expressions::Expression> const& substitution); - /*! - * Retrieves the non-transient assignments to make when choosing this destination. - */ - std::vector<Assignment> const& getTransientAssignments() const; - - private: - /*! - * Sorts the assignments to make all assignments to boolean variables precede all others and order the - * assignments within one variable group by the expression variables. - */ - static void sortAssignments(std::vector<Assignment>& assignments); + // Convenience methods to access the assignments. + bool hasAssignment(Assignment const& assignment) const; + bool removeAssignment(Assignment const& assignment); + private: // The index of the destination location. uint64_t locationIndex; // The probability to go to the destination. storm::expressions::Expression probability; - // The assignments to make when choosing this destination. - std::vector<Assignment> assignments; - - // The assignments to make when choosing this destination. - std::vector<Assignment> nonTransientAssignments; - - // The assignments to make when choosing this destination. - std::vector<Assignment> transientAssignments; - + // The (ordered) assignments to make when choosing this destination. + OrderedAssignments assignments; }; } diff --git a/src/storage/jani/Exporter.cpp b/src/storage/jani/Exporter.cpp deleted file mode 100644 index 51635f10a..000000000 --- a/src/storage/jani/Exporter.cpp +++ /dev/null @@ -1,438 +0,0 @@ -#include "src/storage/jani/Exporter.h" - -#include <iostream> - -namespace storm { - namespace jani { - - void appendIndent(std::stringstream& out, uint64_t indent) { - for (uint64_t i = 0; i < indent; ++i) { - out << "\t"; - } - } - - void appendField(std::stringstream& out, std::string const& name) { - out << "\"" << name << "\": "; - } - - void appendValue(std::stringstream& out, std::string const& value) { - out << "\"" << value << "\""; - } - - void clearLine(std::stringstream& out) { - out << std::endl; - } - - std::string expressionToString(storm::expressions::Expression const& expression) { - std::stringstream s; - s << expression; - return s.str(); - } - - void Exporter::appendVersion(std::stringstream& out, uint64_t janiVersion, uint64_t indent) const { - appendIndent(out, indent); - appendField(out, "jani-version"); - } - - void Exporter::appendModelName(std::stringstream& out, std::string const& name, uint64_t indent) const { - appendIndent(out, indent); - appendField(out, "name"); - appendValue(out, name); - } - - void Exporter::appendModelType(std::stringstream& out, storm::jani::ModelType const& modelType, uint64_t indent) const { - appendIndent(out, indent); - appendField(out, "type"); - } - - void Exporter::appendAction(std::stringstream& out, storm::jani::Action const& action, uint64_t indent) const { - appendIndent(out, indent); - out << "{" << std::endl; - appendIndent(out, indent + 1); - appendField(out, "name"); - appendValue(out, action.getName()); - clearLine(out); - appendIndent(out, indent); - out << "}"; - } - - void Exporter::appendActions(std::stringstream& out, storm::jani::Model const& model, uint64_t indent) const { - appendIndent(out, indent); - appendField(out, "actions"); - out << " [" << std::endl; - - for (uint64_t index = 0; index < model.actions.size(); ++index) { - appendAction(out, model.actions[index], indent + 1); - if (index < model.actions.size() - 1) { - out << ","; - } - clearLine(out); - } - - appendIndent(out, indent); - out << "]"; - } - - void Exporter::appendVariable(std::stringstream& out, storm::jani::BooleanVariable const& variable, uint64_t indent) const { - appendIndent(out, indent); - out << "{"; - clearLine(out); - appendIndent(out, indent + 1); - appendField(out, "name"); - appendValue(out, variable.getName()); - out << ","; - clearLine(out); - appendIndent(out, indent + 1); - appendField(out, "type"); - appendValue(out, "bool"); - out << ","; - clearLine(out); - appendIndent(out, indent); - out << "}"; - } - - void Exporter::appendBoundedIntegerVariableType(std::stringstream& out, storm::jani::BoundedIntegerVariable const& variable, uint64_t indent) const { - out << " {"; - clearLine(out); - - appendIndent(out, indent); - appendField(out, "kind"); - appendValue(out, "bounded"); - clearLine(out); - - appendIndent(out, indent); - appendField(out, "base"); - appendValue(out, "int"); - clearLine(out); - - appendIndent(out, indent); - appendField(out, "lower-bound"); - appendValue(out, expressionToString(variable.getLowerBound())); - clearLine(out); - - appendIndent(out, indent); - appendField(out, "upper-bound"); - appendValue(out, expressionToString(variable.getLowerBound())); - clearLine(out); - - appendIndent(out, indent - 1); - out << "}"; - } - - void Exporter::appendVariable(std::stringstream& out, storm::jani::BoundedIntegerVariable const& variable, uint64_t indent) const { - appendIndent(out, indent); - out << "{"; - clearLine(out); - appendIndent(out, indent + 1); - appendField(out, "name"); - appendValue(out, variable.getName()); - out << ","; - clearLine(out); - appendIndent(out, indent + 1); - appendField(out, "type"); - appendBoundedIntegerVariableType(out, variable, indent + 2); - out << ","; - clearLine(out); - appendIndent(out, indent); - out << "}"; - } - - void Exporter::appendVariable(std::stringstream& out, storm::jani::UnboundedIntegerVariable const& variable, uint64_t indent) const { - appendIndent(out, indent); - out << "{"; - clearLine(out); - appendIndent(out, indent + 1); - appendField(out, "name"); - appendValue(out, variable.getName()); - out << ","; - clearLine(out); - appendIndent(out, indent + 1); - appendField(out, "type"); - appendValue(out, "int"); - out << ","; - clearLine(out); - appendIndent(out, indent); - out << "}"; - } - - void Exporter::appendVariables(std::stringstream& out, storm::jani::VariableSet const& variables, uint64_t indent) const { - appendIndent(out, indent); - appendField(out, "variables"); - out << " ["; - clearLine(out); - - for (auto const& variable : variables.getBooleanVariables()) { - appendVariable(out, variable, indent + 1); - clearLine(out); - } - for (auto const& variable : variables.getBoundedIntegerVariables()) { - appendVariable(out, variable, indent + 1); - clearLine(out); - } - for (auto const& variable : variables.getUnboundedIntegerVariables()) { - appendVariable(out, variable, indent + 1); - clearLine(out); - } - - appendIndent(out, indent); - out << "]"; - } - - void Exporter::appendLocation(std::stringstream& out, storm::jani::Location const& location, uint64_t indent) const { - appendIndent(out, indent); - out << "{"; - clearLine(out); - - appendIndent(out, indent + 1); - appendField(out, "name"); - appendValue(out, location.getName()); - clearLine(out); - - appendIndent(out, indent); - out << "}"; - } - - void Exporter::appendLocations(std::stringstream& out, storm::jani::Automaton const& automaton, uint64_t indent) const { - appendIndent(out, indent); - appendField(out, "locations"); - out << " ["; - clearLine(out); - - for (auto const& location : automaton.getLocations()) { - appendLocation(out, location, indent + 1); - out << ","; - clearLine(out); - } - - appendIndent(out, indent); - out << "]"; - } - - void Exporter::appendAssignment(std::stringstream& out, storm::jani::Model const& model, storm::jani::Automaton const& automaton, storm::jani::Assignment const& assignment, uint64_t indent) const { - appendIndent(out, indent); - out << "{"; - clearLine(out); - - appendIndent(out, indent + 1); - appendField(out, "ref"); - storm::jani::Variable const& variable = model.getGlobalVariables().hasVariable(assignment.getExpressionVariable()) ? model.getGlobalVariables().getVariable(assignment.getExpressionVariable()) : automaton.getVariables().getVariable(assignment.getExpressionVariable()); - appendValue(out, variable.getName()); - out << ","; - clearLine(out); - - appendIndent(out, indent + 1); - appendField(out, "value"); - appendValue(out, expressionToString(assignment.getAssignedExpression())); - clearLine(out); - - appendIndent(out, indent); - out << "}"; - } - - void Exporter::appendEdgeDestination(std::stringstream& out, storm::jani::Model const& model, storm::jani::Automaton const& automaton, storm::jani::EdgeDestination const& destination, uint64_t indent) const { - appendIndent(out, indent); - out << "{"; - clearLine(out); - - appendIndent(out, indent + 1); - appendField(out, "probability"); - appendValue(out, expressionToString(destination.getProbability())); - out << ","; - clearLine(out); - - appendIndent(out, indent + 1); - appendField(out, "location"); - appendValue(out, automaton.getLocation(destination.getLocationIndex()).getName()); - out << ","; - clearLine(out); - - appendIndent(out, indent + 1); - appendField(out, "assignments"); - out << " ["; - clearLine(out); - - for (uint64_t index = 0; index < destination.getAssignments().size(); ++index) { - appendAssignment(out, model, automaton, destination.getAssignments()[index], indent + 2); - if (index < destination.getAssignments().size() - 1) { - out << ","; - } - clearLine(out); - } - - appendIndent(out, indent + 1); - out << "]"; - clearLine(out); - - appendIndent(out, indent); - out << "}"; - clearLine(out); - } - - void Exporter::appendEdge(std::stringstream& out, storm::jani::Model const& model, storm::jani::Automaton const& automaton, storm::jani::Edge const& edge, uint64_t indent) const { - appendIndent(out, indent); - out << "{"; - clearLine(out); - - appendIndent(out, indent + 1); - appendField(out, "location"); - appendValue(out, automaton.getLocation(edge.getSourceLocationIndex()).getName()); - out << ","; - clearLine(out); - - appendIndent(out, indent + 1); - appendField(out, "action"); - appendValue(out, model.getAction(edge.getActionIndex()).getName()); - out << ","; - clearLine(out); - - appendIndent(out, indent + 1); - appendField(out, "guard"); - appendValue(out, expressionToString(edge.getGuard())); - out << ","; - clearLine(out); - - appendIndent(out, indent + 1); - appendField(out, "destinations"); - out << " ["; - clearLine(out); - - for (auto const& destination : edge.getDestinations()) { - appendEdgeDestination(out, model, automaton, destination, indent + 2); - } - - appendIndent(out, indent + 1); - out << "]"; - clearLine(out); - - appendIndent(out, indent); - out << "}"; - } - - void Exporter::appendEdges(std::stringstream& out, storm::jani::Model const& model, storm::jani::Automaton const& automaton, uint64_t indent) const { - appendIndent(out, indent); - appendField(out, "edges"); - out << " ["; - clearLine(out); - - for (uint64_t location = 0; location < automaton.getNumberOfLocations(); ++location) { - for (auto const& edge : automaton.getEdgesFromLocation(location)) { - appendEdge(out, model, automaton, edge, indent + 1); - out << ","; - clearLine(out); - } - } - - appendIndent(out, indent); - out << "]"; - clearLine(out); - } - - void Exporter::appendAutomaton(std::stringstream& out, storm::jani::Model const& model, storm::jani::Automaton const& automaton, uint64_t indent) const { - appendIndent(out, indent); - out << "{"; - clearLine(out); - - appendIndent(out, indent + 1); - appendField(out, "name"); - appendValue(out, automaton.getName()); - clearLine(out); - appendVariables(out, automaton.getVariables(), indent + 1); - out << ","; - clearLine(out); - - appendLocations(out, automaton, indent + 1); - out << ","; - clearLine(out); - appendIndent(out, indent + 1); - appendField(out, "initial-locations"); - out << " ["; - clearLine(out); - for (auto const& index : automaton.getInitialLocationIndices()) { - appendIndent(out, indent + 2); - appendValue(out, automaton.getLocation(index).getName()); - clearLine(out); - } - appendIndent(out, indent + 1); - out << "]"; - clearLine(out); - if (automaton.hasInitialStatesRestriction()) { - appendIndent(out, indent + 1); - appendField(out, "initial-states"); - clearLine(out); - appendIndent(out, indent + 2); - out << "{"; - clearLine(out); - appendIndent(out, indent + 3); - appendField(out, "exp"); - appendValue(out, expressionToString(automaton.getInitialStatesExpression())); - clearLine(out); - appendIndent(out, indent + 2); - out << "}"; - clearLine(out); - } - - appendEdges(out, model, automaton, indent + 1); - - appendIndent(out, indent); - out << "}"; - } - - void Exporter::appendAutomata(std::stringstream& out, storm::jani::Model const& model, uint64_t indent) const { - appendIndent(out, indent); - appendField(out, "automata"); - out << " ["; - clearLine(out); - - for (uint64_t index = 0; index < model.automata.size(); ++index) { - appendAutomaton(out, model, model.automata[index], indent + 1); - if (index < model.automata.size() - 1) { - out << ","; - } - clearLine(out); - } - - appendIndent(out, indent); - out << "]"; - } - - std::string Exporter::toJaniString(storm::jani::Model const& model) const { - std::stringstream out; - - out << "{" << std::endl; - appendVersion(out, model.getJaniVersion(), 1); - out << ","; - clearLine(out); - appendModelName(out, model.getName(), 1); - out << ","; - clearLine(out); - appendModelType(out, model.getModelType(), 1); - out << ","; - clearLine(out); - appendActions(out, model, 1); - clearLine(out); - appendVariables(out, model.getGlobalVariables(), 1); - clearLine(out); - - appendIndent(out, 1); - appendField(out, "initial-states"); - clearLine(out); - appendIndent(out, 2); - out << "{"; - clearLine(out); - appendIndent(out, 3); - appendField(out, "exp"); - appendValue(out, expressionToString(model.getInitialStatesRestriction())); - clearLine(out); - appendIndent(out, 2); - out << "}"; - clearLine(out); - - appendAutomata(out, model, 1); - clearLine(out); - out << "}" << std::endl; - - return out.str(); - } - - } -} \ No newline at end of file diff --git a/src/storage/jani/Exporter.h b/src/storage/jani/Exporter.h deleted file mode 100644 index 7b0a1990b..000000000 --- a/src/storage/jani/Exporter.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include <sstream> -#include <cstdint> - -#include "src/storage/jani/Model.h" - -namespace storm { - namespace jani { - - class Exporter { - public: - Exporter() = default; - - std::string toJaniString(storm::jani::Model const& model) const; - - private: - void appendVersion(std::stringstream& out, uint64_t janiVersion, uint64_t indent) const; - - void appendModelName(std::stringstream& out, std::string const& name, uint64_t indent) const; - - void appendModelType(std::stringstream& out, storm::jani::ModelType const& modelType, uint64_t indent) const; - - void appendAction(std::stringstream& out, storm::jani::Action const& action, uint64_t indent) const; - - void appendActions(std::stringstream& out, storm::jani::Model const& model, uint64_t indent) const; - - void appendVariables(std::stringstream& out, storm::jani::VariableSet const& variables, uint64_t indent) const; - - void appendVariable(std::stringstream& out, storm::jani::BooleanVariable const& variable, uint64_t indent) const; - void appendVariable(std::stringstream& out, storm::jani::BoundedIntegerVariable const& variable, uint64_t indent) const; - void appendBoundedIntegerVariableType(std::stringstream& out, storm::jani::BoundedIntegerVariable const& variable, uint64_t indent) const; - void appendVariable(std::stringstream& out, storm::jani::UnboundedIntegerVariable const& variable, uint64_t indent) const; - - void appendAutomata(std::stringstream& out, storm::jani::Model const& model, uint64_t indent) const; - void appendAutomaton(std::stringstream& out, storm::jani::Model const& model, storm::jani::Automaton const& automaton, uint64_t indent) const; - - void appendLocation(std::stringstream& out, storm::jani::Location const& location, uint64_t indent) const; - void appendLocations(std::stringstream& out, storm::jani::Automaton const& automaton, uint64_t indent) const; - - void appendAssignment(std::stringstream& out, storm::jani::Model const& model, storm::jani::Automaton const& automaton, storm::jani::Assignment const& assignment, uint64_t indent) const; - void appendEdgeDestination(std::stringstream& out, storm::jani::Model const& model, storm::jani::Automaton const& automaton, storm::jani::EdgeDestination const& destination, uint64_t indent) const; - void appendEdge(std::stringstream& out, storm::jani::Model const& model, storm::jani::Automaton const& automaton, storm::jani::Edge const& edge, uint64_t indent) const; - void appendEdges(std::stringstream& out, storm::jani::Model const& model, storm::jani::Automaton const& automaton, uint64_t indent) const; - }; - - } -} \ No newline at end of file diff --git a/src/storage/jani/Location.cpp b/src/storage/jani/Location.cpp index 2f1f1f6b7..05a22d4e5 100644 --- a/src/storage/jani/Location.cpp +++ b/src/storage/jani/Location.cpp @@ -1,12 +1,13 @@ #include "src/storage/jani/Location.h" -#include "src/storage/jani/Assignment.h" -#include "src/exceptions/InvalidJaniException.h" + #include "src/utility/macros.h" +#include "src/exceptions/InvalidJaniException.h" +#include "src/exceptions/InvalidArgumentException.h" namespace storm { namespace jani { - Location::Location(std::string const& name, std::vector<Assignment> const& transientAssignments) : name(name), transientAssignments(transientAssignments) { + Location::Location(std::string const& name, std::vector<Assignment> const& transientAssignments) : name(name), assignments(transientAssignments) { // Intentionally left empty. } @@ -14,15 +15,24 @@ namespace storm { return name; } - std::vector<Assignment> const& Location::getTransientAssignments() const { - return transientAssignments; + OrderedAssignments const& Location::getAssignments() const { + return assignments; } - void Location::checkValid() const { - for(auto const& assignment : transientAssignments) { - STORM_LOG_THROW(assignment.isTransientAssignment(), storm::exceptions::InvalidJaniException, "Only transient assignments are allowed in locations."); + void Location::addTransientAssignment(storm::jani::Assignment const& assignment) { + STORM_LOG_THROW(assignment.isTransient(), storm::exceptions::InvalidArgumentException, "Must not add non-transient assignment to location."); + assignments.add(assignment); + } + + void Location::substitute(std::map<storm::expressions::Variable, storm::expressions::Expression> const& substitution) { + for (auto& assignment : assignments) { + assignment.substitute(substitution); } } + void Location::checkValid() const { + // Intentionally left empty. + } + } } \ No newline at end of file diff --git a/src/storage/jani/Location.h b/src/storage/jani/Location.h index 461b68e1f..a2328cb22 100644 --- a/src/storage/jani/Location.h +++ b/src/storage/jani/Location.h @@ -1,8 +1,8 @@ #pragma once #include <string> -#include <vector> -#include "src/storage/jani/Assignment.h" + +#include "src/storage/jani/OrderedAssignments.h" namespace storm { namespace jani { @@ -25,9 +25,19 @@ namespace storm { std::string const& getName() const; /*! - * Retrieves the transient assignments of this location. + * Retrieves the assignments of this location. + */ + OrderedAssignments const& getAssignments() const; + + /*! + * Adds the given transient assignment to this location. + */ + void addTransientAssignment(storm::jani::Assignment const& assignment); + + /*! + * Substitutes all variables in all expressions according to the given substitution. */ - std::vector<Assignment> const& getTransientAssignments() const; + void substitute(std::map<storm::expressions::Variable, storm::expressions::Expression> const& substitution); /*! * Checks whether the location is valid, that is, whether the assignments are indeed all transient assignments. @@ -39,7 +49,7 @@ namespace storm { std::string name; /// The transient assignments made in this location. - std::vector<Assignment> transientAssignments; + OrderedAssignments assignments; }; } diff --git a/src/storage/jani/Model.cpp b/src/storage/jani/Model.cpp index a9fb3f25e..fc170ac58 100644 --- a/src/storage/jani/Model.cpp +++ b/src/storage/jani/Model.cpp @@ -34,6 +34,10 @@ namespace storm { silentActionIndex = addAction(storm::jani::Action(SILENT_ACTION_NAME)); } + storm::expressions::ExpressionManager& Model::getManager() const { + return *expressionManager; + } + uint64_t Model::getJaniVersion() const { return version; } @@ -104,29 +108,35 @@ namespace storm { std::vector<Constant>& Model::getConstants() { return constants; } - + Variable const& Model::addVariable(Variable const& variable) { if (variable.isBooleanVariable()) { - return addBooleanVariable(variable.asBooleanVariable()); + return addVariable(variable.asBooleanVariable()); } else if (variable.isBoundedIntegerVariable()) { - return addBoundedIntegerVariable(variable.asBoundedIntegerVariable()); + return addVariable(variable.asBoundedIntegerVariable()); } else if (variable.isUnboundedIntegerVariable()) { - return addUnboundedIntegerVariable(variable.asUnboundedIntegerVariable()); + return addVariable(variable.asUnboundedIntegerVariable()); + } else if (variable.isRealVariable()) { + return addVariable(variable.asRealVariable()); } else { STORM_LOG_THROW(false, storm::exceptions::InvalidTypeException, "Variable has invalid type."); } } - BooleanVariable const& Model::addBooleanVariable(BooleanVariable const& variable) { - return globalVariables.addBooleanVariable(variable); + BooleanVariable const& Model::addVariable(BooleanVariable const& variable) { + return globalVariables.addVariable(variable); } - BoundedIntegerVariable const& Model::addBoundedIntegerVariable(BoundedIntegerVariable const& variable) { - return globalVariables.addBoundedIntegerVariable(variable); + BoundedIntegerVariable const& Model::addVariable(BoundedIntegerVariable const& variable) { + return globalVariables.addVariable(variable); } - UnboundedIntegerVariable const& Model::addUnboundedIntegerVariable(UnboundedIntegerVariable const& variable) { - return globalVariables.addUnboundedIntegerVariable(variable); + UnboundedIntegerVariable const& Model::addVariable(UnboundedIntegerVariable const& variable) { + return globalVariables.addVariable(variable); + } + + RealVariable const& Model::addVariable(RealVariable const& variable) { + return globalVariables.addVariable(variable); } VariableSet& Model::getGlobalVariables() { @@ -137,6 +147,23 @@ namespace storm { return globalVariables; } + bool Model::hasGlobalVariable(std::string const& name) const { + return globalVariables.hasVariable(name); + } + + Variable const& Model::getGlobalVariable(std::string const& name) const { + return globalVariables.getVariable(name); + } + + bool Model::hasNonGlobalTransientVariable() const { + for (auto const& automaton : automata) { + if (automaton.hasTransientVariable()) { + return true; + } + } + return false; + } + storm::expressions::ExpressionManager& Model::getExpressionManager() { return *expressionManager; } @@ -173,6 +200,12 @@ namespace storm { return automata[it->second]; } + uint64_t Model::getAutomatonIndex(std::string const& name) const { + auto it = automatonToIndex.find(name); + STORM_LOG_THROW(it != automatonToIndex.end(), storm::exceptions::InvalidOperationException, "Unable to retrieve unknown automaton '" << name << "'."); + return it->second; + } + std::size_t Model::getNumberOfAutomata() const { return automata.size(); } @@ -304,9 +337,7 @@ namespace storm { // Substitute constants in all global variables. for (auto& variable : result.getGlobalVariables().getBoundedIntegerVariables()) { - variable.setInitExpression(variable.getInitExpression().substitute(constantSubstitution)); - variable.setLowerBound(variable.getLowerBound().substitute(constantSubstitution)); - variable.setUpperBound(variable.getUpperBound().substitute(constantSubstitution)); + variable.substitute(constantSubstitution); } // Substitute constants in initial states expression. @@ -314,26 +345,7 @@ namespace storm { // Substitute constants in variables of automata and their edges. for (auto& automaton : result.getAutomata()) { - for (auto& variable : automaton.getVariables().getBoundedIntegerVariables()) { - variable.setInitExpression(variable.getInitExpression().substitute(constantSubstitution)); - variable.setLowerBound(variable.getLowerBound().substitute(constantSubstitution)); - variable.setUpperBound(variable.getUpperBound().substitute(constantSubstitution)); - } - - automaton.setInitialStatesRestriction(automaton.getInitialStatesExpression().substitute(constantSubstitution)); - - for (auto& edge : automaton.getEdges()) { - edge.setGuard(edge.getGuard().substitute(constantSubstitution)); - if (edge.hasRate()) { - edge.setRate(edge.getRate().substitute(constantSubstitution)); - } - for (auto& destination : edge.getDestinations()) { - destination.setProbability(destination.getProbability().substitute(constantSubstitution)); - for (auto& assignment : destination.getAssignments()) { - assignment.setAssignedExpression(assignment.getAssignedExpression().substitute(constantSubstitution)); - } - } - } + automaton.substitute(constantSubstitution); } return result; diff --git a/src/storage/jani/Model.h b/src/storage/jani/Model.h index 0379cd5dd..2fc06b18f 100644 --- a/src/storage/jani/Model.h +++ b/src/storage/jani/Model.h @@ -30,6 +30,11 @@ namespace storm { * Creates an empty model with the given type. */ Model(std::string const& name, ModelType const& modelType, uint64_t version = 1, boost::optional<std::shared_ptr<storm::expressions::ExpressionManager>> const& expressionManager = boost::none); + + /*! + * Retrieves the expression manager responsible for the expressions in the model. + */ + storm::expressions::ExpressionManager& getManager() const; /*! * Retrieves the JANI-version of the model. @@ -106,7 +111,7 @@ namespace storm { * Retrieves the constant with the given name (if any). */ Constant const& getConstant(std::string const& name) const; - + /*! * Adds the given variable to this model. */ @@ -115,17 +120,22 @@ namespace storm { /*! * Adds the given boolean variable to this model. */ - BooleanVariable const& addBooleanVariable(BooleanVariable const& variable); + BooleanVariable const& addVariable(BooleanVariable const& variable); /*! * Adds the given bounded integer variable to this model. */ - BoundedIntegerVariable const& addBoundedIntegerVariable(BoundedIntegerVariable const& variable); + BoundedIntegerVariable const& addVariable(BoundedIntegerVariable const& variable); /*! * Adds the given unbounded integer variable to this model. */ - UnboundedIntegerVariable const& addUnboundedIntegerVariable(UnboundedIntegerVariable const& variable); + UnboundedIntegerVariable const& addVariable(UnboundedIntegerVariable const& variable); + + /*! + * Adds the given real variable to this model. + */ + RealVariable const& addVariable(RealVariable const& variable); /*! * Retrieves the variables of this automaton. @@ -136,6 +146,21 @@ namespace storm { * Retrieves the variables of this automaton. */ VariableSet const& getGlobalVariables() const; + + /*! + * Retrieves whether this model has a global variable with the given name. + */ + bool hasGlobalVariable(std::string const& name) const; + + /*! + * Retrieves the global variable with the given name if one exists. + */ + Variable const& getGlobalVariable(std::string const& name) const; + + /*! + * Retrieves whether this model has a non-global transient variable. + */ + bool hasNonGlobalTransientVariable() const; /*! * Retrieves the manager responsible for the expressions in the JANI model. @@ -171,6 +196,11 @@ namespace storm { * Retrieves the automaton with the given name. */ Automaton const& getAutomaton(std::string const& name) const; + + /*! + * Retrieves the index of the given automaton. + */ + uint64_t getAutomatonIndex(std::string const& name) const; /*! * Retrieves the number of automata in this model. diff --git a/src/storage/jani/OrderedAssignments.cpp b/src/storage/jani/OrderedAssignments.cpp new file mode 100644 index 000000000..2b7ee613f --- /dev/null +++ b/src/storage/jani/OrderedAssignments.cpp @@ -0,0 +1,116 @@ +#include "src/storage/jani/OrderedAssignments.h" + +#include "src/utility/macros.h" +#include "src/exceptions/InvalidArgumentException.h" + +namespace storm { + namespace jani { + + OrderedAssignments::OrderedAssignments(std::vector<Assignment> const& assignments) { + for (auto const& assignment : assignments) { + add(assignment); + } + } + + bool OrderedAssignments::add(Assignment const& assignment) { + // If the element is contained in this set of assignment, nothing needs to be added. + if (this->contains(assignment)) { + return false; + } + + // Otherwise, we find the spot to insert it. + auto it = lowerBound(assignment, allAssignments); + + if (it != allAssignments.end()) { + STORM_LOG_THROW(assignment.getExpressionVariable() != (*it)->getExpressionVariable(), storm::exceptions::InvalidArgumentException, "Cannot add assignment as an assignment to this variable already exists."); + } + + // Finally, insert the new element in the correct vectors. + auto elementToInsert = std::make_shared<Assignment>(assignment); + allAssignments.emplace(it, elementToInsert); + if (assignment.isTransient()) { + auto transientIt = lowerBound(assignment, transientAssignments); + transientAssignments.emplace(transientIt, elementToInsert); + } else { + auto nonTransientIt = lowerBound(assignment, nonTransientAssignments); + nonTransientAssignments.emplace(nonTransientIt, elementToInsert); + } + + return true; + } + + bool OrderedAssignments::remove(Assignment const& assignment) { + // If the element is contained in this set of assignment, nothing needs to be removed. + if (!this->contains(assignment)) { + return false; + } + + // Otherwise, we find the element to delete. + auto it = lowerBound(assignment, allAssignments); + STORM_LOG_ASSERT(it != allAssignments.end(), "Invalid iterator, expected existing element."); + STORM_LOG_ASSERT(assignment == **it, "Wrong iterator position."); + allAssignments.erase(it); + + if (assignment.isTransient()) { + auto transientIt = lowerBound(assignment, transientAssignments); + STORM_LOG_ASSERT(transientIt != transientAssignments.end(), "Invalid iterator, expected existing element."); + STORM_LOG_ASSERT(assignment == **transientIt, "Wrong iterator position."); + transientAssignments.erase(transientIt); + } else { + auto nonTransientIt = lowerBound(assignment, nonTransientAssignments); + STORM_LOG_ASSERT(nonTransientIt != nonTransientAssignments.end(), "Invalid iterator, expected existing element."); + STORM_LOG_ASSERT(assignment == **nonTransientIt, "Wrong iterator position."); + nonTransientAssignments.erase(nonTransientIt); + } + return true; + } + + bool OrderedAssignments::contains(Assignment const& assignment) const { + auto it = lowerBound(assignment, allAssignments); + if (it != allAssignments.end() && assignment == **it) { + return true; + } else { + return false; + } + } + + detail::ConstAssignments OrderedAssignments::getAllAssignments() const { + return detail::ConstAssignments(allAssignments.begin(), allAssignments.end()); + } + + detail::ConstAssignments OrderedAssignments::getTransientAssignments() const { + return detail::ConstAssignments(transientAssignments.begin(), transientAssignments.end()); + } + + detail::ConstAssignments OrderedAssignments::getNonTransientAssignments() const { + return detail::ConstAssignments(nonTransientAssignments.begin(), nonTransientAssignments.end()); + } + + detail::Assignments::iterator OrderedAssignments::begin() { + return detail::Assignments::make_iterator(allAssignments.begin()); + } + + detail::ConstAssignments::iterator OrderedAssignments::begin() const { + return detail::ConstAssignments::make_iterator(allAssignments.begin()); + } + + detail::Assignments::iterator OrderedAssignments::end() { + return detail::Assignments::make_iterator(allAssignments.end()); + } + + detail::ConstAssignments::iterator OrderedAssignments::end() const { + return detail::ConstAssignments::make_iterator(allAssignments.end()); + } + + void OrderedAssignments::substitute(std::map<storm::expressions::Variable, storm::expressions::Expression> const& substitution) { + for (auto& assignment : allAssignments) { + assignment->substitute(substitution); + } + } + + std::vector<std::shared_ptr<Assignment>>::const_iterator OrderedAssignments::lowerBound(Assignment const& assignment, std::vector<std::shared_ptr<Assignment>> const& assignments) { + return std::lower_bound(assignments.begin(), assignments.end(), assignment, storm::jani::AssignmentPartialOrderByVariable()); + } + + } +} \ No newline at end of file diff --git a/src/storage/jani/OrderedAssignments.h b/src/storage/jani/OrderedAssignments.h new file mode 100644 index 000000000..c9e07a34d --- /dev/null +++ b/src/storage/jani/OrderedAssignments.h @@ -0,0 +1,91 @@ +#pragma once + +#include "src/adapters/DereferenceIteratorAdapter.h" + +#include "src/storage/jani/Assignment.h" + +namespace storm { + namespace jani { + + namespace detail { + using Assignments = storm::adapters::DereferenceIteratorAdapter<std::vector<std::shared_ptr<Assignment>>>; + using ConstAssignments = storm::adapters::DereferenceIteratorAdapter<std::vector<std::shared_ptr<Assignment>> const>; + } + + class OrderedAssignments { + public: + /*! + * Creates an ordered set of assignments. + */ + OrderedAssignments(std::vector<Assignment> const& assignments = std::vector<Assignment>()); + + /*! + * Adds the given assignment to the set of assignments. + * + * @return True iff the assignment was added. + */ + bool add(Assignment const& assignment); + + /*! + * Removes the given assignment from this set of assignments. + * + * @return True if the assignment was found and removed. + */ + bool remove(Assignment const& assignment); + + /*! + * Retrieves whether the given assignment is contained in this set of assignments. + */ + bool contains(Assignment const& assignment) const; + + /*! + * Returns all assignments in this set of assignments. + */ + detail::ConstAssignments getAllAssignments() const; + + /*! + * Returns all transient assignments in this set of assignments. + */ + detail::ConstAssignments getTransientAssignments() const; + + /*! + * Returns all non-transient assignments in this set of assignments. + */ + detail::ConstAssignments getNonTransientAssignments() const; + + /*! + * Returns an iterator to the assignments. + */ + detail::Assignments::iterator begin(); + + /*! + * Returns an iterator to the assignments. + */ + detail::ConstAssignments::iterator begin() const; + + /*! + * Returns an iterator past the end of the assignments. + */ + detail::Assignments::iterator end(); + + /*! + * Returns an iterator past the end of the assignments. + */ + detail::ConstAssignments::iterator end() const; + + /*! + * Substitutes all variables in all expressions according to the given substitution. + */ + void substitute(std::map<storm::expressions::Variable, storm::expressions::Expression> const& substitution); + + private: + static std::vector<std::shared_ptr<Assignment>>::const_iterator lowerBound(Assignment const& assignment, std::vector<std::shared_ptr<Assignment>> const& assignments); + + // The vectors to store the assignments. These need to be ordered at all times. + std::vector<std::shared_ptr<Assignment>> allAssignments; + std::vector<std::shared_ptr<Assignment>> transientAssignments; + std::vector<std::shared_ptr<Assignment>> nonTransientAssignments; + }; + + } +} \ No newline at end of file diff --git a/src/storage/jani/RealVariable.cpp b/src/storage/jani/RealVariable.cpp new file mode 100644 index 000000000..8880a9a0a --- /dev/null +++ b/src/storage/jani/RealVariable.cpp @@ -0,0 +1,19 @@ +#include "src/storage/jani/RealVariable.h" + +namespace storm { + namespace jani { + + RealVariable::RealVariable(std::string const& name, storm::expressions::Variable const& variable, bool transient) : storm::jani::Variable(name, variable, transient) { + // Intentionally left empty. + } + + RealVariable::RealVariable(std::string const& name, storm::expressions::Variable const& variable, storm::expressions::Expression const& initValue, bool transient) : storm::jani::Variable(name, variable, initValue, transient) { + // Intentionally left empty. + } + + bool RealVariable::isRealVariable() const { + return true; + } + + } +} \ No newline at end of file diff --git a/src/storage/jani/RealVariable.h b/src/storage/jani/RealVariable.h new file mode 100644 index 000000000..74c337ed9 --- /dev/null +++ b/src/storage/jani/RealVariable.h @@ -0,0 +1,25 @@ +#pragma once + +#include "src/storage/jani/Variable.h" + +namespace storm { + namespace jani { + + class RealVariable : public Variable { + public: + /*! + * Creates a real variable without initial value. + */ + RealVariable(std::string const& name, storm::expressions::Variable const& variable, bool transient=false); + + /*! + * Creates a real variable with initial value. + */ + RealVariable(std::string const& name, storm::expressions::Variable const& variable, storm::expressions::Expression const& initValue, bool transient=false); + + virtual bool isRealVariable() const override; + }; + + + } +} \ No newline at end of file diff --git a/src/storage/jani/UnboundedIntegerVariable.h b/src/storage/jani/UnboundedIntegerVariable.h index d3d3daa01..17f81d334 100644 --- a/src/storage/jani/UnboundedIntegerVariable.h +++ b/src/storage/jani/UnboundedIntegerVariable.h @@ -15,7 +15,6 @@ namespace storm { * Creates an unbounded integer variable with initial value. */ UnboundedIntegerVariable(std::string const& name, storm::expressions::Variable const& variable, storm::expressions::Expression const&, bool transient=false); - virtual bool isUnboundedIntegerVariable() const override; }; diff --git a/src/storage/jani/Variable.cpp b/src/storage/jani/Variable.cpp index 57a9289a4..9280b7c8e 100644 --- a/src/storage/jani/Variable.cpp +++ b/src/storage/jani/Variable.cpp @@ -3,6 +3,7 @@ #include "src/storage/jani/BooleanVariable.h" #include "src/storage/jani/BoundedIntegerVariable.h" #include "src/storage/jani/UnboundedIntegerVariable.h" +#include "src/storage/jani/RealVariable.h" namespace storm { namespace jani { @@ -15,7 +16,6 @@ namespace storm { // Intentionally left empty. } - storm::expressions::Variable const& Variable::getExpressionVariable() const { return variable; } @@ -36,7 +36,11 @@ namespace storm { return false; } - bool Variable::isTransientVariable() const { + bool Variable::isRealVariable() const { + return false; + } + + bool Variable::isTransient() const { return transient; } @@ -53,27 +57,41 @@ namespace storm { } BooleanVariable& Variable::asBooleanVariable() { - return dynamic_cast<BooleanVariable&>(*this); + return static_cast<BooleanVariable&>(*this); } BooleanVariable const& Variable::asBooleanVariable() const { - return dynamic_cast<BooleanVariable const&>(*this); + return static_cast<BooleanVariable const&>(*this); } BoundedIntegerVariable& Variable::asBoundedIntegerVariable() { - return dynamic_cast<BoundedIntegerVariable&>(*this); + return static_cast<BoundedIntegerVariable&>(*this); } BoundedIntegerVariable const& Variable::asBoundedIntegerVariable() const { - return dynamic_cast<BoundedIntegerVariable const&>(*this); + return static_cast<BoundedIntegerVariable const&>(*this); } UnboundedIntegerVariable& Variable::asUnboundedIntegerVariable() { - return dynamic_cast<UnboundedIntegerVariable&>(*this); + return static_cast<UnboundedIntegerVariable&>(*this); } UnboundedIntegerVariable const& Variable::asUnboundedIntegerVariable() const { - return dynamic_cast<UnboundedIntegerVariable const&>(*this); + return static_cast<UnboundedIntegerVariable const&>(*this); + } + + RealVariable& Variable::asRealVariable() { + return static_cast<RealVariable&>(*this); + } + + RealVariable const& Variable::asRealVariable() const { + return static_cast<RealVariable const&>(*this); + } + + void Variable::substitute(std::map<storm::expressions::Variable, storm::expressions::Expression> const& substitution) { + if (this->hasInitExpression()) { + this->setInitExpression(this->getInitExpression().substitute(substitution)); + } } } diff --git a/src/storage/jani/Variable.h b/src/storage/jani/Variable.h index 820014b03..1add2768b 100644 --- a/src/storage/jani/Variable.h +++ b/src/storage/jani/Variable.h @@ -13,6 +13,7 @@ namespace storm { class BooleanVariable; class BoundedIntegerVariable; class UnboundedIntegerVariable; + class RealVariable; class Variable { public: @@ -58,8 +59,9 @@ namespace storm { virtual bool isBooleanVariable() const; virtual bool isBoundedIntegerVariable() const; virtual bool isUnboundedIntegerVariable() const; + virtual bool isRealVariable() const; - virtual bool isTransientVariable() const; + virtual bool isTransient() const; // Methods to get the variable as a different type. BooleanVariable& asBooleanVariable(); @@ -68,6 +70,13 @@ namespace storm { BoundedIntegerVariable const& asBoundedIntegerVariable() const; UnboundedIntegerVariable& asUnboundedIntegerVariable(); UnboundedIntegerVariable const& asUnboundedIntegerVariable() const; + RealVariable& asRealVariable(); + RealVariable const& asRealVariable() const; + + /*! + * Substitutes all variables in all expressions according to the given substitution. + */ + virtual void substitute(std::map<storm::expressions::Variable, storm::expressions::Expression> const& substitution); private: // The name of the variable. diff --git a/src/storage/jani/VariableSet.cpp b/src/storage/jani/VariableSet.cpp index fe7ef300c..4caae4269 100644 --- a/src/storage/jani/VariableSet.cpp +++ b/src/storage/jani/VariableSet.cpp @@ -6,46 +6,7 @@ namespace storm { namespace jani { - - namespace detail { - - template<typename VariableType> - VariableType& Dereferencer<VariableType>::operator()(std::shared_ptr<VariableType> const& d) const { - return *d; - } - - template<typename VariableType> - Variables<VariableType>::Variables(input_iterator it, input_iterator ite) : it(it), ite(ite) { - // Intentionally left empty. - } - template<typename VariableType> - typename Variables<VariableType>::iterator Variables<VariableType>::begin() { - return boost::make_transform_iterator(it, Dereferencer<VariableType>()); - } - - template<typename VariableType> - typename Variables<VariableType>::iterator Variables<VariableType>::end() { - return boost::make_transform_iterator(ite, Dereferencer<VariableType>()); - } - - template<typename VariableType> - ConstVariables<VariableType>::ConstVariables(const_input_iterator it, const_input_iterator ite) : it(it), ite(ite) { - // Intentionally left empty. - } - - template<typename VariableType> - typename ConstVariables<VariableType>::const_iterator ConstVariables<VariableType>::begin() { - return boost::make_transform_iterator(it, Dereferencer<VariableType const>()); - } - - template<typename VariableType> - typename ConstVariables<VariableType>::const_iterator ConstVariables<VariableType>::end() { - return boost::make_transform_iterator(ite, Dereferencer<VariableType const>()); - } - - } - VariableSet::VariableSet() { // Intentionally left empty. } @@ -74,7 +35,15 @@ namespace storm { return detail::ConstVariables<UnboundedIntegerVariable>(unboundedIntegerVariables.begin(), unboundedIntegerVariables.end()); } - BooleanVariable const& VariableSet::addBooleanVariable(BooleanVariable const& variable) { + detail::Variables<RealVariable> VariableSet::getRealVariables() { + return detail::Variables<RealVariable>(realVariables.begin(), realVariables.end()); + } + + detail::ConstVariables<RealVariable> VariableSet::getRealVariables() const { + return detail::ConstVariables<RealVariable>(realVariables.begin(), realVariables.end()); + } + + BooleanVariable const& VariableSet::addVariable(BooleanVariable const& variable) { STORM_LOG_THROW(!this->hasVariable(variable.getName()), storm::exceptions::WrongFormatException, "Cannot add variable with name '" << variable.getName() << "', because a variable with that name already exists."); std::shared_ptr<BooleanVariable> newVariable = std::make_shared<BooleanVariable>(variable); variables.push_back(newVariable); @@ -84,7 +53,7 @@ namespace storm { return *newVariable; } - BoundedIntegerVariable const& VariableSet::addBoundedIntegerVariable(BoundedIntegerVariable const& variable) { + BoundedIntegerVariable const& VariableSet::addVariable(BoundedIntegerVariable const& variable) { STORM_LOG_THROW(!this->hasVariable(variable.getName()), storm::exceptions::WrongFormatException, "Cannot add variable with name '" << variable.getName() << "', because a variable with that name already exists."); std::shared_ptr<BoundedIntegerVariable> newVariable = std::make_shared<BoundedIntegerVariable>(variable); variables.push_back(newVariable); @@ -94,7 +63,7 @@ namespace storm { return *newVariable; } - UnboundedIntegerVariable const& VariableSet::addUnboundedIntegerVariable(UnboundedIntegerVariable const& variable) { + UnboundedIntegerVariable const& VariableSet::addVariable(UnboundedIntegerVariable const& variable) { STORM_LOG_THROW(!this->hasVariable(variable.getName()), storm::exceptions::WrongFormatException, "Cannot add variable with name '" << variable.getName() << "', because a variable with that name already exists."); std::shared_ptr<UnboundedIntegerVariable> newVariable = std::make_shared<UnboundedIntegerVariable>(variable); variables.push_back(newVariable); @@ -104,6 +73,16 @@ namespace storm { return *newVariable; } + RealVariable const& VariableSet::addVariable(RealVariable const& variable) { + STORM_LOG_THROW(!this->hasVariable(variable.getName()), storm::exceptions::WrongFormatException, "Cannot add variable with name '" << variable.getName() << "', because a variable with that name already exists."); + std::shared_ptr<RealVariable> newVariable = std::make_shared<RealVariable>(variable); + variables.push_back(newVariable); + realVariables.push_back(newVariable); + nameToVariable.emplace(variable.getName(), variable.getExpressionVariable()); + variableToVariable.emplace(variable.getExpressionVariable(), newVariable); + return *newVariable; + } + bool VariableSet::hasVariable(std::string const& name) const { return nameToVariable.find(name) != nameToVariable.end(); } @@ -114,20 +93,20 @@ namespace storm { return getVariable(it->second); } - VariableSet::iterator VariableSet::begin() { - return boost::make_transform_iterator(variables.begin(), detail::Dereferencer<Variable>()); + typename detail::Variables<Variable>::iterator VariableSet::begin() { + return detail::Variables<Variable>::make_iterator(variables.begin()); } - VariableSet::const_iterator VariableSet::begin() const { - return boost::make_transform_iterator(variables.begin(), detail::Dereferencer<Variable const>()); + typename detail::ConstVariables<Variable>::iterator VariableSet::begin() const { + return detail::ConstVariables<Variable>::make_iterator(variables.begin()); } - VariableSet::iterator VariableSet::end() { - return boost::make_transform_iterator(variables.end(), detail::Dereferencer<Variable>()); + typename detail::Variables<Variable>::iterator VariableSet::end() { + return detail::Variables<Variable>::make_iterator(variables.end()); } - VariableSet::const_iterator VariableSet::end() const { - return boost::make_transform_iterator(variables.end(), detail::Dereferencer<Variable const>()); + detail::ConstVariables<Variable>::iterator VariableSet::end() const { + return detail::ConstVariables<Variable>::make_iterator(variables.end()); } Variable const& VariableSet::getVariable(storm::expressions::Variable const& variable) const { @@ -140,6 +119,15 @@ namespace storm { return variableToVariable.find(variable) != variableToVariable.end(); } + bool VariableSet::hasTransientVariable() const { + for (auto const& variable : variables) { + if (variable->isTransient()) { + return true; + } + } + return false; + } + bool VariableSet::containsBooleanVariable() const { return !booleanVariables.empty(); } @@ -152,24 +140,43 @@ namespace storm { return !unboundedIntegerVariables.empty(); } + bool VariableSet::containsRealVariables() const { + return !realVariables.empty(); + } + + bool VariableSet::containsNonTransientRealVariables() const { + for (auto const& variable : realVariables) { + if (!variable->isTransient()) { + std::cout << "var " << variable->getName() << "is non-transient " << std::endl; + return true; + } + } + return false; + } + bool VariableSet::empty() const { return !(containsBooleanVariable() || containsBoundedIntegerVariable() || containsUnboundedIntegerVariables()); } - template class detail::Dereferencer<Variable>; - template class detail::Dereferencer<BooleanVariable>; - template class detail::Dereferencer<BoundedIntegerVariable>; - template class detail::Dereferencer<UnboundedIntegerVariable>; - template class detail::Dereferencer<Variable const>; - template class detail::Dereferencer<BooleanVariable const>; - template class detail::Dereferencer<BoundedIntegerVariable const>; - template class detail::Dereferencer<UnboundedIntegerVariable const>; - template class detail::Variables<BooleanVariable>; - template class detail::Variables<BoundedIntegerVariable>; - template class detail::Variables<UnboundedIntegerVariable>; - template class detail::ConstVariables<BooleanVariable>; - template class detail::ConstVariables<BoundedIntegerVariable>; - template class detail::ConstVariables<UnboundedIntegerVariable>; - + uint_fast64_t VariableSet::getNumberOfTransientVariables() const { + uint_fast64_t result = 0; + for (auto const& variable : variables) { + if (variable->isTransient()) { + ++result; + } + } + return result; + } + + std::vector<std::shared_ptr<Variable const>> VariableSet::getTransientVariables() const { + std::vector<std::shared_ptr<Variable const>> result; + for (auto const& variable : variables) { + if (variable->isTransient()) { + result.push_back(variable); + } + } + return result; + } + } } diff --git a/src/storage/jani/VariableSet.h b/src/storage/jani/VariableSet.h index 84e265ed1..78275de0b 100644 --- a/src/storage/jani/VariableSet.h +++ b/src/storage/jani/VariableSet.h @@ -3,65 +3,26 @@ #include <vector> #include <set> -#include <boost/iterator/transform_iterator.hpp> +#include "src/adapters/DereferenceIteratorAdapter.h" #include "src/storage/jani/BooleanVariable.h" #include "src/storage/jani/UnboundedIntegerVariable.h" #include "src/storage/jani/BoundedIntegerVariable.h" +#include "src/storage/jani/RealVariable.h" namespace storm { namespace jani { - - class VariableSet; - - namespace detail { - - template<typename VariableType> - class Dereferencer { - public: - VariableType& operator()(std::shared_ptr<VariableType> const& d) const; - }; - - template<typename VariableType> - class Variables { - public: - typedef typename std::vector<std::shared_ptr<VariableType>>::iterator input_iterator; - typedef boost::transform_iterator<Dereferencer<VariableType>, input_iterator> iterator; - - Variables(input_iterator it, input_iterator ite); - iterator begin(); - iterator end(); - - private: - input_iterator it; - input_iterator ite; - }; - - template<typename VariableType> - class ConstVariables { - public: - typedef typename std::vector<std::shared_ptr<VariableType>>::const_iterator const_input_iterator; - typedef boost::transform_iterator<Dereferencer<VariableType const>, const_input_iterator> const_iterator; - - ConstVariables(const_input_iterator it, const_input_iterator ite); - - const_iterator begin(); - const_iterator end(); - - private: - const_input_iterator it; - const_input_iterator ite; - }; + namespace detail { + template <typename VariableType> + using Variables = storm::adapters::DereferenceIteratorAdapter<std::vector<std::shared_ptr<VariableType>>>; + + template <typename VariableType> + using ConstVariables = storm::adapters::DereferenceIteratorAdapter<std::vector<std::shared_ptr<VariableType>> const>; } class VariableSet { public: - typedef typename std::vector<std::shared_ptr<Variable>>::iterator input_iterator; - typedef typename std::vector<std::shared_ptr<Variable>>::const_iterator const_input_iterator; - typedef boost::transform_iterator<detail::Dereferencer<Variable>, input_iterator> iterator; - typedef boost::transform_iterator<detail::Dereferencer<Variable const>, const_input_iterator> const_iterator; - /*! * Creates an empty variable set. */ @@ -97,20 +58,35 @@ namespace storm { */ detail::ConstVariables<UnboundedIntegerVariable> getUnboundedIntegerVariables() const; + /*! + * Retrieves the real variables in this set. + */ + detail::Variables<RealVariable> getRealVariables(); + + /*! + * Retrieves the real variables in this set. + */ + detail::ConstVariables<RealVariable> getRealVariables() const; + /*! * Adds the given boolean variable to this set. */ - BooleanVariable const& addBooleanVariable(BooleanVariable const& variable); + BooleanVariable const& addVariable(BooleanVariable const& variable); /*! * Adds the given bounded integer variable to this set. */ - BoundedIntegerVariable const& addBoundedIntegerVariable(BoundedIntegerVariable const& variable); + BoundedIntegerVariable const& addVariable(BoundedIntegerVariable const& variable); /*! * Adds the given unbounded integer variable to this set. */ - UnboundedIntegerVariable const& addUnboundedIntegerVariable(UnboundedIntegerVariable const& variable); + UnboundedIntegerVariable const& addVariable(UnboundedIntegerVariable const& variable); + + /*! + * Adds the given real variable to this set. + */ + RealVariable const& addVariable(RealVariable const& variable); /*! * Retrieves whether this variable set contains a variable with the given name. @@ -132,25 +108,30 @@ namespace storm { */ Variable const& getVariable(storm::expressions::Variable const& variable) const; + /*! + * Retrieves whether this variable set contains a transient variable. + */ + bool hasTransientVariable() const; + /*! * Retrieves an iterator to the variables in this set. */ - iterator begin(); + typename detail::Variables<Variable>::iterator begin(); /*! * Retrieves an iterator to the variables in this set. */ - const_iterator begin() const; + typename detail::ConstVariables<Variable>::iterator begin() const; /*! * Retrieves the end iterator to the variables in this set. */ - iterator end(); + typename detail::Variables<Variable>::iterator end(); /*! * Retrieves the end iterator to the variables in this set. */ - const_iterator end() const; + typename detail::ConstVariables<Variable>::iterator end() const; /*! * Retrieves whether the set of variables contains a boolean variable. @@ -167,11 +148,31 @@ namespace storm { */ bool containsUnboundedIntegerVariables() const; + /*! + * Retrieves whether the set of variables contains a real variable. + */ + bool containsRealVariables() const; + + /*! + * Retrieves whether the set of variables contains a non-transient real variable. + */ + bool containsNonTransientRealVariables() const; + /*! * Retrieves whether this variable set is empty. */ bool empty() const; + /*! + * Retrieves the number of transient variables in this variable set. + */ + uint_fast64_t getNumberOfTransientVariables() const; + + /*! + * Retrieves a vector of transient variables in this variable set. + */ + std::vector<std::shared_ptr<Variable const>> getTransientVariables() const; + private: /// The vector of all variables. std::vector<std::shared_ptr<Variable>> variables; @@ -185,6 +186,9 @@ namespace storm { /// The unbounded integer variables in this set. std::vector<std::shared_ptr<UnboundedIntegerVariable>> unboundedIntegerVariables; + /// The real variables in this set. + std::vector<std::shared_ptr<RealVariable>> realVariables; + /// A set of all variable names currently in use. std::map<std::string, storm::expressions::Variable> nameToVariable; diff --git a/src/storage/prism/BooleanVariable.cpp b/src/storage/prism/BooleanVariable.cpp index c38812ec6..422b2266f 100644 --- a/src/storage/prism/BooleanVariable.cpp +++ b/src/storage/prism/BooleanVariable.cpp @@ -1,5 +1,7 @@ #include "src/storage/prism/BooleanVariable.h" +#include "src/storage/expressions/ExpressionManager.h" + namespace storm { namespace prism { BooleanVariable::BooleanVariable(storm::expressions::Variable const& variable, storm::expressions::Expression const& initialValueExpression, std::string const& filename, uint_fast64_t lineNumber) : Variable(variable, initialValueExpression, false, filename, lineNumber) { @@ -7,11 +9,21 @@ namespace storm { } BooleanVariable BooleanVariable::substitute(std::map<storm::expressions::Variable, storm::expressions::Expression> const& substitution) const { - return BooleanVariable(this->getExpressionVariable(), this->getInitialValueExpression().substitute(substitution), this->getFilename(), this->getLineNumber()); + return BooleanVariable(this->getExpressionVariable(), this->getInitialValueExpression().isInitialized() ? this->getInitialValueExpression().substitute(substitution) : this->getInitialValueExpression(), this->getFilename(), this->getLineNumber()); + } + + void BooleanVariable::createMissingInitialValue() { + if (!this->hasInitialValue()) { + this->setInitialValueExpression(this->getExpressionVariable().getManager().boolean(false)); + } } std::ostream& operator<<(std::ostream& stream, BooleanVariable const& variable) { - stream << variable.getName() << ": bool init " << variable.getInitialValueExpression() << ";"; + stream << variable.getName() << ": bool"; + if (variable.hasInitialValue()) { + stream << " init " << variable.getInitialValueExpression(); + } + stream << ";"; return stream; } diff --git a/src/storage/prism/BooleanVariable.h b/src/storage/prism/BooleanVariable.h index 5b43e191c..c5c3ba8e4 100644 --- a/src/storage/prism/BooleanVariable.h +++ b/src/storage/prism/BooleanVariable.h @@ -37,6 +37,8 @@ namespace storm { */ BooleanVariable substitute(std::map<storm::expressions::Variable, storm::expressions::Expression> const& substitution) const; + virtual void createMissingInitialValue() override; + friend std::ostream& operator<<(std::ostream& stream, BooleanVariable const& variable); }; diff --git a/src/storage/prism/Constant.cpp b/src/storage/prism/Constant.cpp index 0416dc4b9..1c3b07057 100644 --- a/src/storage/prism/Constant.cpp +++ b/src/storage/prism/Constant.cpp @@ -42,7 +42,12 @@ namespace storm { } std::ostream& operator<<(std::ostream& stream, Constant const& constant) { - stream << "const " << constant.getExpressionVariable().getType() << " "; + stream << "const "; + if (constant.getType().isRationalType()) { + stream << "double" << " "; + } else { + stream << constant.getType() << " "; + } stream << constant.getName(); if (constant.isDefined()) { stream << " = " << constant.getExpression(); diff --git a/src/storage/prism/IntegerVariable.cpp b/src/storage/prism/IntegerVariable.cpp index b2306ece0..8b60b0e56 100644 --- a/src/storage/prism/IntegerVariable.cpp +++ b/src/storage/prism/IntegerVariable.cpp @@ -19,11 +19,21 @@ namespace storm { } IntegerVariable IntegerVariable::substitute(std::map<storm::expressions::Variable, storm::expressions::Expression> const& substitution) const { - return IntegerVariable(this->getExpressionVariable(), this->getLowerBoundExpression().substitute(substitution), this->getUpperBoundExpression().substitute(substitution), this->getInitialValueExpression().substitute(substitution), this->getFilename(), this->getLineNumber()); + return IntegerVariable(this->getExpressionVariable(), this->getLowerBoundExpression().substitute(substitution), this->getUpperBoundExpression().substitute(substitution), this->getInitialValueExpression().isInitialized() ? this->getInitialValueExpression().substitute(substitution) : this->getInitialValueExpression(), this->getFilename(), this->getLineNumber()); + } + + void IntegerVariable::createMissingInitialValue() { + if (!this->hasInitialValue()) { + this->setInitialValueExpression(this->getLowerBoundExpression()); + } } std::ostream& operator<<(std::ostream& stream, IntegerVariable const& variable) { - stream << variable.getName() << ": [" << variable.getLowerBoundExpression() << ".." << variable.getUpperBoundExpression() << "]" << " init " << variable.getInitialValueExpression() << ";"; + stream << variable.getName() << ": [" << variable.getLowerBoundExpression() << ".." << variable.getUpperBoundExpression() << "]"; + if (variable.hasInitialValue()) { + stream << " init " << variable.getInitialValueExpression(); + } + stream << ";"; return stream; } } // namespace prism diff --git a/src/storage/prism/IntegerVariable.h b/src/storage/prism/IntegerVariable.h index fc1c6ecf5..335c2ce05 100644 --- a/src/storage/prism/IntegerVariable.h +++ b/src/storage/prism/IntegerVariable.h @@ -60,6 +60,8 @@ namespace storm { */ IntegerVariable substitute(std::map<storm::expressions::Variable, storm::expressions::Expression> const& substitution) const; + virtual void createMissingInitialValue() override; + friend std::ostream& operator<<(std::ostream& stream, IntegerVariable const& variable); private: diff --git a/src/storage/prism/Module.cpp b/src/storage/prism/Module.cpp index d4168196d..095ac3e11 100644 --- a/src/storage/prism/Module.cpp +++ b/src/storage/prism/Module.cpp @@ -42,7 +42,7 @@ namespace storm { std::vector<storm::prism::IntegerVariable> const& Module::getIntegerVariables() const { return this->integerVariables; } - + std::set<storm::expressions::Variable> Module::getAllExpressionVariables() const { std::set<storm::expressions::Variable> result; for (auto const& var : this->getBooleanVariables()) { @@ -188,10 +188,7 @@ namespace storm { std::vector<Command> newCommands; newCommands.reserve(this->getNumberOfCommands()); for (auto const& command : this->getCommands()) { - Command newCommand = command.substitute(substitution); - if (!newCommand.getGuardExpression().isFalse()) { - newCommands.emplace_back(newCommand); - } + newCommands.emplace_back(command.substitute(substitution)); } return Module(this->getName(), newBooleanVariables, newIntegerVariables, newCommands, this->getFilename(), this->getLineNumber()); @@ -216,12 +213,23 @@ namespace storm { } for (auto const& command : this->getCommands()) { - command.containsVariablesOnlyInUpdateProbabilities(undefinedConstantVariables); + if (!command.containsVariablesOnlyInUpdateProbabilities(undefinedConstantVariables)) { + return false; + } } return true; } + void Module::createMissingInitialValues() { + for (auto& variable : booleanVariables) { + variable.createMissingInitialValue(); + } + for (auto& variable : integerVariables) { + variable.createMissingInitialValue(); + } + } + std::ostream& operator<<(std::ostream& stream, Module const& module) { stream << "module " << module.getName() << std::endl; for (auto const& booleanVariable : module.getBooleanVariables()) { diff --git a/src/storage/prism/Module.h b/src/storage/prism/Module.h index 620432580..c3179b407 100644 --- a/src/storage/prism/Module.h +++ b/src/storage/prism/Module.h @@ -104,7 +104,6 @@ namespace storm { */ std::set<storm::expressions::Variable> getAllExpressionVariables() const; - /*! * Retrieves a list of expressions that characterize the legal ranges of all variables declared by this * module. @@ -226,6 +225,11 @@ namespace storm { */ bool containsVariablesOnlyInUpdateProbabilities(std::set<storm::expressions::Variable> const& undefinedConstantVariables) const; + /*! + * Equips all of the modules' variables without initial values with initial values based on their type. + */ + void createMissingInitialValues(); + friend std::ostream& operator<<(std::ostream& stream, Module const& module); private: diff --git a/src/storage/prism/Program.cpp b/src/storage/prism/Program.cpp index 3562747e7..126f53c4e 100644 --- a/src/storage/prism/Program.cpp +++ b/src/storage/prism/Program.cpp @@ -20,7 +20,7 @@ #include "src/storage/prism/CompositionVisitor.h" #include "src/storage/prism/Compositions.h" -#include "src/storage/prism/CompositionToJaniVisitor.h" +#include "src/storage/prism/ToJaniConverter.h" namespace storm { namespace prism { @@ -148,28 +148,15 @@ namespace storm { // Start by creating the necessary mappings from the given ones. this->createMappings(); - // Set the initial construct. + // Set the initial construct if given. if (initialConstruct) { this->initialConstruct = initialConstruct.get(); } else { - // Create a new initial construct if none was given. - storm::expressions::Expression newInitialExpression = manager->boolean(true); - - for (auto const& booleanVariable : this->getGlobalBooleanVariables()) { - newInitialExpression = newInitialExpression && storm::expressions::iff(booleanVariable.getExpression(), booleanVariable.getInitialValueExpression()); - } - for (auto const& integerVariable : this->getGlobalIntegerVariables()) { - newInitialExpression = newInitialExpression && integerVariable.getExpression() == integerVariable.getInitialValueExpression(); + // Otherwise, we create the missing initial values. + this->createMissingInitialValues(); + for (auto& modules : this->modules) { + modules.createMissingInitialValues(); } - for (auto const& module : this->getModules()) { - for (auto const& booleanVariable : module.getBooleanVariables()) { - newInitialExpression = newInitialExpression && storm::expressions::iff(booleanVariable.getExpression(), booleanVariable.getInitialValueExpression()); - } - for (auto const& integerVariable : module.getIntegerVariables()) { - newInitialExpression = newInitialExpression && integerVariable.getExpression() == integerVariable.getInitialValueExpression(); - } - } - this->initialConstruct = storm::prism::InitialConstruct(newInitialExpression, this->getInitialConstruct().getFilename(), this->getInitialConstruct().getLineNumber()); } if (finalModel) { @@ -230,7 +217,7 @@ namespace storm { // constants' variables is empty (except for the update probabilities). // Start by checking the defining expressions of all defined constants. If it contains a currently undefined - //constant, we need to mark the target constant as undefined as well. + // constant, we need to mark the target constant as undefined as well. for (auto const& constant : this->getConstants()) { if (constant.isDefined()) { if (constant.getExpression().containsVariable(undefinedConstantVariables)) { @@ -241,13 +228,17 @@ namespace storm { // Now check initial value expressions of global variables. for (auto const& booleanVariable : this->getGlobalBooleanVariables()) { - if (booleanVariable.getInitialValueExpression().containsVariable(undefinedConstantVariables)) { - return false; + if (booleanVariable.hasInitialValue()) { + if (booleanVariable.getInitialValueExpression().containsVariable(undefinedConstantVariables)) { + return false; + } } } for (auto const& integerVariable : this->getGlobalIntegerVariables()) { - if (integerVariable.getInitialValueExpression().containsVariable(undefinedConstantVariables)) { - return false; + if (integerVariable.hasInitialValue()) { + if (integerVariable.getInitialValueExpression().containsVariable(undefinedConstantVariables)) { + return false; + } } if (integerVariable.getLowerBoundExpression().containsVariable(undefinedConstantVariables)) { return false; @@ -266,7 +257,9 @@ namespace storm { // Proceed by checking each of the modules. for (auto const& module : this->getModules()) { - module.containsVariablesOnlyInUpdateProbabilities(undefinedConstantVariables); + if (!module.containsVariablesOnlyInUpdateProbabilities(undefinedConstantVariables)) { + return false; + } } // Check the reward models. @@ -275,8 +268,10 @@ namespace storm { } // Initial construct. - if (this->getInitialConstruct().getInitialStatesExpression().containsVariable(undefinedConstantVariables)) { - return false; + if (this->hasInitialConstruct()) { + if (this->getInitialConstruct().getInitialStatesExpression().containsVariable(undefinedConstantVariables)) { + return false; + } } // Labels. @@ -337,7 +332,6 @@ namespace storm { return constantsSubstitution; } - std::size_t Program::getNumberOfConstants() const { return this->getConstants().size(); } @@ -446,10 +440,61 @@ namespace storm { return actionToIndexMap; } + bool Program::hasInitialConstruct() const { + return static_cast<bool>(initialConstruct); + } + storm::prism::InitialConstruct const& Program::getInitialConstruct() const { + return this->initialConstruct.get(); + } + + boost::optional<InitialConstruct> const& Program::getOptionalInitialConstruct() const { return this->initialConstruct; } + storm::expressions::Expression Program::getInitialStatesExpression() const { + // If there is an initial construct, return its expression. If not, we construct the expression from the + // initial values of the variables (which have to exist). + if (this->hasInitialConstruct()) { + return this->getInitialConstruct().getInitialStatesExpression(); + } else { + storm::expressions::Expression result; + + for (auto const& variable : this->getGlobalBooleanVariables()) { + if (result.isInitialized()) { + result = result && storm::expressions::iff(variable.getExpressionVariable(), variable.getInitialValueExpression()); + } else { + result = storm::expressions::iff(variable.getExpressionVariable(), variable.getInitialValueExpression()); + } + } + for (auto const& variable : this->getGlobalIntegerVariables()) { + if (result.isInitialized()) { + result = result && variable.getExpressionVariable() == variable.getInitialValueExpression(); + } else { + result = variable.getExpressionVariable() == variable.getInitialValueExpression(); + } + } + for (auto const& module : this->getModules()) { + for (auto const& variable : module.getBooleanVariables()) { + if (result.isInitialized()) { + result = result && storm::expressions::iff(variable.getExpressionVariable(), variable.getInitialValueExpression()); + } else { + result = storm::expressions::iff(variable.getExpressionVariable(), variable.getInitialValueExpression()); + } + } + for (auto const& variable : module.getIntegerVariables()) { + if (result.isInitialized()) { + result = result && variable.getExpressionVariable() == variable.getInitialValueExpression(); + } else { + result = variable.getExpressionVariable() == variable.getInitialValueExpression(); + } + } + } + + return result; + } + } + bool Program::specifiesSystemComposition() const { return static_cast<bool>(systemCompositionConstruct); } @@ -611,7 +656,7 @@ namespace storm { newModules.push_back(module.restrictCommands(indexSet)); } - return Program(this->manager, this->getModelType(), this->getConstants(), this->getGlobalBooleanVariables(), this->getGlobalIntegerVariables(), this->getFormulas(), newModules, this->getActionNameToIndexMapping(), this->getRewardModels(), this->getLabels(), this->getInitialConstruct(), this->getOptionalSystemCompositionConstruct()); + return Program(this->manager, this->getModelType(), this->getConstants(), this->getGlobalBooleanVariables(), this->getGlobalIntegerVariables(), this->getFormulas(), newModules, this->getActionNameToIndexMapping(), this->getRewardModels(), this->getLabels(), this->getOptionalInitialConstruct(), this->getOptionalSystemCompositionConstruct()); } void Program::createMappings() { @@ -711,11 +756,11 @@ namespace storm { STORM_LOG_THROW(definedUndefinedConstants.find(constantExpressionPair.first) != definedUndefinedConstants.end(), storm::exceptions::InvalidArgumentException, "Unable to define non-existant constant."); } - return Program(this->manager, this->getModelType(), newConstants, this->getGlobalBooleanVariables(), this->getGlobalIntegerVariables(), this->getFormulas(), this->getModules(), this->getActionNameToIndexMapping(), this->getRewardModels(), this->getLabels(), this->getInitialConstruct(), this->getOptionalSystemCompositionConstruct()); + return Program(this->manager, this->getModelType(), newConstants, this->getGlobalBooleanVariables(), this->getGlobalIntegerVariables(), this->getFormulas(), this->getModules(), this->getActionNameToIndexMapping(), this->getRewardModels(), this->getLabels(), this->getOptionalInitialConstruct(), this->getOptionalSystemCompositionConstruct()); } Program Program::substituteConstants() const { - // We start by creating the appropriate substitution + // We start by creating the appropriate substitution. std::map<storm::expressions::Variable, storm::expressions::Expression> constantSubstitution; std::vector<Constant> newConstants(this->getConstants()); for (uint_fast64_t constantIndex = 0; constantIndex < newConstants.size(); ++constantIndex) { @@ -763,7 +808,10 @@ namespace storm { newRewardModels.emplace_back(rewardModel.substitute(constantSubstitution)); } - storm::prism::InitialConstruct newInitialConstruct = this->getInitialConstruct().substitute(constantSubstitution); + boost::optional<storm::prism::InitialConstruct> newInitialConstruct; + if (this->hasInitialConstruct()) { + newInitialConstruct = this->getInitialConstruct().substitute(constantSubstitution); + } std::vector<Label> newLabels; newLabels.reserve(this->getNumberOfLabels()); @@ -807,18 +855,22 @@ namespace storm { // Now we check the variable declarations. We start with the global variables. std::set<storm::expressions::Variable> variables; for (auto const& variable : this->getGlobalBooleanVariables()) { - // Check the initial value of the variable. - std::set<storm::expressions::Variable> containedVariables = variable.getInitialValueExpression().getVariables(); - std::set<storm::expressions::Variable> illegalVariables; - std::set_difference(containedVariables.begin(), containedVariables.end(), constants.begin(), constants.end(), std::inserter(illegalVariables, illegalVariables.begin())); - bool isValid = illegalVariables.empty(); - - if (!isValid) { - std::vector<std::string> illegalVariableNames; - for (auto const& var : illegalVariables) { - illegalVariableNames.push_back(var.getName()); + if (variable.hasInitialValue()) { + STORM_LOG_THROW(!this->hasInitialConstruct(), storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": illegal to specify initial value if an initial construct is present."); + + // Check the initial value of the variable. + std::set<storm::expressions::Variable> containedVariables = variable.getInitialValueExpression().getVariables(); + std::set<storm::expressions::Variable> illegalVariables; + std::set_difference(containedVariables.begin(), containedVariables.end(), constants.begin(), constants.end(), std::inserter(illegalVariables, illegalVariables.begin())); + bool isValid = illegalVariables.empty(); + + if (!isValid) { + std::vector<std::string> illegalVariableNames; + for (auto const& var : illegalVariables) { + illegalVariableNames.push_back(var.getName()); + } + STORM_LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": initial value expression refers to unknown constants: " << boost::algorithm::join(illegalVariableNames, ",") << "."); } - STORM_LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": initial value expression refers to unknown constants: " << boost::algorithm::join(illegalVariableNames, ",") << "."); } // Record the new identifier for future checks. @@ -853,16 +905,20 @@ namespace storm { STORM_LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": upper bound expression refers to unknown constants: " << boost::algorithm::join(illegalVariableNames, ",") << "."); } - // Check the initial value of the variable. - containedVariables = variable.getInitialValueExpression().getVariables(); - std::set_difference(containedVariables.begin(), containedVariables.end(), constants.begin(), constants.end(), std::inserter(illegalVariables, illegalVariables.begin())); - isValid = illegalVariables.empty(); - if (!isValid) { - std::vector<std::string> illegalVariableNames; - for (auto const& var : illegalVariables) { - illegalVariableNames.push_back(var.getName()); + if (variable.hasInitialValue()) { + STORM_LOG_THROW(!this->hasInitialConstruct(), storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": illegal to specify initial value if an initial construct is present."); + + // Check the initial value of the variable. + containedVariables = variable.getInitialValueExpression().getVariables(); + std::set_difference(containedVariables.begin(), containedVariables.end(), constants.begin(), constants.end(), std::inserter(illegalVariables, illegalVariables.begin())); + isValid = illegalVariables.empty(); + if (!isValid) { + std::vector<std::string> illegalVariableNames; + for (auto const& var : illegalVariables) { + illegalVariableNames.push_back(var.getName()); + } + STORM_LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": initial value expression refers to unknown constants: " << boost::algorithm::join(illegalVariableNames, ",") << "."); } - STORM_LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": initial value expression refers to unknown constants: " << boost::algorithm::join(illegalVariableNames, ",") << "."); } // Record the new identifier for future checks. @@ -875,17 +931,21 @@ namespace storm { // Now go through the variables of the modules. for (auto const& module : this->getModules()) { for (auto const& variable : module.getBooleanVariables()) { - // Check the initial value of the variable. - std::set<storm::expressions::Variable> containedVariables = variable.getInitialValueExpression().getVariables(); - std::set<storm::expressions::Variable> illegalVariables; - std::set_difference(containedVariables.begin(), containedVariables.end(), constants.begin(), constants.end(), std::inserter(illegalVariables, illegalVariables.begin())); - bool isValid = illegalVariables.empty(); - if (!isValid) { - std::vector<std::string> illegalVariableNames; - for (auto const& var : illegalVariables) { - illegalVariableNames.push_back(var.getName()); + if (variable.hasInitialValue()) { + STORM_LOG_THROW(!this->hasInitialConstruct(), storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": illegal to specify initial value if an initial construct is present."); + + // Check the initial value of the variable. + std::set<storm::expressions::Variable> containedVariables = variable.getInitialValueExpression().getVariables(); + std::set<storm::expressions::Variable> illegalVariables; + std::set_difference(containedVariables.begin(), containedVariables.end(), constants.begin(), constants.end(), std::inserter(illegalVariables, illegalVariables.begin())); + bool isValid = illegalVariables.empty(); + if (!isValid) { + std::vector<std::string> illegalVariableNames; + for (auto const& var : illegalVariables) { + illegalVariableNames.push_back(var.getName()); + } + STORM_LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": initial value expression refers to unknown constants: " << boost::algorithm::join(illegalVariableNames, ",") << "."); } - STORM_LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": initial value expression refers to unknown constants: " << boost::algorithm::join(illegalVariableNames, ",") << "."); } // Record the new identifier for future checks. @@ -918,17 +978,21 @@ namespace storm { STORM_LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": upper bound expression refers to unknown constants: " << boost::algorithm::join(illegalVariableNames, ",") << "."); } - // Check the initial value of the variable. - containedVariables = variable.getInitialValueExpression().getVariables(); - illegalVariables.clear(); - std::set_difference(containedVariables.begin(), containedVariables.end(), constants.begin(), constants.end(), std::inserter(illegalVariables, illegalVariables.begin())); - isValid = illegalVariables.empty(); - if (!isValid) { - std::vector<std::string> illegalVariableNames; - for (auto const& var : illegalVariables) { - illegalVariableNames.push_back(var.getName()); + if (variable.hasInitialValue()) { + STORM_LOG_THROW(!this->hasInitialConstruct(), storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": illegal to specify initial value if an initial construct is present."); + + // Check the initial value of the variable. + containedVariables = variable.getInitialValueExpression().getVariables(); + illegalVariables.clear(); + std::set_difference(containedVariables.begin(), containedVariables.end(), constants.begin(), constants.end(), std::inserter(illegalVariables, illegalVariables.begin())); + isValid = illegalVariables.empty(); + if (!isValid) { + std::vector<std::string> illegalVariableNames; + for (auto const& var : illegalVariables) { + illegalVariableNames.push_back(var.getName()); + } + STORM_LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": initial value expression refers to unknown constants: " << boost::algorithm::join(illegalVariableNames, ",") << "."); } - STORM_LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << variable.getFilename() << ", line " << variable.getLineNumber() << ": initial value expression refers to unknown constants: " << boost::algorithm::join(illegalVariableNames, ",") << "."); } // Record the new identifier for future checks. @@ -1091,9 +1155,11 @@ namespace storm { } // Check the initial states expression. - std::set<storm::expressions::Variable> containedIdentifiers = this->getInitialConstruct().getInitialStatesExpression().getVariables(); - bool isValid = std::includes(variablesAndConstants.begin(), variablesAndConstants.end(), containedIdentifiers.begin(), containedIdentifiers.end()); - STORM_LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << this->getInitialConstruct().getFilename() << ", line " << this->getInitialConstruct().getLineNumber() << ": initial expression refers to unknown identifiers."); + if (this->hasInitialConstruct()) { + std::set<storm::expressions::Variable> containedIdentifiers = this->getInitialConstruct().getInitialStatesExpression().getVariables(); + bool isValid = std::includes(variablesAndConstants.begin(), variablesAndConstants.end(), containedIdentifiers.begin(), containedIdentifiers.end()); + STORM_LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Error in " << this->getInitialConstruct().getFilename() << ", line " << this->getInitialConstruct().getLineNumber() << ": initial construct refers to unknown identifiers."); + } // Check the system composition if given. if (systemCompositionConstruct) { @@ -1151,17 +1217,28 @@ namespace storm { } Program Program::simplify() { + // Start by substituting the constants, because this will potentially erase some commands or even actions. + Program substitutedProgram = this->substituteConstants(); + + // As we possibly delete some commands and some actions might be dropped from modules altogether, we need to + // maintain a list of actions that we need to remove in other modules. For example, if module A loses all [a] + // commands, we need to delete all [a] commands from all other modules as well. If we do not do that, we will + // remove the forced synchronization that was there before. + std::set<uint_fast64_t> actionIndicesToDelete; + std::vector<Module> newModules; - std::vector<Constant> newConstants = this->getConstants(); - for (auto const& module : this->getModules()) { - // Remove identity assignments from the updates + std::vector<Constant> newConstants = substitutedProgram.getConstants(); + for (auto const& module : substitutedProgram.getModules()) { + + // Discard all commands with a guard equivalent to false and remove identity assignments from the updates. std::vector<Command> newCommands; for (auto const& command : module.getCommands()) { - newCommands.emplace_back(command.removeIdentityAssignmentsFromUpdates()); + if (!command.getGuardExpression().isFalse()) { + newCommands.emplace_back(command.removeIdentityAssignmentsFromUpdates()); + } } - // Substitute Variables by Global constants if possible. - + // Substitute variables by global constants if possible. std::map<storm::expressions::Variable, storm::expressions::Expression> booleanVars; std::map<storm::expressions::Variable, storm::expressions::Expression> integerVars; for (auto const& variable : module.getBooleanVariables()) { @@ -1171,54 +1248,84 @@ namespace storm { integerVars.emplace(variable.getExpressionVariable(), variable.getInitialValueExpression()); } + // Collect all variables that are being written. These variables cannot be turned to constants. for (auto const& command : newCommands) { // Check all updates. for (auto const& update : command.getUpdates()) { // Check all assignments. for (auto const& assignment : update.getAssignments()) { - auto bit = booleanVars.find(assignment.getVariable()); - if(bit != booleanVars.end()) { - booleanVars.erase(bit); + if (assignment.getVariable().getType().isBooleanType()) { + auto it = booleanVars.find(assignment.getVariable()); + if (it != booleanVars.end()) { + booleanVars.erase(it); + } } else { - auto iit = integerVars.find(assignment.getVariable()); - if(iit != integerVars.end()) { - integerVars.erase(iit); + auto it = integerVars.find(assignment.getVariable()); + if (it != integerVars.end()) { + integerVars.erase(it); } } } } } - std::vector<storm::prism::BooleanVariable> newBVars; - for(auto const& variable : module.getBooleanVariables()) { - if(booleanVars.count(variable.getExpressionVariable()) == 0) { - newBVars.push_back(variable); + std::vector<storm::prism::BooleanVariable> newBooleanVars; + for (auto const& variable : module.getBooleanVariables()) { + if (booleanVars.find(variable.getExpressionVariable()) == booleanVars.end()) { + newBooleanVars.push_back(variable); } } - std::vector<storm::prism::IntegerVariable> newIVars; - for(auto const& variable : module.getIntegerVariables()) { - if(integerVars.count(variable.getExpressionVariable()) == 0) { - newIVars.push_back(variable); + std::vector<storm::prism::IntegerVariable> newIntegerVars; + for (auto const& variable : module.getIntegerVariables()) { + if (integerVars.find(variable.getExpressionVariable()) == integerVars.end()) { + newIntegerVars.push_back(variable); } } - newModules.emplace_back(module.getName(), newBVars, newIVars, newCommands); + newModules.emplace_back(module.getName(), newBooleanVars, newIntegerVars, newCommands); + + // Determine the set of action indices that have been deleted entirely. + std::set_difference(module.getSynchronizingActionIndices().begin(), module.getSynchronizingActionIndices().end(), newModules.back().getSynchronizingActionIndices().begin(), newModules.back().getSynchronizingActionIndices().end(), std::inserter(actionIndicesToDelete, actionIndicesToDelete.begin())); - for(auto const& entry : booleanVars) { + for (auto const& entry : booleanVars) { newConstants.emplace_back(entry.first, entry.second); } - for(auto const& entry : integerVars) { + for (auto const& entry : integerVars) { newConstants.emplace_back(entry.first, entry.second); } } - return replaceModulesAndConstantsInProgram(newModules, newConstants).substituteConstants(); + // If we have to delete whole actions, do so now. + std::map<std::string, uint_fast64_t> newActionToIndexMap; + std::vector<RewardModel> newRewardModels; + if (!actionIndicesToDelete.empty()) { + boost::container::flat_set<uint_fast64_t> actionsToKeep; + std::set_difference(this->getSynchronizingActionIndices().begin(), this->getSynchronizingActionIndices().end(), actionIndicesToDelete.begin(), actionIndicesToDelete.end(), std::inserter(actionsToKeep, actionsToKeep.begin())); + + // Insert the silent action as this is not contained in the synchronizing action indices. + actionsToKeep.insert(0); + + std::vector<Module> cleanedModules; + cleanedModules.reserve(newModules.size()); + for (auto const& module : newModules) { + cleanedModules.emplace_back(module.restrictCommands(actionsToKeep)); + } + newModules = std::move(cleanedModules); + + newRewardModels.reserve(substitutedProgram.getNumberOfRewardModels()); + for (auto const& rewardModel : substitutedProgram.getRewardModels()) { + newRewardModels.emplace_back(rewardModel.restrictActionRelatedRewards(actionsToKeep)); + } + + for (auto const& entry : this->getActionNameToIndexMapping()) { + if (actionsToKeep.find(entry.second) != actionsToKeep.end()) { + newActionToIndexMap.emplace(entry.first, entry.second); + } + } + } - } - - Program Program::replaceModulesAndConstantsInProgram(std::vector<Module> const& newModules, std::vector<Constant> const& newConstants) { - return Program(this->manager, modelType, newConstants, getGlobalBooleanVariables(), getGlobalIntegerVariables(), getFormulas(), newModules, getActionNameToIndexMapping(), getRewardModels(), getLabels(), getInitialConstruct(), this->getOptionalSystemCompositionConstruct()); + return Program(this->manager, modelType, newConstants, getGlobalBooleanVariables(), getGlobalIntegerVariables(), getFormulas(), newModules, actionIndicesToDelete.empty() ? getActionNameToIndexMapping() : newActionToIndexMap, actionIndicesToDelete.empty() ? this->getRewardModels() : newRewardModels, getLabels(), getOptionalInitialConstruct(), this->getOptionalSystemCompositionConstruct()); } Program Program::flattenModules(std::unique_ptr<storm::utility::solver::SmtSolverFactory> const& smtSolverFactory) const { @@ -1404,7 +1511,7 @@ namespace storm { // Finally, we can create the module and the program and return it. storm::prism::Module singleModule(newModuleName.str(), allBooleanVariables, allIntegerVariables, newCommands, this->getFilename(), 0); - return Program(manager, this->getModelType(), this->getConstants(), std::vector<storm::prism::BooleanVariable>(), std::vector<storm::prism::IntegerVariable>(), this->getFormulas(), {singleModule}, actionToIndexMap, this->getRewardModels(), this->getLabels(), this->getInitialConstruct(), this->getOptionalSystemCompositionConstruct(), this->getFilename(), 0, true); + return Program(manager, this->getModelType(), this->getConstants(), std::vector<storm::prism::BooleanVariable>(), std::vector<storm::prism::IntegerVariable>(), this->getFormulas(), {singleModule}, actionToIndexMap, this->getRewardModels(), this->getLabels(), this->getOptionalInitialConstruct(), this->getOptionalSystemCompositionConstruct(), this->getFilename(), 0, true); } std::unordered_map<uint_fast64_t, std::string> Program::buildCommandIndexToActionNameMap() const { @@ -1493,173 +1600,30 @@ namespace storm { return Command(newCommandIndex, false, actionIndex, actionName, newGuard, newUpdates, this->getFilename(), 0); } - uint_fast64_t Program::numberOfActions() const { + uint_fast64_t Program::getNumberOfActions() const { return this->actions.size(); } - uint_fast64_t Program::largestActionIndex() const { - assert(numberOfActions() != 0); - return this->indexToActionMap.rbegin()->first; - } - - storm::expressions::ExpressionManager const& Program::getManager() const { - return *this->manager; + storm::jani::Model Program::toJani(bool allVariablesGlobal) const { + ToJaniConverter converter; + return converter.convert(*this, allVariablesGlobal); } - storm::expressions::ExpressionManager& Program::getManager() { + storm::expressions::ExpressionManager& Program::getManager() const { return *this->manager; } - storm::jani::Model Program::toJani(bool allVariablesGlobal) const { - // Start by creating an empty JANI model. - storm::jani::ModelType modelType; - switch (this->getModelType()) { - case Program::ModelType::DTMC: modelType = storm::jani::ModelType::DTMC; - break; - case Program::ModelType::CTMC: modelType = storm::jani::ModelType::CTMC; - break; - case Program::ModelType::MDP: modelType = storm::jani::ModelType::MDP; - break; - case Program::ModelType::CTMDP: modelType = storm::jani::ModelType::CTMDP; - break; - case Program::ModelType::MA: modelType = storm::jani::ModelType::MA; - break; - default: modelType = storm::jani::ModelType::UNDEFINED; - } - storm::jani::Model janiModel("jani_from_prism", modelType, 1, manager); - - // Add all constants of the PRISM program to the JANI model. - for (auto const& constant : constants) { - janiModel.addConstant(storm::jani::Constant(constant.getName(), constant.getExpressionVariable(), constant.isDefined() ? boost::optional<storm::expressions::Expression>(constant.getExpression()) : boost::none)); - } - - // Maintain a mapping from expression variables to JANI variables so we can fill in the correct objects when - // creating assignments. - std::map<storm::expressions::Variable, std::reference_wrapper<storm::jani::Variable const>> variableToVariableMap; - - // Add all global variables of the PRISM program to the JANI model. - for (auto const& variable : globalIntegerVariables) { - storm::jani::BoundedIntegerVariable const& newVariable = janiModel.addBoundedIntegerVariable(storm::jani::BoundedIntegerVariable(variable.getName(), variable.getExpressionVariable(), variable.getInitialValueExpression(), variable.getLowerBoundExpression(), variable.getUpperBoundExpression())); - variableToVariableMap.emplace(variable.getExpressionVariable(), newVariable); - } - for (auto const& variable : globalBooleanVariables) { - storm::jani::BooleanVariable const& newVariable = janiModel.addBooleanVariable(storm::jani::BooleanVariable(variable.getName(), variable.getExpressionVariable(), variable.getInitialValueExpression())); - variableToVariableMap.emplace(variable.getExpressionVariable(), newVariable); - } - - // Add all actions of the PRISM program to the JANI model. - for (auto const& action : indexToActionMap) { - // Ignore the empty action as every JANI program has predefined tau action. - if (!action.second.empty()) { - janiModel.addAction(storm::jani::Action(action.second)); - } - } - - // Because of the rules of JANI, we have to make all variables of modules global that are read by other modules. - - // Create a mapping from variables to the indices of module indices that write/read the variable. - std::map<storm::expressions::Variable, std::set<uint_fast64_t>> variablesToAccessingModuleIndices; - for (uint_fast64_t index = 0; index < modules.size(); ++index) { - storm::prism::Module const& module = modules[index]; - - for (auto const& command : module.getCommands()) { - std::set<storm::expressions::Variable> variables = command.getGuardExpression().getVariables(); - for (auto const& variable : variables) { - variablesToAccessingModuleIndices[variable].insert(index); - } - - for (auto const& update : command.getUpdates()) { - for (auto const& assignment : update.getAssignments()) { - variables = assignment.getExpression().getVariables(); - for (auto const& variable : variables) { - variablesToAccessingModuleIndices[variable].insert(index); - } - variablesToAccessingModuleIndices[assignment.getVariable()].insert(index); - } - } + void Program::createMissingInitialValues() { + for (auto& variable : globalBooleanVariables) { + if (!variable.hasInitialValue()) { + variable.setInitialValueExpression(manager->boolean(false)); } } - - // Now create the separate JANI automata from the modules of the PRISM program. While doing so, we use the - // previously built mapping to make variables global that are read by more than one module. - for (auto const& module : modules) { - storm::jani::Automaton automaton(module.getName()); - - for (auto const& variable : module.getIntegerVariables()) { - storm::jani::BoundedIntegerVariable newIntegerVariable(variable.getName(), variable.getExpressionVariable(), variable.getInitialValueExpression(), variable.getLowerBoundExpression(), variable.getUpperBoundExpression()); - std::set<uint_fast64_t> const& accessingModuleIndices = variablesToAccessingModuleIndices[variable.getExpressionVariable()]; - // If there is exactly one module reading and writing the variable, we can make the variable local to this module. - if (!allVariablesGlobal && accessingModuleIndices.size() == 1) { - storm::jani::BoundedIntegerVariable const& newVariable = automaton.addBoundedIntegerVariable(newIntegerVariable); - variableToVariableMap.emplace(variable.getExpressionVariable(), newVariable); - } else if (!accessingModuleIndices.empty()) { - // Otherwise, we need to make it global. - storm::jani::BoundedIntegerVariable const& newVariable = janiModel.addBoundedIntegerVariable(newIntegerVariable); - variableToVariableMap.emplace(variable.getExpressionVariable(), newVariable); - } - } - for (auto const& variable : module.getBooleanVariables()) { - storm::jani::BooleanVariable newBooleanVariable(variable.getName(), variable.getExpressionVariable(), variable.getInitialValueExpression()); - std::set<uint_fast64_t> const& accessingModuleIndices = variablesToAccessingModuleIndices[variable.getExpressionVariable()]; - // If there is exactly one module reading and writing the variable, we can make the variable local to this module. - if (!allVariablesGlobal && accessingModuleIndices.size() == 1) { - storm::jani::BooleanVariable const& newVariable = automaton.addBooleanVariable(newBooleanVariable); - variableToVariableMap.emplace(variable.getExpressionVariable(), newVariable); - } else if (!accessingModuleIndices.empty()) { - // Otherwise, we need to make it global. - storm::jani::BooleanVariable const& newVariable = janiModel.addBooleanVariable(newBooleanVariable); - variableToVariableMap.emplace(variable.getExpressionVariable(), newVariable); - } + for (auto& variable : globalIntegerVariables) { + if (!variable.hasInitialValue()) { + variable.setInitialValueExpression(variable.getLowerBoundExpression()); } - automaton.setInitialStatesRestriction(manager->boolean(true)); - - // Create a single location that will have all the edges. - uint64_t onlyLocation = automaton.addLocation(storm::jani::Location("l")); - automaton.addInitialLocation(onlyLocation); - - for (auto const& command : module.getCommands()) { - boost::optional<storm::expressions::Expression> rateExpression; - std::vector<storm::jani::EdgeDestination> destinations; - if (this->getModelType() == Program::ModelType::CTMC || this->getModelType() == Program::ModelType::CTMDP) { - for (auto const& update : command.getUpdates()) { - if (rateExpression) { - rateExpression = rateExpression.get() + update.getLikelihoodExpression(); - } else { - rateExpression = update.getLikelihoodExpression(); - } - } - } - - for (auto const& update : command.getUpdates()) { - std::vector<storm::jani::Assignment> assignments; - for (auto const& assignment : update.getAssignments()) { - assignments.push_back(storm::jani::Assignment(variableToVariableMap.at(assignment.getVariable()).get(), assignment.getExpression())); - } - - if (rateExpression) { - destinations.push_back(storm::jani::EdgeDestination(onlyLocation, manager->integer(1) / rateExpression.get(), assignments)); - } else { - destinations.push_back(storm::jani::EdgeDestination(onlyLocation, update.getLikelihoodExpression(), assignments)); - } - } - automaton.addEdge(storm::jani::Edge(onlyLocation, janiModel.getActionIndex(command.getActionName()), rateExpression, command.getGuardExpression(), destinations)); - } - - janiModel.addAutomaton(automaton); } - janiModel.setInitialStatesRestriction(manager->boolean(true)); - - // Set the standard system composition. This is possible, because we reject non-standard compositions anyway. - if (this->specifiesSystemComposition()) { - CompositionToJaniVisitor visitor; - janiModel.setSystemComposition(visitor.toJani(this->getSystemCompositionConstruct().getSystemComposition(), janiModel)); - } else { - janiModel.setSystemComposition(janiModel.getStandardSystemComposition()); - } - - janiModel.finalize(); - - return janiModel; } std::ostream& operator<<(std::ostream& out, Program::ModelType const& type) { @@ -1706,7 +1670,9 @@ namespace storm { stream << label << std::endl; } - stream << program.getInitialConstruct() << std::endl; + if (program.hasInitialConstruct()) { + stream << program.getInitialConstruct() << std::endl; + } if (program.specifiesSystemComposition()) { stream << program.getSystemCompositionConstruct(); diff --git a/src/storage/prism/Program.h b/src/storage/prism/Program.h index 71719c583..bc17072e1 100644 --- a/src/storage/prism/Program.h +++ b/src/storage/prism/Program.h @@ -175,7 +175,7 @@ namespace storm { * @return The global boolean variables of the program. */ std::vector<BooleanVariable> const& getGlobalBooleanVariables() const; - + /*! * Retrieves a the global boolean variable with the given name. * @@ -285,12 +285,31 @@ namespace storm { */ std::map<std::string, uint_fast64_t> const& getActionNameToIndexMapping() const; + /*! + * Retrieves whether the program specifies an initial construct. + */ + bool hasInitialConstruct() const; + /*! * Retrieves the initial construct of the program. * * @return The initial construct of the program. */ InitialConstruct const& getInitialConstruct() const; + + /*! + * Retrieves an optional containing the initial construct of the program if there is any and nothing otherwise. + * + * @return The initial construct of the program. + */ + boost::optional<InitialConstruct> const& getOptionalInitialConstruct() const; + + /*! + * Retrieves an expression characterizing the initial states. + * + * @return an expression characterizing the initial states. + */ + storm::expressions::Expression getInitialStatesExpression() const; /*! * Retrieves whether the program specifies a system composition in terms of process algebra operations over @@ -547,27 +566,15 @@ namespace storm { * * @return The manager responsible for the expressions of this program. */ - storm::expressions::ExpressionManager const& getManager() const; - - /*! - * Retrieves the manager responsible for the expressions of this program. - * - * @return The manager responsible for the expressions of this program. - */ - storm::expressions::ExpressionManager& getManager(); + storm::expressions::ExpressionManager& getManager() const; - /*! - * - */ std::unordered_map<uint_fast64_t, std::string> buildCommandIndexToActionNameMap() const; std::unordered_map<uint_fast64_t, uint_fast64_t> buildCommandIndexToActionIndex() const; std::unordered_map<uint_fast64_t, std::string> buildActionIndexToActionNameMap() const; - uint_fast64_t numberOfActions() const; - - uint_fast64_t largestActionIndex() const; + uint_fast64_t getNumberOfActions() const; /*! * Converts the PRISM model into an equivalent JANI model. @@ -587,6 +594,11 @@ namespace storm { */ Command synchronizeCommands(uint_fast64_t newCommandIndex, uint_fast64_t actionIndex, uint_fast64_t firstUpdateIndex, std::string const& actionName, std::vector<std::reference_wrapper<Command const>> const& commands) const; + /*! + * Equips all global variables without initial values with initial values based on their type. + */ + void createMissingInitialValues(); + // The manager responsible for the variables/expressions of the program. std::shared_ptr<storm::expressions::ExpressionManager> manager; @@ -633,7 +645,7 @@ namespace storm { std::map<std::string, uint_fast64_t> rewardModelToIndexMap; // The initial construct of the program. - InitialConstruct initialConstruct; + boost::optional<InitialConstruct> initialConstruct; // If set, this specifies the way the modules are composed to obtain the full system. boost::optional<SystemCompositionConstruct> systemCompositionConstruct; @@ -661,14 +673,6 @@ namespace storm { // A mapping from variable names to the modules in which they were declared. std::map<std::string, uint_fast64_t> variableToModuleIndexMap; - - /** - * Takes the current program and replaces all modules. As we reuse the expression manager, we recommend to not use the original program any further. - * @param newModules the modules which replace the old modules. - * @param newConstants the constants which replace the old constants. - * @return A program with the new modules and constants. - */ - Program replaceModulesAndConstantsInProgram(std::vector<Module> const& newModules, std::vector<Constant> const& newConstants); }; std::ostream& operator<<(std::ostream& out, Program::ModelType const& type); diff --git a/src/storage/prism/RewardModel.cpp b/src/storage/prism/RewardModel.cpp index 30c43728c..52ef5dc67 100644 --- a/src/storage/prism/RewardModel.cpp +++ b/src/storage/prism/RewardModel.cpp @@ -81,6 +81,24 @@ namespace storm { return true; } + RewardModel RewardModel::restrictActionRelatedRewards(boost::container::flat_set<uint_fast64_t> const& actionIndicesToKeep) const { + std::vector<StateActionReward> newStateActionRewards; + for (auto const& stateActionReward : this->getStateActionRewards()) { + if (actionIndicesToKeep.find(stateActionReward.getActionIndex()) != actionIndicesToKeep.end()) { + newStateActionRewards.emplace_back(stateActionReward); + } + } + + std::vector<TransitionReward> newTransitionRewards; + for (auto const& transitionReward : this->getTransitionRewards()) { + if (actionIndicesToKeep.find(transitionReward.getActionIndex()) != actionIndicesToKeep.end()) { + newTransitionRewards.emplace_back(transitionReward); + } + } + + return RewardModel(this->getName(), this->getStateRewards(), newStateActionRewards, newTransitionRewards, this->getFilename(), this->getLineNumber()); + } + std::ostream& operator<<(std::ostream& stream, RewardModel const& rewardModel) { stream << "rewards"; if (rewardModel.getName() != "") { diff --git a/src/storage/prism/RewardModel.h b/src/storage/prism/RewardModel.h index c62aff61a..01ab69508 100644 --- a/src/storage/prism/RewardModel.h +++ b/src/storage/prism/RewardModel.h @@ -4,6 +4,7 @@ #include <string> #include <vector> #include <map> +#include <boost/container/flat_set.hpp> #include "src/storage/prism/StateReward.h" #include "src/storage/prism/StateActionReward.h" @@ -108,6 +109,14 @@ namespace storm { */ bool containsVariablesOnlyInRewardValueExpressions(std::set<storm::expressions::Variable> const& undefinedConstantVariables) const; + /*! + * Restricts all action-related rewards of the reward model to the ones with an action index in the provided set. + * + * @param actionIndicesToKeep The set of action indices to keep. + * @return The resulting reward model. + */ + RewardModel restrictActionRelatedRewards(boost::container::flat_set<uint_fast64_t> const& actionIndicesToKeep) const; + friend std::ostream& operator<<(std::ostream& stream, RewardModel const& rewardModel); private: diff --git a/src/storage/prism/ToJaniConverter.cpp b/src/storage/prism/ToJaniConverter.cpp new file mode 100644 index 000000000..33ad88fef --- /dev/null +++ b/src/storage/prism/ToJaniConverter.cpp @@ -0,0 +1,269 @@ +#include "src/storage/prism/ToJaniConverter.h" + +#include "src/storage/expressions/ExpressionManager.h" + +#include "src/storage/prism/Program.h" +#include "src/storage/prism/CompositionToJaniVisitor.h" +#include "src/storage/jani/Model.h" + +#include "src/utility/macros.h" +#include "src/exceptions/NotImplementedException.h" + +namespace storm { + namespace prism { + + storm::jani::Model ToJaniConverter::convert(storm::prism::Program const& program, bool allVariablesGlobal) const { + std::shared_ptr<storm::expressions::ExpressionManager> manager = program.getManager().getSharedPointer(); + + // Start by creating an empty JANI model. + storm::jani::ModelType modelType; + switch (program.getModelType()) { + case Program::ModelType::DTMC: modelType = storm::jani::ModelType::DTMC; + break; + case Program::ModelType::CTMC: modelType = storm::jani::ModelType::CTMC; + break; + case Program::ModelType::MDP: modelType = storm::jani::ModelType::MDP; + break; + case Program::ModelType::CTMDP: modelType = storm::jani::ModelType::CTMDP; + break; + case Program::ModelType::MA: modelType = storm::jani::ModelType::MA; + break; + default: modelType = storm::jani::ModelType::UNDEFINED; + } + storm::jani::Model janiModel("jani_from_prism", modelType, 1, manager); + + // Add all constants of the PRISM program to the JANI model. + for (auto const& constant : program.getConstants()) { + janiModel.addConstant(storm::jani::Constant(constant.getName(), constant.getExpressionVariable(), constant.isDefined() ? boost::optional<storm::expressions::Expression>(constant.getExpression()) : boost::none)); + } + + // Maintain a mapping from expression variables to JANI variables so we can fill in the correct objects when + // creating assignments. + std::map<storm::expressions::Variable, std::reference_wrapper<storm::jani::Variable const>> variableToVariableMap; + + // Add all global variables of the PRISM program to the JANI model. + for (auto const& variable : program.getGlobalIntegerVariables()) { + if (variable.hasInitialValue()) { + storm::jani::BoundedIntegerVariable const& createdVariable = janiModel.addVariable(storm::jani::BoundedIntegerVariable(variable.getName(), variable.getExpressionVariable(), variable.getInitialValueExpression(), false, variable.getLowerBoundExpression(), variable.getUpperBoundExpression())); + variableToVariableMap.emplace(variable.getExpressionVariable(), createdVariable); + } else { + storm::jani::BoundedIntegerVariable const& createdVariable = janiModel.addVariable(storm::jani::BoundedIntegerVariable(variable.getName(), variable.getExpressionVariable(), false, variable.getLowerBoundExpression(), variable.getUpperBoundExpression())); + variableToVariableMap.emplace(variable.getExpressionVariable(), createdVariable); + } + } + for (auto const& variable : program.getGlobalBooleanVariables()) { + if (variable.hasInitialValue()) { + storm::jani::BooleanVariable const& createdVariable = janiModel.addVariable(storm::jani::BooleanVariable(variable.getName(), variable.getExpressionVariable(), variable.getInitialValueExpression(), false)); + variableToVariableMap.emplace(variable.getExpressionVariable(), createdVariable); + } else { + storm::jani::BooleanVariable const& createdVariable = janiModel.addVariable(storm::jani::BooleanVariable(variable.getName(), variable.getExpressionVariable(), false)); + variableToVariableMap.emplace(variable.getExpressionVariable(), createdVariable); + } + } + + // Add all actions of the PRISM program to the JANI model. + for (auto const& action : program.getActions()) { + // Ignore the empty action as every JANI program has predefined tau action. + if (!action.empty()) { + janiModel.addAction(storm::jani::Action(action)); + } + } + + // Because of the rules of JANI, we have to make all variables of modules global that are read by other modules. + + // Create a mapping from variables to the indices of module indices that write/read the variable. + std::map<storm::expressions::Variable, std::set<uint_fast64_t>> variablesToAccessingModuleIndices; + for (uint_fast64_t index = 0; index < program.getNumberOfModules(); ++index) { + storm::prism::Module const& module = program.getModule(index); + + for (auto const& command : module.getCommands()) { + std::set<storm::expressions::Variable> variables = command.getGuardExpression().getVariables(); + for (auto const& variable : variables) { + variablesToAccessingModuleIndices[variable].insert(index); + } + + for (auto const& update : command.getUpdates()) { + for (auto const& assignment : update.getAssignments()) { + variables = assignment.getExpression().getVariables(); + for (auto const& variable : variables) { + variablesToAccessingModuleIndices[variable].insert(index); + } + variablesToAccessingModuleIndices[assignment.getVariable()].insert(index); + } + } + } + } + + // Go through the reward models and construct assignments to the transient variables that are to be added to + // edges and transient assignments that are added to the locations. + std::map<uint_fast64_t, std::vector<storm::jani::Assignment>> transientEdgeAssignments; + std::vector<storm::jani::Assignment> transientLocationAssignments; + for (auto const& rewardModel : program.getRewardModels()) { + auto newExpressionVariable = manager->declareRationalVariable(rewardModel.getName().empty() ? "default" : rewardModel.getName()); + storm::jani::RealVariable const& newTransientVariable = janiModel.addVariable(storm::jani::RealVariable(rewardModel.getName(), newExpressionVariable, true)); + + if (rewardModel.hasStateRewards()) { + storm::expressions::Expression transientLocationExpression; + for (auto const& stateReward : rewardModel.getStateRewards()) { + storm::expressions::Expression rewardTerm = stateReward.getStatePredicateExpression().isTrue() ? stateReward.getRewardValueExpression() : storm::expressions::ite(stateReward.getStatePredicateExpression(), stateReward.getRewardValueExpression(), manager->rational(0)); + if (transientLocationExpression.isInitialized()) { + transientLocationExpression = transientLocationExpression + rewardTerm; + } else { + transientLocationExpression = rewardTerm; + } + } + transientLocationAssignments.emplace_back(newTransientVariable, transientLocationExpression); + } + + std::map<uint_fast64_t, storm::expressions::Expression> actionIndexToExpression; + for (auto const& actionReward : rewardModel.getStateActionRewards()) { + storm::expressions::Expression rewardTerm = actionReward.getStatePredicateExpression().isTrue() ? actionReward.getRewardValueExpression() : storm::expressions::ite(actionReward.getStatePredicateExpression(), actionReward.getRewardValueExpression(), manager->rational(0)); + auto it = actionIndexToExpression.find(janiModel.getActionIndex(actionReward.getActionName())); + if (it != actionIndexToExpression.end()) { + it->second = it->second + rewardTerm; + } else { + actionIndexToExpression[janiModel.getActionIndex(actionReward.getActionName())] = rewardTerm; + } + } + + for (auto const& entry : actionIndexToExpression) { + auto it = transientEdgeAssignments.find(entry.first); + if (it != transientEdgeAssignments.end()) { + it->second.push_back(storm::jani::Assignment(newTransientVariable, entry.second)); + } else { + std::vector<storm::jani::Assignment> assignments = {storm::jani::Assignment(newTransientVariable, entry.second)}; + transientEdgeAssignments.emplace(entry.first, assignments); + } + } + STORM_LOG_THROW(!rewardModel.hasTransitionRewards(), storm::exceptions::NotImplementedException, "Transition reward translation currently not implemented."); + } + STORM_LOG_THROW(transientEdgeAssignments.empty() || transientLocationAssignments.empty() || !program.specifiesSystemComposition(), storm::exceptions::NotImplementedException, "Cannot translate reward models from PRISM to JANI that specify a custom system composition."); + + // Now create the separate JANI automata from the modules of the PRISM program. While doing so, we use the + // previously built mapping to make variables global that are read by more than one module. + bool firstModule = true; + for (auto const& module : program.getModules()) { + // Keep track of the action indices contained in this module. + std::set<uint_fast64_t> actionIndicesOfModule; + + storm::jani::Automaton automaton(module.getName()); + for (auto const& variable : module.getIntegerVariables()) { + storm::jani::BoundedIntegerVariable newIntegerVariable = *storm::jani::makeBoundedIntegerVariable(variable.getName(), variable.getExpressionVariable(), variable.hasInitialValue() ? boost::make_optional(variable.getInitialValueExpression()) : boost::none, false, variable.getLowerBoundExpression(), variable.getUpperBoundExpression()); + std::set<uint_fast64_t> const& accessingModuleIndices = variablesToAccessingModuleIndices[variable.getExpressionVariable()]; + // If there is exactly one module reading and writing the variable, we can make the variable local to this module. + if (!allVariablesGlobal && accessingModuleIndices.size() == 1) { + storm::jani::BoundedIntegerVariable const& createdVariable = automaton.addVariable(newIntegerVariable); + variableToVariableMap.emplace(variable.getExpressionVariable(), createdVariable); + } else if (!accessingModuleIndices.empty()) { + // Otherwise, we need to make it global. + storm::jani::BoundedIntegerVariable const& createdVariable = janiModel.addVariable(newIntegerVariable); + variableToVariableMap.emplace(variable.getExpressionVariable(), createdVariable); + } + } + for (auto const& variable : module.getBooleanVariables()) { + storm::jani::BooleanVariable newBooleanVariable = *storm::jani::makeBooleanVariable(variable.getName(), variable.getExpressionVariable(), variable.hasInitialValue() ? boost::make_optional(variable.getInitialValueExpression()) : boost::none, false); + std::set<uint_fast64_t> const& accessingModuleIndices = variablesToAccessingModuleIndices[variable.getExpressionVariable()]; + // If there is exactly one module reading and writing the variable, we can make the variable local to this module. + if (!allVariablesGlobal && accessingModuleIndices.size() == 1) { + storm::jani::BooleanVariable const& createdVariable = automaton.addVariable(newBooleanVariable); + variableToVariableMap.emplace(variable.getExpressionVariable(), createdVariable); + } else if (!accessingModuleIndices.empty()) { + // Otherwise, we need to make it global. + storm::jani::BooleanVariable const& createdVariable = janiModel.addVariable(newBooleanVariable); + variableToVariableMap.emplace(variable.getExpressionVariable(), createdVariable); + } + } + automaton.setInitialStatesRestriction(manager->boolean(true)); + + // Create a single location that will have all the edges. + uint64_t onlyLocationIndex = automaton.addLocation(storm::jani::Location("l")); + automaton.addInitialLocation(onlyLocationIndex); + + // If we are translating the first module, we need to add the transient assignments to the location. + if (firstModule) { + storm::jani::Location& onlyLocation = automaton.getLocation(onlyLocationIndex); + for (auto const& assignment : transientLocationAssignments) { + onlyLocation.addTransientAssignment(assignment); + } + } + + for (auto const& command : module.getCommands()) { + actionIndicesOfModule.insert(command.getActionIndex()); + + boost::optional<storm::expressions::Expression> rateExpression; + std::vector<storm::jani::EdgeDestination> destinations; + if (program.getModelType() == Program::ModelType::CTMC || program.getModelType() == Program::ModelType::CTMDP) { + for (auto const& update : command.getUpdates()) { + if (rateExpression) { + rateExpression = rateExpression.get() + update.getLikelihoodExpression(); + } else { + rateExpression = update.getLikelihoodExpression(); + } + } + } + + for (auto const& update : command.getUpdates()) { + std::vector<storm::jani::Assignment> assignments; + for (auto const& assignment : update.getAssignments()) { + assignments.push_back(storm::jani::Assignment(variableToVariableMap.at(assignment.getVariable()).get(), assignment.getExpression())); + } + + if (rateExpression) { + destinations.push_back(storm::jani::EdgeDestination(onlyLocationIndex, manager->integer(1) / rateExpression.get(), assignments)); + } else { + destinations.push_back(storm::jani::EdgeDestination(onlyLocationIndex, update.getLikelihoodExpression(), assignments)); + } + } + + // Create the edge object so we can add transient assignments. + storm::jani::Edge newEdge(onlyLocationIndex, janiModel.getActionIndex(command.getActionName()), rateExpression, command.getGuardExpression(), destinations); + + // Then add the transient assignments for the rewards. + auto transientEdgeAssignmentsToAdd = transientEdgeAssignments.find(janiModel.getActionIndex(command.getActionName())); + if (transientEdgeAssignmentsToAdd != transientEdgeAssignments.end()) { + for (auto const& assignment : transientEdgeAssignmentsToAdd->second) { + newEdge.addTransientAssignment(assignment); + } + } + + // Finally add the constructed edge. + automaton.addEdge(newEdge); + } + + // Now remove for all actions of this module the corresponding transient assignments, because we must + // not deal out this reward multiple times. + // NOTE: This only works for the standard composition and not for any custom compositions. This case + // must be checked for earlier. + for (auto actionIndex : actionIndicesOfModule) { + auto it = transientEdgeAssignments.find(actionIndex); + if (it != transientEdgeAssignments.end()) { + transientEdgeAssignments.erase(it); + } + } + + janiModel.addAutomaton(automaton); + firstModule = false; + } + + // Create an initial state restriction if there was an initial construct in the program. + if (program.hasInitialConstruct()) { + janiModel.setInitialStatesRestriction(program.getInitialConstruct().getInitialStatesExpression()); + } else { + janiModel.setInitialStatesRestriction(manager->boolean(true)); + } + + // Set the standard system composition. This is possible, because we reject non-standard compositions anyway. + if (program.specifiesSystemComposition()) { + CompositionToJaniVisitor visitor; + janiModel.setSystemComposition(visitor.toJani(program.getSystemCompositionConstruct().getSystemComposition(), janiModel)); + } else { + janiModel.setSystemComposition(janiModel.getStandardSystemComposition()); + } + + janiModel.finalize(); + + return janiModel; + } + + } +} \ No newline at end of file diff --git a/src/storage/prism/ToJaniConverter.h b/src/storage/prism/ToJaniConverter.h new file mode 100644 index 000000000..bbabef568 --- /dev/null +++ b/src/storage/prism/ToJaniConverter.h @@ -0,0 +1,18 @@ +#pragma once + +namespace storm { + namespace jani { + class Model; + } + + namespace prism { + + class Program; + + class ToJaniConverter { + public: + storm::jani::Model convert(storm::prism::Program const& program, bool allVariablesGlobal = false) const; + }; + + } +} \ No newline at end of file diff --git a/src/storage/prism/Variable.cpp b/src/storage/prism/Variable.cpp index 7d347bcec..cda8317a5 100644 --- a/src/storage/prism/Variable.cpp +++ b/src/storage/prism/Variable.cpp @@ -5,11 +5,11 @@ namespace storm { namespace prism { - Variable::Variable(storm::expressions::Variable const& variable, storm::expressions::Expression const& initialValueExpression, bool defaultInitialValue, std::string const& filename, uint_fast64_t lineNumber) : LocatedInformation(filename, lineNumber), variable(variable), initialValueExpression(initialValueExpression), defaultInitialValue(defaultInitialValue) { + Variable::Variable(storm::expressions::Variable const& variable, storm::expressions::Expression const& initialValueExpression, bool defaultInitialValue, std::string const& filename, uint_fast64_t lineNumber) : LocatedInformation(filename, lineNumber), variable(variable), initialValueExpression(initialValueExpression) { // Nothing to do here. } - Variable::Variable(storm::expressions::ExpressionManager& manager, Variable const& oldVariable, std::string const& newName, std::map<storm::expressions::Variable, storm::expressions::Expression> const& renaming, std::string const& filename, uint_fast64_t lineNumber) : LocatedInformation(filename, lineNumber), variable(manager.declareVariable(newName, oldVariable.variable.getType())), initialValueExpression(oldVariable.getInitialValueExpression().substitute(renaming)), defaultInitialValue(oldVariable.hasDefaultInitialValue()) { + Variable::Variable(storm::expressions::ExpressionManager& manager, Variable const& oldVariable, std::string const& newName, std::map<storm::expressions::Variable, storm::expressions::Expression> const& renaming, std::string const& filename, uint_fast64_t lineNumber) : LocatedInformation(filename, lineNumber), variable(manager.declareVariable(newName, oldVariable.variable.getType())), initialValueExpression(oldVariable.getInitialValueExpression().substitute(renaming)) { // Intentionally left empty. } @@ -17,14 +17,18 @@ namespace storm { return this->variable.getName(); } - bool Variable::hasDefaultInitialValue() const { - return this->defaultInitialValue; + bool Variable::hasInitialValue() const { + return this->initialValueExpression.isInitialized(); } storm::expressions::Expression const& Variable::getInitialValueExpression() const { return this->initialValueExpression; } + void Variable::setInitialValueExpression(storm::expressions::Expression const& initialValueExpression) { + this->initialValueExpression = initialValueExpression; + } + storm::expressions::Variable const& Variable::getExpressionVariable() const { return this->variable; } diff --git a/src/storage/prism/Variable.h b/src/storage/prism/Variable.h index 3a19c2b75..40f47eb27 100644 --- a/src/storage/prism/Variable.h +++ b/src/storage/prism/Variable.h @@ -28,19 +28,27 @@ namespace storm { std::string const& getName() const; /*! - * Retrieves the expression defining the initial value of the variable. + * Retrieves whether the variable has an initial value. + * + * @return True iff the variable has an initial value. + */ + bool hasInitialValue() const; + + /*! + * Retrieves the expression defining the initial value of the variable. This can only be called if there is + * an initial value (expression). * * @return The expression defining the initial value of the variable. */ storm::expressions::Expression const& getInitialValueExpression() const; - + /*! - * Retrieves whether the variable has the default initial value with respect to its type. + * Sets the expression defining the initial value of the variable. * - * @return True iff the variable has the default initial value. + * @param initialValueExpression The expression defining the initial value of the variable. */ - bool hasDefaultInitialValue() const; - + void setInitialValueExpression(storm::expressions::Expression const& initialValueExpression); + /*! * Retrieves the expression variable associated with this variable. * @@ -55,6 +63,10 @@ namespace storm { */ storm::expressions::Expression getExpression() const; + /*! + * Equips the variable with an initial value based on its type if not initial value is present. + */ + virtual void createMissingInitialValue() = 0; // Make the constructors protected to forbid instantiation of this class. protected: @@ -90,9 +102,6 @@ namespace storm { // The constant expression defining the initial value of the variable. storm::expressions::Expression initialValueExpression; - - // A flag that stores whether the variable has its default initial expression. - bool defaultInitialValue; }; } // namespace prism diff --git a/src/storm.cpp b/src/storm.cpp index 0d23f9ecc..236d15a7b 100644 --- a/src/storm.cpp +++ b/src/storm.cpp @@ -6,6 +6,7 @@ #include "src/utility/initialize.h" #include "src/settings/SettingsManager.h" +#include "src/settings/modules/GeneralSettings.h" /*! * Main entry point of the executable storm. @@ -13,7 +14,7 @@ int main(const int argc, const char** argv) { try { - auto starttime = std::chrono::high_resolution_clock::now(); + auto start = std::chrono::high_resolution_clock::now(); storm::utility::setUp(); storm::cli::printHeader("Storm", argc, argv); storm::settings::initializeAll("Storm", "storm"); @@ -27,11 +28,11 @@ int main(const int argc, const char** argv) { // All operations have now been performed, so we clean up everything and terminate. storm::utility::cleanUp(); - auto endtime = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(endtime-starttime); - auto durationSec = std::chrono::duration_cast<std::chrono::seconds>(endtime-starttime); - if(storm::settings::getModule<storm::settings::modules::IOSettings>().isPrintTimingsSet()) { - std::cout << "Overal runtime: " << duration.count() << " ms. ( approx " << durationSec.count() << " seconds)." << std::endl; + auto end = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start); + auto durationSec = std::chrono::duration_cast<std::chrono::seconds>(end - start); + if(storm::settings::getModule<storm::settings::modules::GeneralSettings>().isPrintTimingsSet()) { + std::cout << "Overal runtime: " << duration.count() << " ms. (approximately " << durationSec.count() << " seconds)." << std::endl; } return 0; } catch (storm::exceptions::BaseException const& exception) { diff --git a/src/utility/constants.cpp b/src/utility/constants.cpp index 71d56cd63..6d954722a 100644 --- a/src/utility/constants.cpp +++ b/src/utility/constants.cpp @@ -39,6 +39,23 @@ namespace storm { bool isConstant(ValueType const& a) { return true; } + + template<typename ValueType> + bool isInteger(ValueType const& number) { + ValueType iPart; + ValueType result = std::modf(number, &iPart); + return result = zero<ValueType>(); + } + + template<> + bool isInteger(int const& number) { + return true; + } + + template<> + bool isInteger(uint_fast64_t const& number) { + return true; + } #ifdef STORM_HAVE_CARL template<> @@ -50,7 +67,6 @@ namespace storm { bool isZero(storm::RationalNumber const& a) { return carl::isZero(a); } - template<> bool isOne(storm::RationalFunction const& a) { @@ -93,6 +109,16 @@ namespace storm { // FIXME: this should be treated more properly. return storm::RationalNumber(-1); } + + template<> + bool isInteger(storm::RationalNumber const& number) { + return carl::isInteger(number); + } + + template<> + bool isInteger(storm::RationalFunction const& func) { + return storm::utility::isConstant(func) && storm::utility::isOne(func.denominator()); + } #endif template<typename ValueType> @@ -102,9 +128,9 @@ namespace storm { template<typename ValueType> ValueType simplify(ValueType value) { - // In the general case, we don't do anything here, but merely return the value. If something else is - // supposed to happen here, the templated function can be specialized for this particular type. - return value; + // In the general case, we don't do anything here, but merely return the value. If something else is + // supposed to happen here, the templated function can be specialized for this particular type. + return value; } template<> @@ -151,7 +177,12 @@ namespace storm { double convertNumber(RationalNumber const& number){ return carl::toDouble(number); } - + + template<> + uint_fast64_t convertNumber(RationalNumber const& number){ + return carl::toInt<unsigned long>(number); + } + template<> RationalNumber convertNumber(double const& number){ return carl::rationalize<RationalNumber>(number); @@ -167,15 +198,31 @@ namespace storm { return RationalFunction(carl::rationalize<RationalNumber>(number)); } + template<> + RationalNumber convertNumber(std::string const& number) { + return carl::rationalize<RationalNumber>(number); + } + template<> RationalFunction convertNumber(RationalNumber const& number) { return RationalFunction(number); } - + template<> - storm::RationalNumber abs(storm::RationalNumber const& number) { + uint_fast64_t convertNumber(RationalFunction const& func) { + return carl::toInt<unsigned long>(func.nominatorAsNumber()); + } + + template<> + RationalNumber abs(storm::RationalNumber const& number) { return carl::abs(number); } + + template<> + RationalNumber pow(RationalNumber const& value, uint_fast64_t exponent) { + return carl::pow(value, exponent); + } + #endif template<typename IndexType, typename ValueType> @@ -214,6 +261,7 @@ namespace storm { template storm::storage::MatrixEntry<storm::storage::sparse::state_type, double>&& simplify(storm::storage::MatrixEntry<storm::storage::sparse::state_type, double>&& matrixEntry); template double abs(double const& number); + template bool isInteger(double const& number); template bool isOne(float const& value); template bool isZero(float const& value); @@ -224,6 +272,7 @@ namespace storm { template float infinity(); template float pow(float const& value, uint_fast64_t exponent); + template bool isInteger(float const& number); template float simplify(float value); @@ -240,7 +289,8 @@ namespace storm { template int infinity(); template int pow(int const& value, uint_fast64_t exponent); - + template bool isInteger(int const& number); + template int simplify(int value); template storm::storage::MatrixEntry<storm::storage::sparse::state_type, int> simplify(storm::storage::MatrixEntry<storm::storage::sparse::state_type, int> matrixEntry); @@ -278,12 +328,14 @@ namespace storm { template storm::RationalNumber infinity(); template double convertNumber(storm::RationalNumber const& number); + template uint_fast64_t convertNumber(storm::RationalNumber const& number); template storm::RationalNumber convertNumber(double const& number); template storm::RationalNumber convertNumber(storm::RationalNumber const& number); - + RationalNumber convertNumber(std::string const& number); + template storm::RationalNumber abs(storm::RationalNumber const& number); -// template storm::RationalNumber pow(storm::RationalNumber const& value, uint_fast64_t exponent); + template storm::RationalNumber pow(storm::RationalNumber const& value, uint_fast64_t exponent); template storm::RationalNumber simplify(storm::RationalNumber value); template storm::storage::MatrixEntry<storm::storage::sparse::state_type, storm::RationalNumber> simplify(storm::storage::MatrixEntry<storm::storage::sparse::state_type, storm::RationalNumber> matrixEntry); diff --git a/src/utility/constants.h b/src/utility/constants.h index 4ec4e7539..66edb9f46 100644 --- a/src/utility/constants.h +++ b/src/utility/constants.h @@ -56,6 +56,9 @@ namespace storm { template<typename ValueType> ValueType abs(ValueType const& number); + + template<typename ValueType> + bool isInteger(ValueType const& number); } } diff --git a/src/utility/storm.cpp b/src/utility/storm.cpp index 8ea25af28..c567c05cb 100644 --- a/src/utility/storm.cpp +++ b/src/utility/storm.cpp @@ -10,7 +10,7 @@ namespace storm { storm::prism::Program parseProgram(std::string const& path) { - storm::prism::Program program= storm::parser::PrismParser::parse(path).simplify().simplify(); + storm::prism::Program program = storm::parser::PrismParser::parse(path).simplify().simplify(); program.checkValidity(); return program; } @@ -43,8 +43,23 @@ namespace storm { return parseFormulas(formulaParser, inputString); } - std::vector<std::shared_ptr<storm::logic::Formula const>> parseFormulasForProgram(std::string const& inputString, storm::prism::Program const& program) { + std::vector<std::shared_ptr<storm::logic::Formula const>> substituteConstantsInFormulas(std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, std::map<storm::expressions::Variable, storm::expressions::Expression> const& substitution) { + std::vector<std::shared_ptr<storm::logic::Formula const>> preprocessedFormulas; + for (auto const& formula : formulas) { + preprocessedFormulas.emplace_back(formula->substitute(substitution)); + } + return preprocessedFormulas; + } + + std::vector<std::shared_ptr<storm::logic::Formula const>> parseFormulasForJaniModel(std::string const& inputString, storm::jani::Model const& model) { + storm::parser::FormulaParser formulaParser(model.getManager().getSharedPointer()); + auto formulas = parseFormulas(formulaParser, inputString); + return substituteConstantsInFormulas(formulas, model.getConstantsSubstitution()); + } + + std::vector<std::shared_ptr<storm::logic::Formula const>> parseFormulasForPrismProgram(std::string const& inputString, storm::prism::Program const& program) { storm::parser::FormulaParser formulaParser(program); - return parseFormulas(formulaParser, inputString); + auto formulas = parseFormulas(formulaParser, inputString); + return substituteConstantsInFormulas(formulas, program.getConstantsSubstitution()); } } diff --git a/src/utility/storm.h b/src/utility/storm.h index a8aa4a828..c5df89d64 100644 --- a/src/utility/storm.h +++ b/src/utility/storm.h @@ -48,11 +48,13 @@ // Headers of builders. #include "src/builder/ExplicitModelBuilder.h" #include "src/builder/DdPrismModelBuilder.h" +#include "src/builder/DdJaniModelBuilder.h" // Headers for model processing. #include "src/storage/bisimulation/DeterministicModelBisimulationDecomposition.h" #include "src/storage/bisimulation/NondeterministicModelBisimulationDecomposition.h" #include "src/storage/ModelFormulasPair.h" +#include "src/storage/SymbolicModelDescription.h" // Headers for model checking. #include "src/modelchecker/prctl/SparseDtmcPrctlModelChecker.h" @@ -79,9 +81,9 @@ #include "src/counterexamples/MILPMinimalLabelSetGenerator.h" #include "src/counterexamples/SMTMinimalCommandSetGenerator.h" -// Headers related to PRISM model building. +// Headers related to model building. #include "src/generator/PrismNextStateGenerator.h" -#include "src/utility/prism.h" +#include "src/generator/JaniNextStateGenerator.h" // Headers related to exception handling. #include "src/exceptions/InvalidStateException.h" @@ -101,10 +103,11 @@ namespace storm { std::pair<storm::jani::Model, std::vector<storm::jani::Property>> parseJaniModel(std::string const& path); storm::prism::Program parseProgram(std::string const& path); std::vector<std::shared_ptr<storm::logic::Formula const>> parseFormulasForExplicit(std::string const& inputString); - std::vector<std::shared_ptr<storm::logic::Formula const>> parseFormulasForProgram(std::string const& inputString, storm::prism::Program const& program); + std::vector<std::shared_ptr<storm::logic::Formula const>> parseFormulasForPrismProgram(std::string const& inputString, storm::prism::Program const& program); + std::vector<std::shared_ptr<storm::logic::Formula const>> parseFormulasForJaniModel(std::string const& inputString, storm::jani::Model const& model); template<typename ValueType> - std::shared_ptr<storm::models::sparse::Model<ValueType>> buildSparseModel(storm::prism::Program const& program, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, bool onlyInitialStatesRelevant = false) { + std::shared_ptr<storm::models::sparse::Model<ValueType>> buildSparseModel(storm::storage::SymbolicModelDescription const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, bool onlyInitialStatesRelevant = false) { storm::generator::NextStateGeneratorOptions options(formulas); // Generate command labels if we are going to build a counterexample later. @@ -112,18 +115,34 @@ namespace storm { options.setBuildChoiceLabels(true); } - std::shared_ptr<storm::generator::NextStateGenerator<ValueType, uint32_t>> generator = std::make_shared<storm::generator::PrismNextStateGenerator<ValueType, uint32_t>>(program, options); + std::shared_ptr<storm::generator::NextStateGenerator<ValueType, uint32_t>> generator; + if (model.isPrismProgram()) { + generator = std::make_shared<storm::generator::PrismNextStateGenerator<ValueType, uint32_t>>(model.asPrismProgram(), options); + } else if (model.isJaniModel()) { + generator = std::make_shared<storm::generator::JaniNextStateGenerator<ValueType, uint32_t>>(model.asJaniModel(), options); + } else { + STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Cannot build sparse model from this symbolic model description."); + } storm::builder::ExplicitModelBuilder<ValueType> builder(generator); return builder.build(); } template<typename ValueType, storm::dd::DdType LibraryType = storm::dd::DdType::CUDD> - std::shared_ptr<storm::models::symbolic::Model<LibraryType, ValueType>> buildSymbolicModel(storm::prism::Program const& program, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas) { - typename storm::builder::DdPrismModelBuilder<LibraryType, ValueType>::Options options; - options = typename storm::builder::DdPrismModelBuilder<LibraryType, ValueType>::Options(formulas); - - storm::builder::DdPrismModelBuilder<LibraryType, ValueType> builder; - return builder.build(program, options); + std::shared_ptr<storm::models::symbolic::Model<LibraryType, ValueType>> buildSymbolicModel(storm::storage::SymbolicModelDescription const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas) { + if (model.isPrismProgram()) { + typename storm::builder::DdPrismModelBuilder<LibraryType, ValueType>::Options options; + options = typename storm::builder::DdPrismModelBuilder<LibraryType, ValueType>::Options(formulas); + + storm::builder::DdPrismModelBuilder<LibraryType, ValueType> builder; + return builder.build(model.asPrismProgram(), options); + } else { + STORM_LOG_THROW(model.isJaniModel(), storm::exceptions::InvalidArgumentException, "Cannot build symbolic model for the given symbolic model description."); + typename storm::builder::DdJaniModelBuilder<LibraryType, ValueType>::Options options; + options = typename storm::builder::DdJaniModelBuilder<LibraryType, ValueType>::Options(formulas); + + storm::builder::DdJaniModelBuilder<LibraryType, ValueType> builder; + return builder.build(model.asJaniModel(), options); + } } template<typename ModelType> @@ -205,12 +224,13 @@ namespace storm { } template<typename ValueType> - void generateCounterexample(storm::prism::Program const& program, std::shared_ptr<storm::models::sparse::Model<ValueType>> model, std::shared_ptr<storm::logic::Formula const> const& formula) { + void generateCounterexample(storm::storage::SymbolicModelDescription const& model, std::shared_ptr<storm::models::sparse::Model<ValueType>> markovModel, std::shared_ptr<storm::logic::Formula const> const& formula) { if (storm::settings::getModule<storm::settings::modules::CounterexampleGeneratorSettings>().isMinimalCommandSetGenerationSet()) { - STORM_LOG_THROW(model->getType() == storm::models::ModelType::Mdp, storm::exceptions::InvalidTypeException, "Minimal command set generation is only available for MDPs."); - STORM_LOG_THROW(storm::settings::getModule<storm::settings::modules::IOSettings>().isSymbolicSet(), storm::exceptions::InvalidSettingsException, "Minimal command set generation is only available for symbolic models."); - - std::shared_ptr<storm::models::sparse::Mdp<ValueType>> mdp = model->template as<storm::models::sparse::Mdp<ValueType>>(); + STORM_LOG_THROW(model.isPrismProgram(), storm::exceptions::InvalidTypeException, "Minimal command set generation is only available for PRISM models."); + STORM_LOG_THROW(markovModel->getType() == storm::models::ModelType::Mdp, storm::exceptions::InvalidTypeException, "Minimal command set generation is only available for MDPs."); + storm::prism::Program const& program = model.asPrismProgram(); + + std::shared_ptr<storm::models::sparse::Mdp<ValueType>> mdp = markovModel->template as<storm::models::sparse::Mdp<ValueType>>(); // Determine whether we are required to use the MILP-version or the SAT-version. bool useMILP = storm::settings::getModule<storm::settings::modules::CounterexampleGeneratorSettings>().isUseMilpBasedMinimalCommandSetGenerationSet(); @@ -228,20 +248,20 @@ namespace storm { #ifdef STORM_HAVE_CARL template<> - inline void generateCounterexample(storm::prism::Program const& program, std::shared_ptr<storm::models::sparse::Model<storm::RationalNumber>> model, std::shared_ptr<storm::logic::Formula const> const& formula) { - STORM_LOG_THROW(false, storm::exceptions::InvalidSettingsException, "Unable to generate counterexample for parametric model."); + inline void generateCounterexample(storm::storage::SymbolicModelDescription const& model, std::shared_ptr<storm::models::sparse::Model<storm::RationalNumber>> markovModel, std::shared_ptr<storm::logic::Formula const> const& formula) { + STORM_LOG_THROW(false, storm::exceptions::InvalidSettingsException, "Unable to generate counterexample for exact arithmetic model."); } template<> - inline void generateCounterexample(storm::prism::Program const& program, std::shared_ptr<storm::models::sparse::Model<storm::RationalFunction>> model, std::shared_ptr<storm::logic::Formula const> const& formula) { + inline void generateCounterexample(storm::storage::SymbolicModelDescription const& model, std::shared_ptr<storm::models::sparse::Model<storm::RationalFunction>> markovModel, std::shared_ptr<storm::logic::Formula const> const& formula) { STORM_LOG_THROW(false, storm::exceptions::InvalidSettingsException, "Unable to generate counterexample for parametric model."); } #endif template<typename ValueType> - void generateCounterexamples(storm::prism::Program const& program, std::shared_ptr<storm::models::sparse::Model<ValueType>> model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas) { + void generateCounterexamples(storm::storage::SymbolicModelDescription const& model, std::shared_ptr<storm::models::sparse::Model<ValueType>> markovModel, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas) { for (auto const& formula : formulas) { - generateCounterexample(program, model, formula); + generateCounterexample(model, markovModel, formula); } } @@ -382,10 +402,10 @@ namespace storm { if (model->getType() == storm::models::ModelType::Dtmc) { result = verifySparseDtmc(model->template as<storm::models::sparse::Dtmc<storm::RationalFunction>>(), task); } else if (model->getType() == storm::models::ModelType::Mdp) { - std::shared_ptr<storm::models::sparse::Mdp<storm::RationalFunction>> mdp = model->template as<storm::models::sparse::Mdp<storm::RationalFunction>>(); + //std::shared_ptr<storm::models::sparse::Mdp<storm::RationalFunction>> mdp = model->template as<storm::models::sparse::Mdp<storm::RationalFunction>>(); STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "The parametric engine currently does not support MDPs."); } else if (model->getType() == storm::models::ModelType::Ctmc) { - verifySparseCtmc(model->template as<storm::models::sparse::Ctmc<storm::RationalFunction>>(), task); + result = verifySparseCtmc(model->template as<storm::models::sparse::Ctmc<storm::RationalFunction>>(), task); } else { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "The parametric engine currently does not support " << model->getType()); } @@ -409,7 +429,7 @@ namespace storm { // Program and formula storm::prism::Program program = parseProgram(programFilePath); program.checkValidity(); - std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = parseFormulasForProgram(formulaString, program);; + std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = parseFormulasForPrismProgram(formulaString, program); if(formulas.size()!=1) { STORM_LOG_ERROR("The given formulaString does not specify exactly one formula"); return false; diff --git a/test/functional/builder/DdJaniModelBuilderTest.cpp b/test/functional/builder/DdJaniModelBuilderTest.cpp index 1470e196d..3fc00e217 100644 --- a/test/functional/builder/DdJaniModelBuilderTest.cpp +++ b/test/functional/builder/DdJaniModelBuilderTest.cpp @@ -6,6 +6,7 @@ #include "src/storage/dd/Add.h" #include "src/storage/dd/Bdd.h" +#include "src/storage/SymbolicModelDescription.h" #include "src/models/symbolic/StandardRewardModel.h" #include "src/parser/PrismParser.h" @@ -16,78 +17,70 @@ #include "src/settings/modules/IOSettings.h" TEST(DdJaniModelBuilderTest_Sylvan, Dtmc) { - storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/die.pm"); - storm::jani::Model janiModel = program.toJani(true); + storm::storage::SymbolicModelDescription modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/die.pm"); + storm::jani::Model janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); - storm::builder::DdJaniModelBuilder<storm::dd::DdType::Sylvan, double> builder(janiModel); - std::shared_ptr<storm::models::symbolic::Model<storm::dd::DdType::Sylvan>> model = builder.build(); + storm::builder::DdJaniModelBuilder<storm::dd::DdType::Sylvan, double> builder; + std::shared_ptr<storm::models::symbolic::Model<storm::dd::DdType::Sylvan>> model = builder.build(janiModel); EXPECT_EQ(13ul, model->getNumberOfStates()); EXPECT_EQ(20ul, model->getNumberOfTransitions()); - program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/brp-16-2.pm"); - janiModel = program.toJani(true); - builder = storm::builder::DdJaniModelBuilder<storm::dd::DdType::Sylvan, double>(janiModel); - model = builder.build(); + modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/brp-16-2.pm"); + janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + model = builder.build(janiModel); EXPECT_EQ(677ul, model->getNumberOfStates()); EXPECT_EQ(867ul, model->getNumberOfTransitions()); - program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/crowds-5-5.pm"); - janiModel = program.toJani(true); - builder = storm::builder::DdJaniModelBuilder<storm::dd::DdType::Sylvan, double>(janiModel); - model = builder.build(); + modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/crowds-5-5.pm"); + janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + model = builder.build(janiModel); EXPECT_EQ(8607ul, model->getNumberOfStates()); EXPECT_EQ(15113ul, model->getNumberOfTransitions()); - program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/leader-3-5.pm"); - janiModel = program.toJani(true); - builder = storm::builder::DdJaniModelBuilder<storm::dd::DdType::Sylvan, double>(janiModel); - model = builder.build(); + modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/leader-3-5.pm"); + janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + model = builder.build(janiModel); EXPECT_EQ(273ul, model->getNumberOfStates()); EXPECT_EQ(397ul, model->getNumberOfTransitions()); - program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/nand-5-2.pm"); - janiModel = program.toJani(true); + modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/nand-5-2.pm"); + janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); - builder = storm::builder::DdJaniModelBuilder<storm::dd::DdType::Sylvan, double>(janiModel); - model = builder.build(); + model = builder.build(janiModel); EXPECT_EQ(1728ul, model->getNumberOfStates()); EXPECT_EQ(2505ul, model->getNumberOfTransitions()); } TEST(DdJaniModelBuilderTest_Cudd, Dtmc) { - storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/die.pm"); - storm::jani::Model janiModel = program.toJani(true); + storm::storage::SymbolicModelDescription modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/die.pm"); + storm::jani::Model janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); - storm::builder::DdJaniModelBuilder<storm::dd::DdType::CUDD, double> builder(janiModel); - std::shared_ptr<storm::models::symbolic::Model<storm::dd::DdType::CUDD>> model = builder.build(); + storm::builder::DdJaniModelBuilder<storm::dd::DdType::CUDD, double> builder; + std::shared_ptr<storm::models::symbolic::Model<storm::dd::DdType::CUDD>> model = builder.build(janiModel); EXPECT_EQ(13ul, model->getNumberOfStates()); EXPECT_EQ(20ul, model->getNumberOfTransitions()); - program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/brp-16-2.pm"); - janiModel = program.toJani(true); - builder = storm::builder::DdJaniModelBuilder<storm::dd::DdType::CUDD, double>(janiModel); - model = builder.build(); + modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/brp-16-2.pm"); + janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + model = builder.build(janiModel); EXPECT_EQ(677ul, model->getNumberOfStates()); EXPECT_EQ(867ul, model->getNumberOfTransitions()); - program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/crowds-5-5.pm"); - janiModel = program.toJani(true); - builder = storm::builder::DdJaniModelBuilder<storm::dd::DdType::CUDD, double>(janiModel); - model = builder.build(); + modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/crowds-5-5.pm"); + janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + model = builder.build(janiModel); EXPECT_EQ(8607ul, model->getNumberOfStates()); EXPECT_EQ(15113ul, model->getNumberOfTransitions()); - program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/leader-3-5.pm"); - janiModel = program.toJani(true); - builder = storm::builder::DdJaniModelBuilder<storm::dd::DdType::CUDD, double>(janiModel); - model = builder.build(); + modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/leader-3-5.pm"); + janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + model = builder.build(janiModel); EXPECT_EQ(273ul, model->getNumberOfStates()); EXPECT_EQ(397ul, model->getNumberOfTransitions()); - program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/nand-5-2.pm"); - janiModel = program.toJani(true); - builder = storm::builder::DdJaniModelBuilder<storm::dd::DdType::CUDD, double>(janiModel); - model = builder.build(); + modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/nand-5-2.pm"); + janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + model = builder.build(janiModel); EXPECT_EQ(1728ul, model->getNumberOfStates()); EXPECT_EQ(2505ul, model->getNumberOfTransitions()); } @@ -96,38 +89,34 @@ TEST(DdJaniModelBuilderTest_Sylvan, Ctmc) { // Set the PRISM compatibility mode temporarily. It is set to its old value once the returned object is destructed. std::unique_ptr<storm::settings::SettingMemento> enablePrismCompatibility = storm::settings::mutableIOSettings().overridePrismCompatibilityMode(true); - storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/cluster2.sm"); - storm::jani::Model janiModel = program.toJani(true); - storm::builder::DdJaniModelBuilder<storm::dd::DdType::Sylvan, double> builder(janiModel); - std::shared_ptr<storm::models::symbolic::Model<storm::dd::DdType::Sylvan>> model = builder.build(); + storm::storage::SymbolicModelDescription modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/cluster2.sm"); + storm::jani::Model janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + storm::builder::DdJaniModelBuilder<storm::dd::DdType::Sylvan, double> builder; + std::shared_ptr<storm::models::symbolic::Model<storm::dd::DdType::Sylvan>> model = builder.build(janiModel); EXPECT_EQ(276ul, model->getNumberOfStates()); EXPECT_EQ(1120ul, model->getNumberOfTransitions()); - program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/embedded2.sm"); - janiModel = program.toJani(true); - builder = storm::builder::DdJaniModelBuilder<storm::dd::DdType::Sylvan, double>(janiModel); - model = builder.build(); + modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/embedded2.sm"); + janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + model = builder.build(janiModel); EXPECT_EQ(3478ul, model->getNumberOfStates()); EXPECT_EQ(14639ul, model->getNumberOfTransitions()); - program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/polling2.sm"); - janiModel = program.toJani(true); - builder = storm::builder::DdJaniModelBuilder<storm::dd::DdType::Sylvan, double>(janiModel); - model = builder.build(); + modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/polling2.sm"); + janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + model = builder.build(janiModel); EXPECT_EQ(12ul, model->getNumberOfStates()); EXPECT_EQ(22ul, model->getNumberOfTransitions()); - program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/fms2.sm"); - janiModel = program.toJani(true); - builder = storm::builder::DdJaniModelBuilder<storm::dd::DdType::Sylvan, double>(janiModel); - model = builder.build(); + modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/fms2.sm"); + janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + model = builder.build(janiModel); EXPECT_EQ(810ul, model->getNumberOfStates()); EXPECT_EQ(3699ul, model->getNumberOfTransitions()); - program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/tandem5.sm"); - janiModel = program.toJani(true); - builder = storm::builder::DdJaniModelBuilder<storm::dd::DdType::Sylvan, double>(janiModel); - model = builder.build(); + modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/tandem5.sm"); + janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + model = builder.build(janiModel); EXPECT_EQ(66ul, model->getNumberOfStates()); EXPECT_EQ(189ul, model->getNumberOfTransitions()); } @@ -136,47 +125,43 @@ TEST(DdJaniModelBuilderTest_Cudd, Ctmc) { // Set the PRISM compatibility mode temporarily. It is set to its old value once the returned object is destructed. std::unique_ptr<storm::settings::SettingMemento> enablePrismCompatibility = storm::settings::mutableIOSettings().overridePrismCompatibilityMode(true); - storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/cluster2.sm"); - storm::jani::Model janiModel = program.toJani(true); - storm::builder::DdJaniModelBuilder<storm::dd::DdType::CUDD, double> builder(janiModel); - std::shared_ptr<storm::models::symbolic::Model<storm::dd::DdType::CUDD>> model = builder.build(); + storm::storage::SymbolicModelDescription modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/cluster2.sm"); + storm::jani::Model janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + storm::builder::DdJaniModelBuilder<storm::dd::DdType::CUDD, double> builder; + std::shared_ptr<storm::models::symbolic::Model<storm::dd::DdType::CUDD>> model = builder.build(janiModel); EXPECT_EQ(276ul, model->getNumberOfStates()); EXPECT_EQ(1120ul, model->getNumberOfTransitions()); - program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/embedded2.sm"); - janiModel = program.toJani(true); - builder = storm::builder::DdJaniModelBuilder<storm::dd::DdType::CUDD, double>(janiModel); - model = builder.build(); + modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/embedded2.sm"); + janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + model = builder.build(janiModel); EXPECT_EQ(3478ul, model->getNumberOfStates()); EXPECT_EQ(14639ul, model->getNumberOfTransitions()); - program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/polling2.sm"); - janiModel = program.toJani(true); - builder = storm::builder::DdJaniModelBuilder<storm::dd::DdType::CUDD, double>(janiModel); - model = builder.build(); + modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/polling2.sm"); + janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + model = builder.build(janiModel); EXPECT_EQ(12ul, model->getNumberOfStates()); EXPECT_EQ(22ul, model->getNumberOfTransitions()); - program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/fms2.sm"); - janiModel = program.toJani(true); - builder = storm::builder::DdJaniModelBuilder<storm::dd::DdType::CUDD, double>(janiModel); - model = builder.build(); + modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/fms2.sm"); + janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + model = builder.build(janiModel); EXPECT_EQ(810ul, model->getNumberOfStates()); EXPECT_EQ(3699ul, model->getNumberOfTransitions()); - program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/tandem5.sm"); - janiModel = program.toJani(true); - builder = storm::builder::DdJaniModelBuilder<storm::dd::DdType::CUDD, double>(janiModel); - model = builder.build(); + modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/tandem5.sm"); + janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + model = builder.build(janiModel); EXPECT_EQ(66ul, model->getNumberOfStates()); EXPECT_EQ(189ul, model->getNumberOfTransitions()); } TEST(DdJaniModelBuilderTest_Sylvan, Mdp) { - storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/two_dice.nm"); - storm::jani::Model janiModel = program.toJani(true); - storm::builder::DdJaniModelBuilder<storm::dd::DdType::Sylvan, double> builder(janiModel); - std::shared_ptr<storm::models::symbolic::Model<storm::dd::DdType::Sylvan>> model = builder.build(); + storm::storage::SymbolicModelDescription modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/two_dice.nm"); + storm::jani::Model janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + storm::builder::DdJaniModelBuilder<storm::dd::DdType::Sylvan, double> builder; + std::shared_ptr<storm::models::symbolic::Model<storm::dd::DdType::Sylvan>> model = builder.build(janiModel); EXPECT_TRUE(model->getType() == storm::models::ModelType::Mdp); std::shared_ptr<storm::models::symbolic::Mdp<storm::dd::DdType::Sylvan>> mdp = model->as<storm::models::symbolic::Mdp<storm::dd::DdType::Sylvan>>(); @@ -185,10 +170,9 @@ TEST(DdJaniModelBuilderTest_Sylvan, Mdp) { EXPECT_EQ(436ul, mdp->getNumberOfTransitions()); EXPECT_EQ(254ul, mdp->getNumberOfChoices()); - program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/leader3.nm"); - janiModel = program.toJani(true); - builder = storm::builder::DdJaniModelBuilder<storm::dd::DdType::Sylvan, double>(janiModel); - model = builder.build(); + modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/leader3.nm"); + janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + model = builder.build(janiModel); EXPECT_TRUE(model->getType() == storm::models::ModelType::Mdp); mdp = model->as<storm::models::symbolic::Mdp<storm::dd::DdType::Sylvan>>(); @@ -197,10 +181,9 @@ TEST(DdJaniModelBuilderTest_Sylvan, Mdp) { EXPECT_EQ(654ul, mdp->getNumberOfTransitions()); EXPECT_EQ(573ul, mdp->getNumberOfChoices()); - program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/coin2-2.nm"); - janiModel = program.toJani(true); - builder = storm::builder::DdJaniModelBuilder<storm::dd::DdType::Sylvan, double>(janiModel); - model = builder.build(); + modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/coin2-2.nm"); + janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + model = builder.build(janiModel); EXPECT_TRUE(model->getType() == storm::models::ModelType::Mdp); mdp = model->as<storm::models::symbolic::Mdp<storm::dd::DdType::Sylvan>>(); @@ -209,10 +192,9 @@ TEST(DdJaniModelBuilderTest_Sylvan, Mdp) { EXPECT_EQ(492ul, mdp->getNumberOfTransitions()); EXPECT_EQ(400ul, mdp->getNumberOfChoices()); - program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/csma2-2.nm"); - janiModel = program.toJani(true); - builder = storm::builder::DdJaniModelBuilder<storm::dd::DdType::Sylvan, double>(janiModel); - model = builder.build(); + modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/csma2-2.nm"); + janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + model = builder.build(janiModel); EXPECT_TRUE(model->getType() == storm::models::ModelType::Mdp); mdp = model->as<storm::models::symbolic::Mdp<storm::dd::DdType::Sylvan>>(); @@ -221,10 +203,9 @@ TEST(DdJaniModelBuilderTest_Sylvan, Mdp) { EXPECT_EQ(1282ul, mdp->getNumberOfTransitions()); EXPECT_EQ(1054ul, mdp->getNumberOfChoices()); - program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/firewire3-0.5.nm"); - janiModel = program.toJani(true); - builder = storm::builder::DdJaniModelBuilder<storm::dd::DdType::Sylvan, double>(janiModel); - model = builder.build(); + modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/firewire3-0.5.nm"); + janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + model = builder.build(janiModel); EXPECT_TRUE(model->getType() == storm::models::ModelType::Mdp); mdp = model->as<storm::models::symbolic::Mdp<storm::dd::DdType::Sylvan>>(); @@ -233,10 +214,9 @@ TEST(DdJaniModelBuilderTest_Sylvan, Mdp) { EXPECT_EQ(5585ul, mdp->getNumberOfTransitions()); EXPECT_EQ(5519ul, mdp->getNumberOfChoices()); - program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/wlan0-2-2.nm"); - janiModel = program.toJani(true); - builder = storm::builder::DdJaniModelBuilder<storm::dd::DdType::Sylvan, double>(janiModel); - model = builder.build(); + modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/wlan0-2-2.nm"); + janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + model = builder.build(janiModel); EXPECT_TRUE(model->getType() == storm::models::ModelType::Mdp); mdp = model->as<storm::models::symbolic::Mdp<storm::dd::DdType::Sylvan>>(); @@ -247,10 +227,10 @@ TEST(DdJaniModelBuilderTest_Sylvan, Mdp) { } TEST(DdJaniModelBuilderTest_Cudd, Mdp) { - storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/two_dice.nm"); - storm::jani::Model janiModel = program.toJani(true); - storm::builder::DdJaniModelBuilder<storm::dd::DdType::CUDD, double> builder(janiModel); - std::shared_ptr<storm::models::symbolic::Model<storm::dd::DdType::CUDD>> model = builder.build(); + storm::storage::SymbolicModelDescription modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/two_dice.nm"); + storm::jani::Model janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + storm::builder::DdJaniModelBuilder<storm::dd::DdType::CUDD, double> builder; + std::shared_ptr<storm::models::symbolic::Model<storm::dd::DdType::CUDD>> model = builder.build(janiModel); EXPECT_TRUE(model->getType() == storm::models::ModelType::Mdp); std::shared_ptr<storm::models::symbolic::Mdp<storm::dd::DdType::CUDD>> mdp = model->as<storm::models::symbolic::Mdp<storm::dd::DdType::CUDD>>(); @@ -259,10 +239,9 @@ TEST(DdJaniModelBuilderTest_Cudd, Mdp) { EXPECT_EQ(436ul, mdp->getNumberOfTransitions()); EXPECT_EQ(254ul, mdp->getNumberOfChoices()); - program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/leader3.nm"); - janiModel = program.toJani(true); - builder = storm::builder::DdJaniModelBuilder<storm::dd::DdType::CUDD, double>(janiModel); - model = builder.build(); + modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/leader3.nm"); + janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + model = builder.build(janiModel); EXPECT_TRUE(model->getType() == storm::models::ModelType::Mdp); mdp = model->as<storm::models::symbolic::Mdp<storm::dd::DdType::CUDD>>(); @@ -271,10 +250,9 @@ TEST(DdJaniModelBuilderTest_Cudd, Mdp) { EXPECT_EQ(654ul, mdp->getNumberOfTransitions()); EXPECT_EQ(573ul, mdp->getNumberOfChoices()); - program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/coin2-2.nm"); - janiModel = program.toJani(true); - builder = storm::builder::DdJaniModelBuilder<storm::dd::DdType::CUDD, double>(janiModel); - model = builder.build(); + modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/coin2-2.nm"); + janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + model = builder.build(janiModel); EXPECT_TRUE(model->getType() == storm::models::ModelType::Mdp); mdp = model->as<storm::models::symbolic::Mdp<storm::dd::DdType::CUDD>>(); @@ -283,10 +261,9 @@ TEST(DdJaniModelBuilderTest_Cudd, Mdp) { EXPECT_EQ(492ul, mdp->getNumberOfTransitions()); EXPECT_EQ(400ul, mdp->getNumberOfChoices()); - program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/csma2-2.nm"); - janiModel = program.toJani(true); - builder = storm::builder::DdJaniModelBuilder<storm::dd::DdType::CUDD, double>(janiModel); - model = builder.build(); + modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/csma2-2.nm"); + janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + model = builder.build(janiModel); EXPECT_TRUE(model->getType() == storm::models::ModelType::Mdp); mdp = model->as<storm::models::symbolic::Mdp<storm::dd::DdType::CUDD>>(); @@ -295,10 +272,9 @@ TEST(DdJaniModelBuilderTest_Cudd, Mdp) { EXPECT_EQ(1282ul, mdp->getNumberOfTransitions()); EXPECT_EQ(1054ul, mdp->getNumberOfChoices()); - program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/firewire3-0.5.nm"); - janiModel = program.toJani(true); - builder = storm::builder::DdJaniModelBuilder<storm::dd::DdType::CUDD, double>(janiModel); - model = builder.build(); + modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/firewire3-0.5.nm"); + janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + model = builder.build(janiModel); EXPECT_TRUE(model->getType() == storm::models::ModelType::Mdp); mdp = model->as<storm::models::symbolic::Mdp<storm::dd::DdType::CUDD>>(); @@ -307,10 +283,9 @@ TEST(DdJaniModelBuilderTest_Cudd, Mdp) { EXPECT_EQ(5585ul, mdp->getNumberOfTransitions()); EXPECT_EQ(5519ul, mdp->getNumberOfChoices()); - program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/wlan0-2-2.nm"); - janiModel = program.toJani(true); - builder = storm::builder::DdJaniModelBuilder<storm::dd::DdType::CUDD, double>(janiModel); - model = builder.build(); + modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/wlan0-2-2.nm"); + janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + model = builder.build(janiModel); EXPECT_TRUE(model->getType() == storm::models::ModelType::Mdp); mdp = model->as<storm::models::symbolic::Mdp<storm::dd::DdType::CUDD>>(); @@ -321,8 +296,8 @@ TEST(DdJaniModelBuilderTest_Cudd, Mdp) { } TEST(DdJaniModelBuilderTest_Cudd, IllegalSynchronizingWrites) { - storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/coin2-2-illegalSynchronizingWrite.nm"); - storm::jani::Model janiModel = program.toJani(true); - storm::builder::DdJaniModelBuilder<storm::dd::DdType::CUDD, double> builder(janiModel); - EXPECT_THROW(std::shared_ptr<storm::models::symbolic::Model<storm::dd::DdType::CUDD>> model = builder.build(), storm::exceptions::WrongFormatException); + storm::storage::SymbolicModelDescription modelDescription = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/coin2-2-illegalSynchronizingWrite.nm"); + storm::jani::Model janiModel = modelDescription.toJani(true).preprocess().asJaniModel(); + storm::builder::DdJaniModelBuilder<storm::dd::DdType::CUDD, double> builder; + EXPECT_THROW(std::shared_ptr<storm::models::symbolic::Model<storm::dd::DdType::CUDD>> model = builder.build(janiModel), storm::exceptions::WrongFormatException); } \ No newline at end of file diff --git a/test/functional/modelchecker/SparseDtmcRegionModelCheckerTest.cpp b/test/functional/modelchecker/SparseDtmcRegionModelCheckerTest.cpp index f14808886..aed634c75 100644 --- a/test/functional/modelchecker/SparseDtmcRegionModelCheckerTest.cpp +++ b/test/functional/modelchecker/SparseDtmcRegionModelCheckerTest.cpp @@ -24,7 +24,7 @@ TEST(SparseDtmcRegionModelCheckerTest, Brp_Prob) { // Program and formula storm::prism::Program program = storm::parseProgram(programFile); program = storm::utility::prism::preprocess(program, constantsAsString); - std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::parseFormulasForProgram(formulaAsString, program);; + std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::parseFormulasForPrismProgram(formulaAsString, program);; std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>(); auto const& regionSettings = storm::settings::getModule<storm::settings::modules::RegionSettings>(); storm::modelchecker::region::SparseRegionModelCheckerSettings settings(regionSettings.getSampleMode(), regionSettings.getApproxMode(), regionSettings.getSmtMode()); @@ -95,7 +95,7 @@ TEST(SparseDtmcRegionModelCheckerTest, Brp_Rew) { storm::prism::Program program = storm::parseProgram(programFile); program = storm::utility::prism::preprocess(program, constantsAsString); - std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::parseFormulasForProgram(formulaAsString, program);; + std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::parseFormulasForPrismProgram(formulaAsString, program);; std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>(); auto const& regionSettings = storm::settings::getModule<storm::settings::modules::RegionSettings>(); storm::modelchecker::region::SparseRegionModelCheckerSettings settings(regionSettings.getSampleMode(), regionSettings.getApproxMode(), regionSettings.getSmtMode()); @@ -189,7 +189,7 @@ TEST(SparseDtmcRegionModelCheckerTest, Brp_Rew_Infty) { carl::VariablePool::getInstance().clear(); storm::prism::Program program = storm::parseProgram(programFile); program = storm::utility::prism::preprocess(program, constantsAsString); - std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::parseFormulasForProgram(formulaAsString, program);; + std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::parseFormulasForPrismProgram(formulaAsString, program);; std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>(); auto const& regionSettings = storm::settings::getModule<storm::settings::modules::RegionSettings>(); storm::modelchecker::region::SparseRegionModelCheckerSettings settings(regionSettings.getSampleMode(), regionSettings.getApproxMode(), regionSettings.getSmtMode()); @@ -233,7 +233,7 @@ TEST(SparseDtmcRegionModelCheckerTest, Brp_Rew_4Par) { carl::VariablePool::getInstance().clear(); storm::prism::Program program = storm::parseProgram(programFile); program = storm::utility::prism::preprocess(program, constantsAsString); - std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::parseFormulasForProgram(formulaAsString, program);; + std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::parseFormulasForPrismProgram(formulaAsString, program);; std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>(); auto const& regionSettings = storm::settings::getModule<storm::settings::modules::RegionSettings>(); storm::modelchecker::region::SparseRegionModelCheckerSettings settings(regionSettings.getSampleMode(), regionSettings.getApproxMode(), regionSettings.getSmtMode()); @@ -297,7 +297,7 @@ TEST(SparseDtmcRegionModelCheckerTest, Crowds_Prob) { storm::prism::Program program = storm::parseProgram(programFile); program = storm::utility::prism::preprocess(program, constantsAsString); - std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::parseFormulasForProgram(formulaAsString, program);; + std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::parseFormulasForPrismProgram(formulaAsString, program);; std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>(); auto const& regionSettings = storm::settings::getModule<storm::settings::modules::RegionSettings>(); storm::modelchecker::region::SparseRegionModelCheckerSettings settings(regionSettings.getSampleMode(), regionSettings.getApproxMode(), regionSettings.getSmtMode()); @@ -389,7 +389,7 @@ TEST(SparseDtmcRegionModelCheckerTest, Crowds_Prob_1Par) { storm::prism::Program program = storm::parseProgram(programFile); program = storm::utility::prism::preprocess(program, constantsAsString); - std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::parseFormulasForProgram(formulaAsString, program);; + std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::parseFormulasForPrismProgram(formulaAsString, program);; std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>(); auto const& regionSettings = storm::settings::getModule<storm::settings::modules::RegionSettings>(); storm::modelchecker::region::SparseRegionModelCheckerSettings settings(regionSettings.getSampleMode(), regionSettings.getApproxMode(), regionSettings.getSmtMode()); @@ -455,7 +455,7 @@ TEST(SparseDtmcRegionModelCheckerTest, Crowds_Prob_Const) { storm::prism::Program program = storm::parseProgram(programFile); program = storm::utility::prism::preprocess(program, constantsAsString); - std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::parseFormulasForProgram(formulaAsString, program);; + std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::parseFormulasForPrismProgram(formulaAsString, program);; std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>(); auto const& regionSettings = storm::settings::getModule<storm::settings::modules::RegionSettings>(); storm::modelchecker::region::SparseRegionModelCheckerSettings settings(regionSettings.getSampleMode(), regionSettings.getApproxMode(), regionSettings.getSmtMode()); diff --git a/test/functional/modelchecker/SparseMdpRegionModelCheckerTest.cpp b/test/functional/modelchecker/SparseMdpRegionModelCheckerTest.cpp index 1ceb6c4e6..2b2aa9d2c 100644 --- a/test/functional/modelchecker/SparseMdpRegionModelCheckerTest.cpp +++ b/test/functional/modelchecker/SparseMdpRegionModelCheckerTest.cpp @@ -23,7 +23,7 @@ TEST(SparseMdpRegionModelCheckerTest, two_dice_Prob) { carl::VariablePool::getInstance().clear(); storm::prism::Program program = storm::parseProgram(programFile); - std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::parseFormulasForProgram(formulaFile, program); + std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::parseFormulasForPrismProgram(formulaFile, program); std::shared_ptr<storm::models::sparse::Mdp<storm::RationalFunction>> model = storm::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Mdp<storm::RationalFunction>>(); auto const& regionSettings = storm::settings::getModule<storm::settings::modules::RegionSettings>(); storm::modelchecker::region::SparseRegionModelCheckerSettings settings(regionSettings.getSampleMode(), regionSettings.getApproxMode(), regionSettings.getSmtMode()); @@ -91,7 +91,7 @@ TEST(SparseMdpRegionModelCheckerTest, coin_Prob) { carl::VariablePool::getInstance().clear(); storm::prism::Program program = storm::parseProgram(programFile); - std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::parseFormulasForProgram(formulaAsString, program); + std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::parseFormulasForPrismProgram(formulaAsString, program); std::shared_ptr<storm::models::sparse::Mdp<storm::RationalFunction>> model = storm::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Mdp<storm::RationalFunction>>(); auto const& regionSettings = storm::settings::getModule<storm::settings::modules::RegionSettings>(); storm::modelchecker::region::SparseRegionModelCheckerSettings settings(regionSettings.getSampleMode(), regionSettings.getApproxMode(), regionSettings.getSmtMode()); diff --git a/test/functional/parser/PrismParserTest.cpp b/test/functional/parser/PrismParserTest.cpp index 260c4963a..5cfe7b07e 100644 --- a/test/functional/parser/PrismParserTest.cpp +++ b/test/functional/parser/PrismParserTest.cpp @@ -79,10 +79,6 @@ TEST(PrismParser, ComplexTest) { module mod3 = mod1 [ i = i1, j = j1, k = k1 ] endmodule label "mal" = max(a, 10) > 0; - - init - true - endinit rewards "testrewards" [a] true : a + 7; diff --git a/test/functional/utility/ModelInstantiatorTest.cpp b/test/functional/utility/ModelInstantiatorTest.cpp index 2dd74c66a..d1319613c 100644 --- a/test/functional/utility/ModelInstantiatorTest.cpp +++ b/test/functional/utility/ModelInstantiatorTest.cpp @@ -26,7 +26,7 @@ TEST(ModelInstantiatorTest, BrpProb) { // Program and formula storm::prism::Program program = storm::parseProgram(programFile); program.checkValidity(); - std::vector<std::shared_ptr<storm::logic::Formula const>> formulas = storm::parseFormulasForProgram(formulaAsString, program); + std::vector<std::shared_ptr<storm::logic::Formula const>> formulas = storm::parseFormulasForPrismProgram(formulaAsString, program); ASSERT_TRUE(formulas.size()==1); // Parametric model storm::generator::NextStateGeneratorOptions options(*formulas.front()); @@ -144,7 +144,7 @@ TEST(ModelInstantiatorTest, Brp_Rew) { // Program and formula storm::prism::Program program = storm::parseProgram(programFile); program.checkValidity(); - std::vector<std::shared_ptr<storm::logic::Formula const>> formulas = storm::parseFormulasForProgram(formulaAsString, program); + std::vector<std::shared_ptr<storm::logic::Formula const>> formulas = storm::parseFormulasForPrismProgram(formulaAsString, program); ASSERT_TRUE(formulas.size()==1); // Parametric model storm::generator::NextStateGeneratorOptions options(*formulas.front()); @@ -214,7 +214,7 @@ TEST(ModelInstantiatorTest, Consensus) { // Program and formula storm::prism::Program program = storm::parseProgram(programFile); program.checkValidity(); - std::vector<std::shared_ptr<storm::logic::Formula const>> formulas = storm::parseFormulasForProgram(formulaAsString, program); + std::vector<std::shared_ptr<storm::logic::Formula const>> formulas = storm::parseFormulasForPrismProgram(formulaAsString, program); ASSERT_TRUE(formulas.size()==1); // Parametric model storm::generator::NextStateGeneratorOptions options(*formulas.front()); diff --git a/util/osx-package/package.sh b/util/osx-package/package.sh new file mode 100644 index 000000000..0db5b2b5d --- /dev/null +++ b/util/osx-package/package.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +DYLIBBUNDLER_DIR=/Users/chris/work/macdylibbundler/ + +mkdir -p $1 +mkdir -p $1/bin +mkdir -p $1/lib +cp $2 $1/bin +$DYLIBBUNDLER_DIR/dylibbundler -cd -od -b -p @executable_path/../lib -x $1/bin/storm -d $1/lib +python packager.py --bin storm --dir $1 diff --git a/util/osx-package/packager.py b/util/osx-package/packager.py new file mode 100644 index 000000000..60b26a8b8 --- /dev/null +++ b/util/osx-package/packager.py @@ -0,0 +1,103 @@ +import argparse +import subprocess +import os +from shutil import copyfile + +def get_dependencies(file): + # Call otool -L file to obtain the dependencies. + proc = subprocess.Popen(["otool", "-L", args.binary], stdout=subprocess.PIPE) + result = {} + for line_bytes in proc.stdout: + line = line_bytes.decode("utf-8").strip() + lib = line.split()[0] + if (lib.startswith("@")): + lib = lib.split("/", 1)[1] + (base, file) = os.path.split(lib) + print(base + " // " + file) + + return result + +def create_package(args): + create_package_dirs(args.dir) + copy_binary_to_package_dir(args.bin, args.binary_basename, args.dir) + run_dylibbundler(args.bundler_binary, args.dir, args.binary_basename) + pass + +def parse_arguments(): + parser = argparse.ArgumentParser(description='Package the storm binary on Mac OS.') + parser.add_argument('--bin', dest='bin', help='the binary to package', default='storm') + parser.add_argument('--dir', dest='dir', help='the root directory of the package (will be created if it does not exist)', default='.') + parser.add_argument('--dylibbundler', dest='bundler_binary', help='the binary of the dylibbundler', default='dylibbundler') + args = parser.parse_args() + args.binary_dir = os.path.split(args.bin)[0] + args.binary_basename = os.path.split(args.bin)[1] + return args + +def create_package_dirs(root_dir): + if not os.path.exists(root_dir): + os.makedirs(root_dir) + if not os.path.exists(root_dir + "/bin"): + os.makedirs(root_dir + "/bin") + if not os.path.exists(root_dir + "/lib"): + os.makedirs(root_dir + "/bin") + pass + +def copy_binary_to_package_dir(binary, binary_basename, root_dir): + copyfile(binary, root_dir + "/bin/storm") + pass + +def run_dylibbundler(bundler_binary, root_dir, binary_basename): + command = [bundler_binary, "-cd", "-od", "-b", "-p", "@executable_path/../lib", "-x", root_dir + "/bin/" + binary_basename, "-d", root_dir + "/lib"] + print("executing " + str(command)) + #proc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE) + pass + +def fix_paths(root_dir, binary_basename): + fix_paths_file(root_dir + "/bin/" + binary_basename) + for file in os.listdir(root_dir + "/lib"): + fix_paths_file(root_dir + "/lib/" + file) + pass + pass + +def fix_paths_file(file): + print("fixing paths for " + file) + fixable_deps = get_fixable_deps(file) + for (path, lib) in fixable_deps: + change_fixable_dep(file, path, lib) + +native_libs = ["libc++.1.dylib", "libSystem.B.dylib"] + +def get_fixable_deps(file): + # Call otool -L file to obtain the dependencies. + proc = subprocess.Popen(["otool", "-L", file], stdin=subprocess.PIPE, stdout=subprocess.PIPE) + result = [] + for line_bytes in proc.stdout: + line = line_bytes.decode("utf-8").strip() + lib = line.split()[0] + if lib.startswith("@rpath/"): + result.append(("@rpath", lib.split("/", 1)[1])) + elif lib.startswith("/"): + path_file = os.path.split(lib) + if path_file[1] not in native_libs: + result.append((path_file[0], path_file[1])) + return result + +def change_fixable_dep(file, path, lib): + # Call install_name_tool to change the fixable dependencies + command = ["install_name_tool", "-change", path + "/" + lib, "@executable_path/../lib/" + lib, file] + print("executing " + str(command)) + proc = subprocess.Popen(command, stdout=subprocess.PIPE) + #print ("after call to install_name_tool") + #proc = subprocess.Popen(["otool", "-L", file], stdout=subprocess.PIPE) + #for line_bytes in proc.stdout: + # line = line_bytes.decode("utf-8").strip() + # print(line) + pass + +if __name__ == "__main__": + args = parse_arguments() + + #create_package(args) + fix_paths(args.dir, args.binary_basename) + +