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..2f0dc7190 100644 --- a/src/adapters/AddExpressionAdapter.cpp +++ b/src/adapters/AddExpressionAdapter.cpp @@ -194,8 +194,8 @@ namespace storm { } 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) { + 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..dfb13a315 100644 --- a/src/adapters/AddExpressionAdapter.h +++ b/src/adapters/AddExpressionAdapter.h @@ -31,7 +31,7 @@ namespace storm { 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::RationalLiteralExpression const& expression) override; private: // The manager responsible for the DDs built by this adapter. diff --git a/src/adapters/MathsatExpressionAdapter.h b/src/adapters/MathsatExpressionAdapter.h index 9fec0cf15..77b39cc68 100644 --- a/src/adapters/MathsatExpressionAdapter.h +++ b/src/adapters/MathsatExpressionAdapter.h @@ -171,8 +171,8 @@ namespace storm { 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(expressions::RationalLiteralExpression const& expression) override { + return msat_make_number(env, std::to_string(expression.getValueAsDouble()).c_str()); } virtual boost::any visit(expressions::IntegerLiteralExpression const& expression) override { diff --git a/src/adapters/Z3ExpressionAdapter.cpp b/src/adapters/Z3ExpressionAdapter.cpp index 60b4e8e1c..a80355422 100644 --- a/src/adapters/Z3ExpressionAdapter.cpp +++ b/src/adapters/Z3ExpressionAdapter.cpp @@ -177,6 +177,8 @@ 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 << "."); } @@ -208,7 +210,7 @@ namespace storm { return context.bool_val(expression.getValue()); } - boost::any Z3ExpressionAdapter::visit(storm::expressions::DoubleLiteralExpression const& expression) { + boost::any Z3ExpressionAdapter::visit(storm::expressions::RationalLiteralExpression const& expression) { std::stringstream fractionStream; fractionStream << expression.getValue(); return context.real_val(fractionStream.str().c_str()); diff --git a/src/adapters/Z3ExpressionAdapter.h b/src/adapters/Z3ExpressionAdapter.h index 4fc60c8a5..2735f5713 100644 --- a/src/adapters/Z3ExpressionAdapter.h +++ b/src/adapters/Z3ExpressionAdapter.h @@ -63,7 +63,7 @@ namespace storm { virtual boost::any visit(storm::expressions::BooleanLiteralExpression const& expression) override; - virtual boost::any visit(storm::expressions::DoubleLiteralExpression const& expression) override; + virtual boost::any visit(storm::expressions::RationalLiteralExpression const& expression) override; virtual boost::any visit(storm::expressions::IntegerLiteralExpression const& expression) override; diff --git a/src/builder/DdPrismModelBuilder.cpp b/src/builder/DdPrismModelBuilder.cpp index a397b0de0..6c495e15c 100644 --- a/src/builder/DdPrismModelBuilder.cpp +++ b/src/builder/DdPrismModelBuilder.cpp @@ -1415,7 +1415,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/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..0b2fad41a 100644 --- a/src/generator/JaniNextStateGenerator.cpp +++ b/src/generator/JaniNextStateGenerator.cpp @@ -161,6 +161,9 @@ namespace storm { } // Block the current initial state to search for the next one. + if (!blockingExpression.isInitialized()) { + break; + } solver->add(blockingExpression); } diff --git a/src/generator/PrismNextStateGenerator.cpp b/src/generator/PrismNextStateGenerator.cpp index c0026a98d..6e1aeafdb 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); @@ -136,7 +137,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 +167,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 +318,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 +449,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/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/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/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/storage/expressions/BinaryNumericalFunctionExpression.cpp b/src/storage/expressions/BinaryNumericalFunctionExpression.cpp index 95a326de1..7c4367952 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)); } } 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/ExpressionManager.cpp b/src/storage/expressions/ExpressionManager.cpp index d1add4402..ed51f76ee 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 6fee92fd6..f3ec7e76b 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..1abafc2a6 100644 --- a/src/storage/expressions/ExpressionVisitor.h +++ b/src/storage/expressions/ExpressionVisitor.h @@ -15,7 +15,7 @@ namespace storm { class UnaryNumericalFunctionExpression; class BooleanLiteralExpression; class IntegerLiteralExpression; - class DoubleLiteralExpression; + class RationalLiteralExpression; class ExpressionVisitor { public: @@ -28,7 +28,7 @@ namespace storm { 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(RationalLiteralExpression const& expression) = 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/LinearCoefficientVisitor.cpp b/src/storage/expressions/LinearCoefficientVisitor.cpp index 8d7f39278..dc9a909f9 100644 --- a/src/storage/expressions/LinearCoefficientVisitor.cpp +++ b/src/storage/expressions/LinearCoefficientVisitor.cpp @@ -151,8 +151,8 @@ namespace storm { 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) { + 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..dea1bfc2e 100644 --- a/src/storage/expressions/LinearCoefficientVisitor.h +++ b/src/storage/expressions/LinearCoefficientVisitor.h @@ -75,7 +75,7 @@ namespace storm { 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(RationalLiteralExpression const& expression) override; }; } } diff --git a/src/storage/expressions/LinearityCheckVisitor.cpp b/src/storage/expressions/LinearityCheckVisitor.cpp index 35e1e5c76..7a33a501a 100644 --- a/src/storage/expressions/LinearityCheckVisitor.cpp +++ b/src/storage/expressions/LinearityCheckVisitor.cpp @@ -85,7 +85,7 @@ namespace storm { return LinearityStatus::LinearWithoutVariables; } - boost::any LinearityCheckVisitor::visit(DoubleLiteralExpression const& expression) { + boost::any LinearityCheckVisitor::visit(RationalLiteralExpression const& expression) { return LinearityStatus::LinearWithoutVariables; } } diff --git a/src/storage/expressions/LinearityCheckVisitor.h b/src/storage/expressions/LinearityCheckVisitor.h index 2df8f8084..6bb35b7e8 100644 --- a/src/storage/expressions/LinearityCheckVisitor.h +++ b/src/storage/expressions/LinearityCheckVisitor.h @@ -29,7 +29,7 @@ namespace storm { 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(RationalLiteralExpression const& expression) 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..3fa226a8a --- /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) const { + return visitor.visit(*this); + } + + 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..4eff900b2 --- /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) 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..1490e6f59 100644 --- a/src/storage/expressions/SubstitutionVisitor.cpp +++ b/src/storage/expressions/SubstitutionVisitor.cpp @@ -116,7 +116,7 @@ namespace storm { } template<typename MapType> - boost::any SubstitutionVisitor<MapType>::visit(DoubleLiteralExpression const& expression) { + boost::any SubstitutionVisitor<MapType>::visit(RationalLiteralExpression const& expression) { return expression.getSharedPointer(); } diff --git a/src/storage/expressions/SubstitutionVisitor.h b/src/storage/expressions/SubstitutionVisitor.h index 343521335..5f1a93cb2 100644 --- a/src/storage/expressions/SubstitutionVisitor.h +++ b/src/storage/expressions/SubstitutionVisitor.h @@ -37,7 +37,7 @@ namespace storm { 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(RationalLiteralExpression const& expression) override; private: // A mapping of variables to expressions with which they shall be replaced. diff --git a/src/storage/expressions/ToExprtkStringVisitor.cpp b/src/storage/expressions/ToExprtkStringVisitor.cpp index 51d9c1f34..1492aeae5 100644 --- a/src/storage/expressions/ToExprtkStringVisitor.cpp +++ b/src/storage/expressions/ToExprtkStringVisitor.cpp @@ -212,7 +212,7 @@ namespace storm { return boost::any(); } - boost::any ToExprtkStringVisitor::visit(DoubleLiteralExpression const& expression) { + boost::any ToExprtkStringVisitor::visit(RationalLiteralExpression const& expression) { stream << expression.getValue(); return boost::any(); } diff --git a/src/storage/expressions/ToExprtkStringVisitor.h b/src/storage/expressions/ToExprtkStringVisitor.h index 6c285ff28..d68da589d 100644 --- a/src/storage/expressions/ToExprtkStringVisitor.h +++ b/src/storage/expressions/ToExprtkStringVisitor.h @@ -25,7 +25,7 @@ namespace storm { 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(RationalLiteralExpression const& expression) override; private: std::stringstream stream; diff --git a/src/storage/expressions/ToRationalFunctionVisitor.cpp b/src/storage/expressions/ToRationalFunctionVisitor.cpp index 1a3dd0fc3..a219066e4 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" @@ -33,6 +34,7 @@ namespace storm { 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)); + 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."); } @@ -92,8 +99,8 @@ namespace storm { } 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) { + 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..1cda5028f 100644 --- a/src/storage/expressions/ToRationalFunctionVisitor.h +++ b/src/storage/expressions/ToRationalFunctionVisitor.h @@ -27,7 +27,7 @@ namespace storm { 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(RationalLiteralExpression const& expression) 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..1b7319927 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" @@ -28,26 +29,36 @@ namespace storm { 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)); + RationalNumberType firstOperandAsRationalNumber = boost::any_cast<RationalNumberType>(expression.getFirstOperand()->accept(*this)); + RationalNumberType secondOperandAsRationalNumber = boost::any_cast<RationalNumberType>(expression.getSecondOperand()->accept(*this)); 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(); } @@ -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) { #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..3d53b8a87 100644 --- a/src/storage/expressions/ToRationalNumberVisitor.h +++ b/src/storage/expressions/ToRationalNumberVisitor.h @@ -25,7 +25,7 @@ namespace storm { 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(RationalLiteralExpression const& expression) override; }; } } diff --git a/src/storage/expressions/UnaryNumericalFunctionExpression.cpp b/src/storage/expressions/UnaryNumericalFunctionExpression.cpp index fa7cad7e3..f5178fb6f 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)); } } 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..58173bb76 100644 --- a/src/storage/jani/BoundedIntegerVariable.cpp +++ b/src/storage/jani/BoundedIntegerVariable.cpp @@ -47,10 +47,8 @@ namespace storm { } 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..1c68bd239 100644 --- a/src/storage/jani/BoundedIntegerVariable.h +++ b/src/storage/jani/BoundedIntegerVariable.h @@ -61,7 +61,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/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/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..a836b5879 100644 --- a/src/storage/prism/Program.cpp +++ b/src/storage/prism/Program.cpp @@ -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(); - } - 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(); - } + // Otherwise, we create the missing initial values. + this->createMissingInitialValues(); + for (auto& modules : this->modules) { + modules.createMissingInitialValues(); } - 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. @@ -446,10 +441,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 +657,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 +757,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 +809,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 +856,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 +906,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 +932,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 +979,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 +1156,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 +1218,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 +1249,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 +1512,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 { @@ -1527,7 +1635,7 @@ namespace storm { 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)); @@ -1539,12 +1647,22 @@ namespace storm { // 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); + if (variable.hasInitialValue()) { + storm::jani::BoundedIntegerVariable const& createdVariable = janiModel.addBoundedIntegerVariable(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.addBoundedIntegerVariable(storm::jani::BoundedIntegerVariable(variable.getName(), variable.getExpressionVariable(), false, variable.getLowerBoundExpression(), variable.getUpperBoundExpression())); + variableToVariableMap.emplace(variable.getExpressionVariable(), createdVariable); + } } 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); + if (variable.hasInitialValue()) { + storm::jani::BooleanVariable const& createdVariable = janiModel.addBooleanVariable(storm::jani::BooleanVariable(variable.getName(), variable.getExpressionVariable(), variable.getInitialValueExpression(), false)); + variableToVariableMap.emplace(variable.getExpressionVariable(), createdVariable); + } else { + storm::jani::BooleanVariable const& createdVariable = janiModel.addBooleanVariable(storm::jani::BooleanVariable(variable.getName(), variable.getExpressionVariable(), false)); + variableToVariableMap.emplace(variable.getExpressionVariable(), createdVariable); + } } // Add all actions of the PRISM program to the JANI model. @@ -1556,7 +1674,7 @@ namespace storm { } // 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) { @@ -1584,31 +1702,30 @@ namespace storm { // 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()); + 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& newVariable = automaton.addBoundedIntegerVariable(newIntegerVariable); - variableToVariableMap.emplace(variable.getExpressionVariable(), newVariable); + storm::jani::BoundedIntegerVariable const& createdVariable = automaton.addBoundedIntegerVariable(newIntegerVariable); + variableToVariableMap.emplace(variable.getExpressionVariable(), createdVariable); } else if (!accessingModuleIndices.empty()) { // Otherwise, we need to make it global. - storm::jani::BoundedIntegerVariable const& newVariable = janiModel.addBoundedIntegerVariable(newIntegerVariable); - variableToVariableMap.emplace(variable.getExpressionVariable(), newVariable); + storm::jani::BoundedIntegerVariable const& createdVariable = janiModel.addBoundedIntegerVariable(newIntegerVariable); + variableToVariableMap.emplace(variable.getExpressionVariable(), createdVariable); } } for (auto const& variable : module.getBooleanVariables()) { - storm::jani::BooleanVariable newBooleanVariable(variable.getName(), variable.getExpressionVariable(), variable.getInitialValueExpression()); + 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& newVariable = automaton.addBooleanVariable(newBooleanVariable); - variableToVariableMap.emplace(variable.getExpressionVariable(), newVariable); + storm::jani::BooleanVariable const& createdVariable = automaton.addBooleanVariable(newBooleanVariable); + variableToVariableMap.emplace(variable.getExpressionVariable(), createdVariable); } else if (!accessingModuleIndices.empty()) { // Otherwise, we need to make it global. - storm::jani::BooleanVariable const& newVariable = janiModel.addBooleanVariable(newBooleanVariable); - variableToVariableMap.emplace(variable.getExpressionVariable(), newVariable); + storm::jani::BooleanVariable const& createdVariable = janiModel.addBooleanVariable(newBooleanVariable); + variableToVariableMap.emplace(variable.getExpressionVariable(), createdVariable); } } automaton.setInitialStatesRestriction(manager->boolean(true)); @@ -1647,7 +1764,12 @@ namespace storm { janiModel.addAutomaton(automaton); } - janiModel.setInitialStatesRestriction(manager->boolean(true)); + + if (this->hasInitialConstruct()) { + janiModel.setInitialStatesRestriction(this->getInitialConstruct().getInitialStatesExpression()); + } else { + janiModel.setInitialStatesRestriction(manager->boolean(true)); + } // Set the standard system composition. This is possible, because we reject non-standard compositions anyway. if (this->specifiesSystemComposition()) { @@ -1662,6 +1784,19 @@ namespace storm { return janiModel; } + void Program::createMissingInitialValues() { + for (auto& variable : globalBooleanVariables) { + if (!variable.hasInitialValue()) { + variable.setInitialValueExpression(manager->boolean(false)); + } + } + for (auto& variable : globalIntegerVariables) { + if (!variable.hasInitialValue()) { + variable.setInitialValueExpression(variable.getLowerBoundExpression()); + } + } + } + std::ostream& operator<<(std::ostream& out, Program::ModelType const& type) { switch (type) { case Program::ModelType::UNDEFINED: out << "undefined"; break; @@ -1706,7 +1841,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..41ef5f173 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 @@ -587,6 +606,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 +657,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 +685,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/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..5bc0fdfee 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; } diff --git a/src/utility/storm.h b/src/utility/storm.h index a8aa4a828..faaef184f 100644 --- a/src/utility/storm.h +++ b/src/utility/storm.h @@ -382,10 +382,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()); } 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/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) + +