diff --git a/src/adapters/ExplicitModelAdapter.h b/src/adapters/ExplicitModelAdapter.h index e120f4a59..25804b58a 100644 --- a/src/adapters/ExplicitModelAdapter.h +++ b/src/adapters/ExplicitModelAdapter.h @@ -88,62 +88,6 @@ namespace storm { std::vector<boost::container::flat_set<uint_fast64_t>> choiceLabeling; }; - static std::map<std::string, storm::expressions::Expression> parseConstantDefinitionString(storm::prism::Program const& program, std::string const& constantDefinitionString) { - std::map<std::string, storm::expressions::Expression> constantDefinitions; - std::set<std::string> definedConstants; - - if (!constantDefinitionString.empty()) { - // Parse the string that defines the undefined constants of the model and make sure that it contains exactly - // one value for each undefined constant of the model. - std::vector<std::string> definitions; - boost::split(definitions, constantDefinitionString, boost::is_any_of(",")); - for (auto& definition : definitions) { - boost::trim(definition); - - // Check whether the token could be a legal constant definition. - uint_fast64_t positionOfAssignmentOperator = definition.find('='); - if (positionOfAssignmentOperator == std::string::npos) { - throw storm::exceptions::InvalidArgumentException() << "Illegal constant definition string: syntax error."; - } - - // Now extract the variable name and the value from the string. - std::string constantName = definition.substr(0, positionOfAssignmentOperator); - boost::trim(constantName); - std::string value = definition.substr(positionOfAssignmentOperator + 1); - boost::trim(value); - - // Check whether the constant is a legal undefined constant of the program and if so, of what type it is. - if (program.hasConstant(constantName)) { - // Get the actual constant and check whether it's in fact undefined. - auto const& constant = program.getConstant(constantName); - LOG_THROW(!constant.isDefined(), storm::exceptions::InvalidArgumentException, "Illegally trying to define already defined constant '" << constantName <<"'."); - LOG_THROW(definedConstants.find(constantName) == definedConstants.end(), storm::exceptions::InvalidArgumentException, "Illegally trying to define constant '" << constantName <<"' twice."); - definedConstants.insert(constantName); - - if (constant.getType() == storm::expressions::ExpressionReturnType::Bool) { - if (value == "true") { - constantDefinitions[constantName] = storm::expressions::Expression::createTrue(); - } else if (value == "false") { - constantDefinitions[constantName] = storm::expressions::Expression::createFalse(); - } else { - throw storm::exceptions::InvalidArgumentException() << "Illegal value for boolean constant: " << value << "."; - } - } else if (constant.getType() == storm::expressions::ExpressionReturnType::Int) { - int_fast64_t integerValue = std::stoi(value); - constantDefinitions[constantName] = storm::expressions::Expression::createIntegerLiteral(integerValue); - } else if (constant.getType() == storm::expressions::ExpressionReturnType::Double) { - double doubleValue = std::stod(value); - constantDefinitions[constantName] = storm::expressions::Expression::createDoubleLiteral(doubleValue); - } - } else { - throw storm::exceptions::InvalidArgumentException() << "Illegal constant definition string: unknown undefined constant " << constantName << "."; - } - } - } - - return constantDefinitions; - } - /*! * Convert the program given at construction time to an abstract model. The type of the model is the one * specified in the program. The given reward model name selects the rewards that the model will contain. @@ -159,7 +103,7 @@ namespace storm { static std::unique_ptr<storm::models::AbstractModel<ValueType>> translateProgram(storm::prism::Program program, std::string const& constantDefinitionString = "", std::string const& rewardModelName = "") { // Start by defining the undefined constants in the model. // First, we need to parse the constant definition string. - std::map<std::string, storm::expressions::Expression> constantDefinitions = parseConstantDefinitionString(program, constantDefinitionString); + std::map<std::string, storm::expressions::Expression> constantDefinitions = storm::utility::prism::parseConstantDefinitionString(program, constantDefinitionString); storm::prism::Program preparedProgram = program.defineUndefinedConstants(constantDefinitions); LOG_THROW(!preparedProgram.hasUndefinedConstants(), storm::exceptions::InvalidArgumentException, "Program still contains undefined constants."); diff --git a/src/counterexamples/SMTMinimalCommandSetGenerator.h b/src/counterexamples/SMTMinimalCommandSetGenerator.h index da5d4a7ad..2a33b50eb 100644 --- a/src/counterexamples/SMTMinimalCommandSetGenerator.h +++ b/src/counterexamples/SMTMinimalCommandSetGenerator.h @@ -20,6 +20,8 @@ #include "src/adapters/Z3ExpressionAdapter.h" #endif +#include "src/storage/prism/Program.h" +#include "src/storage/expressions/Expression.h" #include "src/adapters/ExplicitModelAdapter.h" #include "src/modelchecker/prctl/SparseMdpPrctlModelChecker.h" #include "src/solver/GmmxxNondeterministicLinearEquationSolver.h" @@ -125,7 +127,7 @@ namespace storm { for (auto const& entry : transitionMatrix.getRow(row)) { // If there is a relevant successor, we need to add the labels of the current choice. - if (relevancyInformation.relevantStates.get(entry.first) || psiStates.get(entry.first)) { + if (relevancyInformation.relevantStates.get(entry.getColumn()) || psiStates.get(entry.getColumn())) { for (auto const& label : choiceLabeling[row]) { relevancyInformation.relevantLabels.insert(label); } @@ -212,21 +214,21 @@ namespace storm { for (auto const& entry : transitionMatrix.getRow(relevantChoice)) { // If the successor state is neither the state itself nor an irrelevant state, we need to add a variable for the transition. - if (state != entry.first && (relevancyInformation.relevantStates.get(entry.first) || psiStates.get(entry.first))) { + if (state != entry.getColumn() && (relevancyInformation.relevantStates.get(entry.getColumn()) || psiStates.get(entry.getColumn()))) { // Make sure that there is not already one variable for the state pair. This may happen because of several nondeterministic choices // targeting the same state. - if (variableInformation.statePairToIndexMap.find(std::make_pair(state, entry.first)) != variableInformation.statePairToIndexMap.end()) { + if (variableInformation.statePairToIndexMap.find(std::make_pair(state, entry.getColumn())) != variableInformation.statePairToIndexMap.end()) { continue; } // At this point we know that the state-pair does not have an associated variable. - variableInformation.statePairToIndexMap[std::make_pair(state, entry.first)] = variableInformation.statePairVariables.size(); + variableInformation.statePairToIndexMap[std::make_pair(state, entry.getColumn())] = variableInformation.statePairVariables.size(); // Clear contents of the stream to construct new expression name. variableName.clear(); variableName.str(""); - variableName << "t" << state << "_" << entry.first; + variableName << "t" << state << "_" << entry.getColumn(); variableInformation.statePairVariables.push_back(context.bool_const(variableName.str().c_str())); } @@ -258,7 +260,7 @@ namespace storm { * @param solver The solver in which to assert the constraints. * @param variableInformation A structure with information about the variables for the labels. */ - static void assertFuMalikInitialConstraints(storm::ir::Program const& program, storm::models::Mdp<T> const& labeledMdp, storm::storage::BitVector const& psiStates, z3::context& context, z3::solver& solver, VariableInformation const& variableInformation, RelevancyInformation const& relevancyInformation) { + static void assertFuMalikInitialConstraints(storm::prism::Program const& program, storm::models::Mdp<T> const& labeledMdp, storm::storage::BitVector const& psiStates, z3::context& context, z3::solver& solver, VariableInformation const& variableInformation, RelevancyInformation const& relevancyInformation) { // Assert that at least one of the labels must be taken. z3::expr formula = variableInformation.labelVariables.at(0); for (uint_fast64_t index = 1; index < variableInformation.labelVariables.size(); ++index) { @@ -316,11 +318,11 @@ namespace storm { // Iterate over successors and add relevant choices of relevant successors to the following label set. bool canReachTargetState = false; for (auto const& entry : transitionMatrix.getRow(currentChoice)) { - if (relevancyInformation.relevantStates.get(entry.first)) { - for (auto relevantChoice : relevancyInformation.relevantChoicesForRelevantStates.at(entry.first)) { + if (relevancyInformation.relevantStates.get(entry.getColumn())) { + for (auto relevantChoice : relevancyInformation.relevantChoicesForRelevantStates.at(entry.getColumn())) { followingLabels[choiceLabeling[currentChoice]].insert(choiceLabeling[currentChoice]); } - } else if (psiStates.get(entry.first)) { + } else if (psiStates.get(entry.getColumn())) { canReachTargetState = true; } } @@ -335,11 +337,11 @@ namespace storm { // Iterate over predecessors and add all choices that target the current state to the preceding // label set of all labels of all relevant choices of the current state. for (auto const& predecessorEntry : backwardTransitions.getRow(currentState)) { - if (relevancyInformation.relevantStates.get(predecessorEntry.first)) { - for (auto predecessorChoice : relevancyInformation.relevantChoicesForRelevantStates.at(predecessorEntry.first)) { + if (relevancyInformation.relevantStates.get(predecessorEntry.getColumn())) { + for (auto predecessorChoice : relevancyInformation.relevantChoicesForRelevantStates.at(predecessorEntry.getColumn())) { bool choiceTargetsCurrentState = false; for (auto const& successorEntry : transitionMatrix.getRow(predecessorChoice)) { - if (successorEntry.first == currentState) { + if (successorEntry.getColumn() == currentState) { choiceTargetsCurrentState = true; } } @@ -554,8 +556,7 @@ namespace storm { * @param context The Z3 context in which to build the expressions. * @param solver The solver to use for the satisfiability evaluation. */ - static void assertSymbolicCuts(storm::ir::Program const& program, storm::models::Mdp<T> const& labeledMdp, VariableInformation const& variableInformation, RelevancyInformation const& relevancyInformation, z3::context& context, z3::solver& solver) { - + static void assertSymbolicCuts(storm::prism::Program const& program, storm::models::Mdp<T> const& labeledMdp, VariableInformation const& variableInformation, RelevancyInformation const& relevancyInformation, z3::context& context, z3::solver& solver) { // A container storing the label sets that may precede a given label set. std::map<boost::container::flat_set<uint_fast64_t>, std::set<boost::container::flat_set<uint_fast64_t>>> precedingLabelSets; @@ -581,11 +582,11 @@ namespace storm { // Iterate over predecessors and add all choices that target the current state to the preceding // label set of all labels of all relevant choices of the current state. for (auto const& predecessorEntry : backwardTransitions.getRow(currentState)) { - if (relevancyInformation.relevantStates.get(predecessorEntry.first)) { - for (auto predecessorChoice : relevancyInformation.relevantChoicesForRelevantStates.at(predecessorEntry.first)) { + if (relevancyInformation.relevantStates.get(predecessorEntry.getColumn())) { + for (auto predecessorChoice : relevancyInformation.relevantChoicesForRelevantStates.at(predecessorEntry.getColumn())) { bool choiceTargetsCurrentState = false; for (auto const& successorEntry : transitionMatrix.getRow(predecessorChoice)) { - if (successorEntry.first == currentState) { + if (successorEntry.getColumn() == currentState) { choiceTargetsCurrentState = true; } } @@ -599,45 +600,50 @@ namespace storm { } } - storm::utility::ir::VariableInformation programVariableInformation = storm::utility::ir::createVariableInformation(program); - // Create a context and register all variables of the program with their correct type. z3::context localContext; + z3::solver localSolver(localContext); std::map<std::string, z3::expr> solverVariables; - for (auto const& booleanVariable : programVariableInformation.booleanVariables) { + for (auto const& booleanVariable : program.getGlobalBooleanVariables()) { solverVariables.emplace(booleanVariable.getName(), localContext.bool_const(booleanVariable.getName().c_str())); } - for (auto const& integerVariable : programVariableInformation.integerVariables) { + for (auto const& integerVariable : program.getGlobalIntegerVariables()) { solverVariables.emplace(integerVariable.getName(), localContext.int_const(integerVariable.getName().c_str())); } - // Now create a corresponding local solver and assert all range bounds for the integer variables. - z3::solver localSolver(localContext); + for (auto const& module : program.getModules()) { + for (auto const& booleanVariable : module.getBooleanVariables()) { + solverVariables.emplace(booleanVariable.getName(), localContext.bool_const(booleanVariable.getName().c_str())); + } + for (auto const& integerVariable : module.getIntegerVariables()) { + solverVariables.emplace(integerVariable.getName(), localContext.int_const(integerVariable.getName().c_str())); + } + } + storm::adapters::Z3ExpressionAdapter expressionAdapter(localContext, solverVariables); - for (auto const& integerVariable : programVariableInformation.integerVariables) { - z3::expr lowerBound = expressionAdapter.translateExpression(integerVariable.getLowerBound()); + + // Then add the constraints for bounds of the integer variables.. + for (auto const& integerVariable : program.getGlobalIntegerVariables()) { + z3::expr lowerBound = expressionAdapter.translateExpression(integerVariable.getLowerBoundExpression()); lowerBound = solverVariables.at(integerVariable.getName()) >= lowerBound; localSolver.add(lowerBound); - - z3::expr upperBound = expressionAdapter.translateExpression(integerVariable.getUpperBound()); + z3::expr upperBound = expressionAdapter.translateExpression(integerVariable.getUpperBoundExpression()); upperBound = solverVariables.at(integerVariable.getName()) <= upperBound; localSolver.add(upperBound); } - - // Construct an expression that exactly characterizes the initial state. - std::unique_ptr<storm::utility::ir::StateType> initialState(storm::utility::ir::getInitialState(program, programVariableInformation)); - z3::expr initialStateExpression = localContext.bool_val(true); - for (uint_fast64_t index = 0; index < programVariableInformation.booleanVariables.size(); ++index) { - if (std::get<0>(*initialState).at(programVariableInformation.booleanVariableToIndexMap.at(programVariableInformation.booleanVariables[index].getName()))) { - initialStateExpression = initialStateExpression && solverVariables.at(programVariableInformation.booleanVariables[index].getName()); - } else { - initialStateExpression = initialStateExpression && !solverVariables.at(programVariableInformation.booleanVariables[index].getName()); + for (auto const& module : program.getModules()) { + for (auto const& integerVariable : module.getIntegerVariables()) { + z3::expr lowerBound = expressionAdapter.translateExpression(integerVariable.getLowerBoundExpression()); + lowerBound = solverVariables.at(integerVariable.getName()) >= lowerBound; + localSolver.add(lowerBound); + z3::expr upperBound = expressionAdapter.translateExpression(integerVariable.getUpperBoundExpression()); + upperBound = solverVariables.at(integerVariable.getName()) <= upperBound; + localSolver.add(upperBound); } } - for (uint_fast64_t index = 0; index < programVariableInformation.integerVariables.size(); ++index) { - storm::ir::IntegerVariable const& variable = programVariableInformation.integerVariables[index]; - initialStateExpression = initialStateExpression && (solverVariables.at(variable.getName()) == localContext.int_val(static_cast<int>(std::get<1>(*initialState).at(programVariableInformation.integerVariableToIndexMap.at(variable.getName()))))); - } + + // Construct an expression that exactly characterizes the initial state. + storm::expressions::Expression initialStateExpression = program.getInitialConstruct().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; @@ -646,12 +652,12 @@ namespace storm { for (auto const& labelSetAndPrecedingLabelSetsPair : precedingLabelSets) { // Find out the commands for the currently considered label set. - std::vector<std::reference_wrapper<storm::ir::Command const>> currentCommandVector; + std::vector<std::reference_wrapper<storm::prism::Command const>> currentCommandVector; for (uint_fast64_t moduleIndex = 0; moduleIndex < program.getNumberOfModules(); ++moduleIndex) { - storm::ir::Module const& module = program.getModule(moduleIndex); + storm::prism::Module const& module = program.getModule(moduleIndex); for (uint_fast64_t commandIndex = 0; commandIndex < module.getNumberOfCommands(); ++commandIndex) { - storm::ir::Command const& command = module.getCommand(commandIndex); + storm::prism::Command const& command = module.getCommand(commandIndex); // If the current command is one of the commands we need to consider, store a reference to it in the container. if (labelSetAndPrecedingLabelSetsPair.first.find(command.getGlobalIndex()) != labelSetAndPrecedingLabelSetsPair.first.end()) { @@ -665,9 +671,9 @@ namespace storm { // Check if the command set is enabled in the initial state. for (auto const& command : currentCommandVector) { - localSolver.add(expressionAdapter.translateExpression(command.get().getGuard())); + localSolver.add(expressionAdapter.translateExpression(command.get().getGuardExpression())); } - localSolver.add(initialStateExpression); + localSolver.add(expressionAdapter.translateExpression(initialStateExpression)); z3::check_result checkResult = localSolver.check(); localSolver.pop(); @@ -676,19 +682,19 @@ namespace storm { // If the solver reports unsat, then we know that the current selection is not enabled in the initial state. if (checkResult == z3::unsat) { LOG4CPLUS_DEBUG(logger, "Selection not enabled in initial state."); - std::unique_ptr<storm::ir::expressions::BaseExpression> guardConjunction; + storm::expressions::Expression guardConjunction; if (currentCommandVector.size() == 1) { - guardConjunction = currentCommandVector.begin()->get().getGuard()->clone(); + guardConjunction = currentCommandVector.begin()->get().getGuardExpression(); } else if (currentCommandVector.size() > 1) { - std::vector<std::reference_wrapper<storm::ir::Command const>>::const_iterator setIterator = currentCommandVector.begin(); - std::unique_ptr<storm::ir::expressions::BaseExpression> first = setIterator->get().getGuard()->clone(); + std::vector<std::reference_wrapper<storm::prism::Command const>>::const_iterator setIterator = currentCommandVector.begin(); + storm::expressions::Expression first = setIterator->get().getGuardExpression(); ++setIterator; - std::unique_ptr<storm::ir::expressions::BaseExpression> second = setIterator->get().getGuard()->clone(); - guardConjunction = std::unique_ptr<storm::ir::expressions::BaseExpression>(new storm::ir::expressions::BinaryBooleanFunctionExpression(std::move(first), std::move(second), storm::ir::expressions::BinaryBooleanFunctionExpression::AND)); + storm::expressions::Expression second = setIterator->get().getGuardExpression(); + guardConjunction = first && second; ++setIterator; while (setIterator != currentCommandVector.end()) { - guardConjunction = std::unique_ptr<storm::ir::expressions::BaseExpression>(new storm::ir::expressions::BinaryBooleanFunctionExpression(std::move(guardConjunction), setIterator->get().getGuard()->clone(), storm::ir::expressions::BinaryBooleanFunctionExpression::AND)); + guardConjunction = guardConjunction && setIterator->get().getGuardExpression(); ++setIterator; } } else { @@ -701,9 +707,9 @@ namespace storm { bool firstAssignment = true; for (auto const& command : currentCommandVector) { if (firstAssignment) { - guardExpression = !expressionAdapter.translateExpression(command.get().getGuard()); + guardExpression = !expressionAdapter.translateExpression(command.get().getGuardExpression()); } else { - guardExpression = guardExpression | !expressionAdapter.translateExpression(command.get().getGuard()); + guardExpression = guardExpression | !expressionAdapter.translateExpression(command.get().getGuardExpression()); } } localSolver.add(guardExpression); @@ -715,12 +721,12 @@ namespace storm { localSolver.push(); // Find out the commands for the currently considered preceding label set. - std::vector<std::reference_wrapper<storm::ir::Command const>> currentPrecedingCommandVector; + std::vector<std::reference_wrapper<storm::prism::Command const>> currentPrecedingCommandVector; for (uint_fast64_t moduleIndex = 0; moduleIndex < program.getNumberOfModules(); ++moduleIndex) { - storm::ir::Module const& module = program.getModule(moduleIndex); + storm::prism::Module const& module = program.getModule(moduleIndex); for (uint_fast64_t commandIndex = 0; commandIndex < module.getNumberOfCommands(); ++commandIndex) { - storm::ir::Command const& command = module.getCommand(commandIndex); + storm::prism::Command const& command = module.getCommand(commandIndex); // If the current command is one of the commands we need to consider, store a reference to it in the container. if (precedingLabelSet.find(command.getGlobalIndex()) != precedingLabelSet.end()) { @@ -731,10 +737,10 @@ namespace storm { // Assert all the guards of the preceding command set. for (auto const& command : currentPrecedingCommandVector) { - localSolver.add(expressionAdapter.translateExpression(command.get().getGuard())); + localSolver.add(expressionAdapter.translateExpression(command.get().getGuardExpression())); } - std::vector<std::vector<storm::ir::Update>::const_iterator> iteratorVector; + std::vector<std::vector<storm::prism::Update>::const_iterator> iteratorVector; for (auto const& command : currentPrecedingCommandVector) { iteratorVector.push_back(command.get().getUpdates().begin()); } @@ -743,13 +749,15 @@ namespace storm { std::vector<z3::expr> formulae; bool done = false; while (!done) { - std::vector<std::reference_wrapper<storm::ir::Update const>> currentUpdateCombination; + std::map<std::string, storm::expressions::Expression> currentUpdateCombinationMap; for (auto const& updateIterator : iteratorVector) { - currentUpdateCombination.push_back(*updateIterator); + for (auto const& assignment : updateIterator->getAssignments()) { + currentUpdateCombinationMap.emplace(assignment.getVariableName(), assignment.getExpression()); + } } LOG4CPLUS_DEBUG(logger, "About to assert a weakest precondition."); - std::unique_ptr<storm::ir::expressions::BaseExpression> wp = storm::utility::ir::getWeakestPrecondition(guardConjunction->clone(), currentUpdateCombination); + storm::expressions::Expression wp = guardConjunction.substitute(currentUpdateCombinationMap); formulae.push_back(expressionAdapter.translateExpression(wp)); LOG4CPLUS_DEBUG(logger, "Asserted weakest precondition."); @@ -913,16 +921,16 @@ namespace storm { // Assert the constraints (1). boost::container::flat_set<uint_fast64_t> relevantPredecessors; for (auto const& predecessorEntry : backwardTransitions.getRow(relevantState)) { - if (relevantState != predecessorEntry.first && relevancyInformation.relevantStates.get(predecessorEntry.first)) { - relevantPredecessors.insert(predecessorEntry.first); + if (relevantState != predecessorEntry.getColumn() && relevancyInformation.relevantStates.get(predecessorEntry.getColumn())) { + relevantPredecessors.insert(predecessorEntry.getColumn()); } } boost::container::flat_set<uint_fast64_t> relevantSuccessors; for (auto const& relevantChoice : relevancyInformation.relevantChoicesForRelevantStates.at(relevantState)) { for (auto const& successorEntry : transitionMatrix.getRow(relevantChoice)) { - if (relevantState != successorEntry.first && (relevancyInformation.relevantStates.get(successorEntry.first) || psiStates.get(successorEntry.first))) { - relevantSuccessors.insert(successorEntry.first); + if (relevantState != successorEntry.getColumn() && (relevancyInformation.relevantStates.get(successorEntry.getColumn()) || psiStates.get(successorEntry.getColumn()))) { + relevantSuccessors.insert(successorEntry.getColumn()); } } } @@ -941,8 +949,8 @@ namespace storm { boost::container::flat_set<uint_fast64_t> relevantSuccessors; for (auto const& relevantChoice : relevancyInformation.relevantChoicesForRelevantStates.at(relevantState)) { for (auto const& successorEntry : transitionMatrix.getRow(relevantChoice)) { - if (relevantState != successorEntry.first && (relevancyInformation.relevantStates.get(successorEntry.first) || psiStates.get(successorEntry.first))) { - relevantSuccessors.insert(successorEntry.first); + if (relevantState != successorEntry.getColumn() && (relevancyInformation.relevantStates.get(successorEntry.getColumn()) || psiStates.get(successorEntry.getColumn()))) { + relevantSuccessors.insert(successorEntry.getColumn()); } } } @@ -965,7 +973,7 @@ namespace storm { boost::container::flat_set<uint_fast64_t> choicesForStatePair; for (auto const& relevantChoice : relevancyInformation.relevantChoicesForRelevantStates.at(sourceState)) { for (auto const& successorEntry : transitionMatrix.getRow(relevantChoice)) { - if (successorEntry.first == targetState) { + if (successorEntry.getColumn() == targetState) { choicesForStatePair.insert(relevantChoice); } } @@ -1400,13 +1408,13 @@ namespace storm { bool choiceTargetsRelevantState = false; for (auto const& successorEntry : transitionMatrix.getRow(currentChoice)) { - if (relevancyInformation.relevantStates.get(successorEntry.first) && currentState != successorEntry.first) { + if (relevancyInformation.relevantStates.get(successorEntry.getColumn()) && currentState != successorEntry.getColumn()) { choiceTargetsRelevantState = true; - if (!reachableStates.get(successorEntry.first)) { - reachableStates.set(successorEntry.first); - stack.push_back(successorEntry.first); + if (!reachableStates.get(successorEntry.getColumn())) { + reachableStates.set(successorEntry.getColumn()); + stack.push_back(successorEntry.getColumn()); } - } else if (psiStates.get(successorEntry.first)) { + } else if (psiStates.get(successorEntry.getColumn())) { targetStateIsReachable = true; } } @@ -1446,7 +1454,7 @@ namespace storm { // Determine whether the state has the option to leave the reachable state space and go to the unreachable relevant states. for (auto const& successorEntry : originalMdp.getTransitionMatrix().getRow(currentChoice)) { - if (unreachableRelevantStates.get(successorEntry.first)) { + if (unreachableRelevantStates.get(successorEntry.getColumn())) { isBorderChoice = true; } } @@ -1529,13 +1537,13 @@ namespace storm { bool choiceTargetsRelevantState = false; for (auto const& successorEntry : transitionMatrix.getRow(currentChoice)) { - if (relevancyInformation.relevantStates.get(successorEntry.first) && currentState != successorEntry.first) { + if (relevancyInformation.relevantStates.get(successorEntry.getColumn()) && currentState != successorEntry.getColumn()) { choiceTargetsRelevantState = true; - if (!reachableStates.get(successorEntry.first)) { - reachableStates.set(successorEntry.first, true); - stack.push_back(successorEntry.first); + if (!reachableStates.get(successorEntry.getColumn())) { + reachableStates.set(successorEntry.getColumn(), true); + stack.push_back(successorEntry.getColumn()); } - } else if (psiStates.get(successorEntry.first)) { + } else if (psiStates.get(successorEntry.getColumn())) { targetStateIsReachable = true; } } @@ -1613,7 +1621,7 @@ namespace storm { * @param checkThresholdFeasible If set, it is verified that the model can actually achieve/exceed the given probability value. If this check * is made and fails, an exception is thrown. */ - static boost::container::flat_set<uint_fast64_t> getMinimalCommandSet(storm::ir::Program program, std::string const& constantDefinitionString, storm::models::Mdp<T> const& labeledMdp, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, double probabilityThreshold, bool strictBound, bool checkThresholdFeasible = false, bool includeReachabilityEncoding = false) { + static boost::container::flat_set<uint_fast64_t> getMinimalCommandSet(storm::prism::Program program, std::string const& constantDefinitionString, storm::models::Mdp<T> const& labeledMdp, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, double probabilityThreshold, bool strictBound, bool checkThresholdFeasible = false, bool includeReachabilityEncoding = false) { #ifdef STORM_HAVE_Z3 // Set up all clocks used for time measurement. auto totalClock = std::chrono::high_resolution_clock::now(); @@ -1632,7 +1640,9 @@ namespace storm { auto analysisClock = std::chrono::high_resolution_clock::now(); decltype(std::chrono::high_resolution_clock::now() - analysisClock) totalAnalysisTime(0); - storm::utility::ir::defineUndefinedConstants(program, constantDefinitionString); + std::map<std::string, storm::expressions::Expression> constantDefinitions = storm::utility::prism::parseConstantDefinitionString(program, constantDefinitionString); + storm::prism::Program preparedProgram = program.defineUndefinedConstants(constantDefinitions); + preparedProgram = preparedProgram.substituteConstants(); // (0) Check whether the MDP is indeed labeled. if (!labeledMdp.hasChoiceLabeling()) { @@ -1676,7 +1686,7 @@ namespace storm { LOG4CPLUS_DEBUG(logger, "Asserting cuts."); assertExplicitCuts(labeledMdp, psiStates, variableInformation, relevancyInformation, context, solver); LOG4CPLUS_DEBUG(logger, "Asserted explicit cuts."); - assertSymbolicCuts(program, labeledMdp, variableInformation, relevancyInformation, context, solver); + assertSymbolicCuts(preparedProgram, labeledMdp, variableInformation, relevancyInformation, context, solver); LOG4CPLUS_DEBUG(logger, "Asserted symbolic cuts."); if (includeReachabilityEncoding) { assertReachabilityCuts(labeledMdp, psiStates, variableInformation, relevancyInformation, context, solver); @@ -1764,8 +1774,6 @@ namespace storm { std::cout << " * number of models that could not reach a target state: " << zeroProbabilityCount << " (" << 100 * static_cast<double>(zeroProbabilityCount)/iterations << "%)" << std::endl << std::endl; } - // (9) Return the resulting command set after undefining the constants. - storm::utility::ir::undefineUndefinedConstants(program); return commandSet; #else @@ -1773,7 +1781,7 @@ namespace storm { #endif } - static void computeCounterexample(storm::ir::Program program, std::string const& constantDefinitionString, storm::models::Mdp<T> const& labeledMdp, storm::property::prctl::AbstractPrctlFormula<double> const* formulaPtr) { + static void computeCounterexample(storm::prism::Program program, std::string const& constantDefinitionString, storm::models::Mdp<T> const& labeledMdp, storm::property::prctl::AbstractPrctlFormula<double> const* formulaPtr) { #ifdef STORM_HAVE_Z3 std::cout << std::endl << "Generating minimal label counterexample for formula " << formulaPtr->toString() << std::endl; // First, we need to check whether the current formula is an Until-Formula. @@ -1821,9 +1829,8 @@ namespace storm { std::cout << std::endl << "Computed minimal label set of size " << labelSet.size() << " in " << std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count() << "ms." << std::endl; std::cout << "Resulting program:" << std::endl << std::endl; - storm::ir::Program restrictedProgram(program); - restrictedProgram.restrictCommands(labelSet); - std::cout << restrictedProgram.toString() << std::endl; + storm::prism::Program restrictedProgram = program.restrictCommands(labelSet); + std::cout << restrictedProgram << std::endl; std::cout << std::endl << "-------------------------------------------" << std::endl; #else diff --git a/src/models/Mdp.h b/src/models/Mdp.h index 61858fefa..de5e3d78b 100644 --- a/src/models/Mdp.h +++ b/src/models/Mdp.h @@ -139,7 +139,7 @@ public: std::vector<boost::container::flat_set<uint_fast64_t>> const& choiceLabeling = this->getChoiceLabeling(); - storm::storage::SparseMatrixBuilder<T> transitionMatrixBuilder; + storm::storage::SparseMatrixBuilder<T> transitionMatrixBuilder(0, 0, 0, true); std::vector<boost::container::flat_set<uint_fast64_t>> newChoiceLabeling; // Check for each choice of each state, whether the choice labels are fully contained in the given label set. @@ -156,7 +156,7 @@ public: } stateHasValidChoice = true; for (auto const& entry : this->getTransitionMatrix().getRow(choice)) { - transitionMatrixBuilder.addNextValue(currentRow, entry.first, entry.second); + transitionMatrixBuilder.addNextValue(currentRow, entry.getColumn(), entry.getValue()); } newChoiceLabeling.emplace_back(choiceLabeling[choice]); ++currentRow; diff --git a/src/storm.cpp b/src/storm.cpp index 8d3810f72..af69da175 100644 --- a/src/storm.cpp +++ b/src/storm.cpp @@ -35,7 +35,7 @@ #include "src/solver/GmmxxNondeterministicLinearEquationSolver.h" #include "src/solver/GurobiLpSolver.h" #include "src/counterexamples/MILPMinimalLabelSetGenerator.h" -// #include "src/counterexamples/SMTMinimalCommandSetGenerator.h" +#include "src/counterexamples/SMTMinimalCommandSetGenerator.h" #include "src/counterexamples/PathBasedSubsystemGenerator.h" #include "src/parser/AutoParser.h" #include "src/parser/MarkovAutomatonParser.h" @@ -587,7 +587,7 @@ int main(const int argc, const char* argv[]) { if (useMILP) { storm::counterexamples::MILPMinimalLabelSetGenerator<double>::computeCounterexample(program, *mdp, formulaPtr); } else { - // storm::counterexamples::SMTMinimalCommandSetGenerator<double>::computeCounterexample(program, constants, *mdp, formulaPtr); + storm::counterexamples::SMTMinimalCommandSetGenerator<double>::computeCounterexample(program, constants, *mdp, formulaPtr); } // Once we are done with the formula, delete it. @@ -596,7 +596,7 @@ int main(const int argc, const char* argv[]) { // MinCMD Time Measurement, End std::chrono::high_resolution_clock::time_point minCmdEnd = std::chrono::high_resolution_clock::now(); - std::cout << "Minimal command Counterexample generation took " << std::chrono::duration_cast<std::chrono::milliseconds>(minCmdStart - minCmdEnd).count() << " milliseconds." << std::endl; + std::cout << "Minimal command Counterexample generation took " << std::chrono::duration_cast<std::chrono::milliseconds>(minCmdEnd - minCmdStart).count() << " milliseconds." << std::endl; } else if (s->isSet("prctl")) { // Depending on the model type, the appropriate model checking procedure is chosen. storm::modelchecker::prctl::AbstractModelChecker<double>* modelchecker = nullptr; diff --git a/src/utility/PrismUtility.h b/src/utility/PrismUtility.h index 9ed15411a..de8bcb2c7 100644 --- a/src/utility/PrismUtility.h +++ b/src/utility/PrismUtility.h @@ -188,6 +188,62 @@ namespace storm { auto& labeledEntry = choice.getOrAddEntry(state); labeledEntry.addValue(probability, labels); } + + static std::map<std::string, storm::expressions::Expression> parseConstantDefinitionString(storm::prism::Program const& program, std::string const& constantDefinitionString) { + std::map<std::string, storm::expressions::Expression> constantDefinitions; + std::set<std::string> definedConstants; + + if (!constantDefinitionString.empty()) { + // Parse the string that defines the undefined constants of the model and make sure that it contains exactly + // one value for each undefined constant of the model. + std::vector<std::string> definitions; + boost::split(definitions, constantDefinitionString, boost::is_any_of(",")); + for (auto& definition : definitions) { + boost::trim(definition); + + // Check whether the token could be a legal constant definition. + uint_fast64_t positionOfAssignmentOperator = definition.find('='); + if (positionOfAssignmentOperator == std::string::npos) { + throw storm::exceptions::InvalidArgumentException() << "Illegal constant definition string: syntax error."; + } + + // Now extract the variable name and the value from the string. + std::string constantName = definition.substr(0, positionOfAssignmentOperator); + boost::trim(constantName); + std::string value = definition.substr(positionOfAssignmentOperator + 1); + boost::trim(value); + + // Check whether the constant is a legal undefined constant of the program and if so, of what type it is. + if (program.hasConstant(constantName)) { + // Get the actual constant and check whether it's in fact undefined. + auto const& constant = program.getConstant(constantName); + LOG_THROW(!constant.isDefined(), storm::exceptions::InvalidArgumentException, "Illegally trying to define already defined constant '" << constantName <<"'."); + LOG_THROW(definedConstants.find(constantName) == definedConstants.end(), storm::exceptions::InvalidArgumentException, "Illegally trying to define constant '" << constantName <<"' twice."); + definedConstants.insert(constantName); + + if (constant.getType() == storm::expressions::ExpressionReturnType::Bool) { + if (value == "true") { + constantDefinitions[constantName] = storm::expressions::Expression::createTrue(); + } else if (value == "false") { + constantDefinitions[constantName] = storm::expressions::Expression::createFalse(); + } else { + throw storm::exceptions::InvalidArgumentException() << "Illegal value for boolean constant: " << value << "."; + } + } else if (constant.getType() == storm::expressions::ExpressionReturnType::Int) { + int_fast64_t integerValue = std::stoi(value); + constantDefinitions[constantName] = storm::expressions::Expression::createIntegerLiteral(integerValue); + } else if (constant.getType() == storm::expressions::ExpressionReturnType::Double) { + double doubleValue = std::stod(value); + constantDefinitions[constantName] = storm::expressions::Expression::createDoubleLiteral(doubleValue); + } + } else { + throw storm::exceptions::InvalidArgumentException() << "Illegal constant definition string: unknown undefined constant " << constantName << "."; + } + } + } + + return constantDefinitions; + } } // namespace prism } // namespace utility } // namespace storm