diff --git a/src/adapters/GmmxxAdapter.h b/src/adapters/GmmxxAdapter.h index ff35800b2..b1d2206a5 100644 --- a/src/adapters/GmmxxAdapter.h +++ b/src/adapters/GmmxxAdapter.h @@ -51,8 +51,8 @@ public: columns.reserve(matrix.getEntryCount()); for (auto const& entry : matrix) { - columns.emplace_back(entry.first); - values.emplace_back(entry.second); + columns.emplace_back(entry.getColumn()); + values.emplace_back(entry.getValue()); } std::swap(result->ir, columns); diff --git a/src/counterexamples/MILPMinimalLabelSetGenerator.h b/src/counterexamples/MILPMinimalLabelSetGenerator.h index 197db76ab..ad581e616 100644 --- a/src/counterexamples/MILPMinimalLabelSetGenerator.h +++ b/src/counterexamples/MILPMinimalLabelSetGenerator.h @@ -65,13 +65,13 @@ namespace storm { * A helper struct capturing information about the variables of the MILP formulation. */ struct VariableInformation { - std::unordered_map<uint_fast64_t, uint_fast64_t> labelToVariableIndexMap; - std::unordered_map<uint_fast64_t, std::list<uint_fast64_t>> stateToChoiceVariablesIndexMap; - std::unordered_map<uint_fast64_t, uint_fast64_t> initialStateToChoiceVariableIndexMap; - std::unordered_map<uint_fast64_t, uint_fast64_t> stateToProbabilityVariableIndexMap; - uint_fast64_t virtualInitialStateVariableIndex; - std::unordered_map<uint_fast64_t, uint_fast64_t> problematicStateToVariableIndexMap; - std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, uint_fast64_t, PairHash> problematicTransitionToVariableIndexMap; + std::unordered_map<uint_fast64_t, std::string> labelToVariableMap; + std::unordered_map<uint_fast64_t, std::list<std::string>> stateToChoiceVariablesMap; + std::unordered_map<uint_fast64_t, std::string> initialStateToChoiceVariableMap; + std::unordered_map<uint_fast64_t, std::string> stateToProbabilityVariableMap; + std::string virtualInitialStateVariable; + std::unordered_map<uint_fast64_t, std::string> problematicStateToVariableMap; + std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, std::string, PairHash> problematicTransitionToVariableMap; uint_fast64_t numberOfVariables; VariableInformation() : numberOfVariables(0) {} @@ -130,7 +130,7 @@ namespace storm { bool allSuccessorsProblematic = true; for (auto const& successorEntry : transitionMatrix.getRow(row)) { // If there is a relevant successor, we need to add the labels of the current choice. - if (stateInformation.relevantStates.get(successorEntry.first) || psiStates.get(successorEntry.first)) { + if (stateInformation.relevantStates.get(successorEntry.getColumn()) || psiStates.get(successorEntry.getColumn())) { for (auto const& label : choiceLabeling[row]) { result.allRelevantLabels.insert(label); } @@ -139,7 +139,7 @@ namespace storm { result.relevantChoicesForRelevantStates[state].push_back(row); } } - if (!stateInformation.problematicStates.get(successorEntry.first)) { + if (!stateInformation.problematicStates.get(successorEntry.getColumn())) { allSuccessorsProblematic = false; } } @@ -167,14 +167,15 @@ namespace storm { * @param relevantLabels The set of relevant labels of the model. * @return A mapping from labels to variable indices. */ - static std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> createLabelVariables(storm::solver::LpSolver& solver, boost::container::flat_set<uint_fast64_t> const& relevantLabels) { + static std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> createLabelVariables(storm::solver::LpSolver& solver, boost::container::flat_set<uint_fast64_t> const& relevantLabels) { std::stringstream variableNameBuffer; - std::unordered_map<uint_fast64_t, uint_fast64_t> resultingMap; + std::unordered_map<uint_fast64_t, std::string> resultingMap; for (auto const& label : relevantLabels) { variableNameBuffer.str(""); variableNameBuffer.clear(); variableNameBuffer << "label" << label; - resultingMap[label] = solver.createBinaryVariable(variableNameBuffer.str(), 1); + resultingMap[label] = variableNameBuffer.str(); + solver.addBinaryVariable(resultingMap[label], 1); } return std::make_pair(resultingMap, relevantLabels.size()); } @@ -187,19 +188,20 @@ namespace storm { * @param choiceInformation The information about the choices of the model. * @return A mapping from states to a list of choice variable indices. */ - static std::pair<std::unordered_map<uint_fast64_t, std::list<uint_fast64_t>>, uint_fast64_t> createSchedulerVariables(storm::solver::LpSolver& solver, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation) { + static std::pair<std::unordered_map<uint_fast64_t, std::list<std::string>>, uint_fast64_t> createSchedulerVariables(storm::solver::LpSolver& solver, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation) { std::stringstream variableNameBuffer; uint_fast64_t numberOfVariablesCreated = 0; - std::unordered_map<uint_fast64_t, std::list<uint_fast64_t>> resultingMap; + std::unordered_map<uint_fast64_t, std::list<std::string>> resultingMap; for (auto state : stateInformation.relevantStates) { - resultingMap.emplace(state, std::list<uint_fast64_t>()); + resultingMap.emplace(state, std::list<std::string>()); std::list<uint_fast64_t> const& relevantChoicesForState = choiceInformation.relevantChoicesForRelevantStates.at(state); for (uint_fast64_t row : relevantChoicesForState) { variableNameBuffer.str(""); variableNameBuffer.clear(); variableNameBuffer << "choice" << row << "in" << state; - resultingMap[state].push_back(solver.createBinaryVariable(variableNameBuffer.str(), 0)); + resultingMap[state].push_back(variableNameBuffer.str()); + solver.addBinaryVariable(resultingMap[state].back()); ++numberOfVariablesCreated; } } @@ -215,10 +217,10 @@ namespace storm { * @param stateInformation The information about the states of the model. * @return A mapping from initial states to choice variable indices. */ - static std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> createInitialChoiceVariables(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, StateInformation const& stateInformation) { + static std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> createInitialChoiceVariables(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, StateInformation const& stateInformation) { std::stringstream variableNameBuffer; uint_fast64_t numberOfVariablesCreated = 0; - std::unordered_map<uint_fast64_t, uint_fast64_t> resultingMap; + std::unordered_map<uint_fast64_t, std::string> resultingMap; for (auto initialState : labeledMdp.getLabeledStates("init")) { // Only consider this initial state if it is relevant. @@ -226,7 +228,8 @@ namespace storm { variableNameBuffer.str(""); variableNameBuffer.clear(); variableNameBuffer << "init" << initialState; - resultingMap[initialState] = solver.createBinaryVariable(variableNameBuffer.str(), 0); + resultingMap[initialState] = variableNameBuffer.str(); + solver.addBinaryVariable(resultingMap[initialState]); ++numberOfVariablesCreated; } } @@ -240,34 +243,36 @@ namespace storm { * @param stateInformation The information about the states in the model. * @return A mapping from states to the index of the corresponding probability variables. */ - static std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> createProbabilityVariables(storm::solver::LpSolver& solver, StateInformation const& stateInformation) { + static std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> createProbabilityVariables(storm::solver::LpSolver& solver, StateInformation const& stateInformation) { std::stringstream variableNameBuffer; uint_fast64_t numberOfVariablesCreated = 0; - std::unordered_map<uint_fast64_t, uint_fast64_t> resultingMap; + std::unordered_map<uint_fast64_t, std::string> resultingMap; for (auto state : stateInformation.relevantStates) { variableNameBuffer.str(""); variableNameBuffer.clear(); variableNameBuffer << "p" << state; - resultingMap[state] = solver.createContinuousVariable(variableNameBuffer.str(), storm::solver::LpSolver::BOUNDED, 0, 1, 0); + resultingMap[state] = variableNameBuffer.str(); + solver.addBoundedContinuousVariable(resultingMap[state], 0, 1); ++numberOfVariablesCreated; } return std::make_pair(resultingMap, numberOfVariablesCreated); } /*! - * Creates the variables for the probabilities in the model. + * Creates the variable for the probability of the virtual initial state. * * @param solver The MILP solver. * @param maximizeProbability If set to true, the objective function is constructed in a way that a * label-minimal subsystem of maximal probability is computed. * @return The index of the variable for the probability of the virtual initial state. */ - static std::pair<uint_fast64_t, uint_fast64_t> createVirtualInitialStateVariable(storm::solver::LpSolver& solver, bool maximizeProbability = false) { + static std::pair<std::string, uint_fast64_t> createVirtualInitialStateVariable(storm::solver::LpSolver& solver, bool maximizeProbability = false) { std::stringstream variableNameBuffer; variableNameBuffer << "pinit"; - - return std::make_pair(solver.createContinuousVariable(variableNameBuffer.str(), storm::solver::LpSolver::BOUNDED, 0, 1, 0), 1); + std::string variableName = variableNameBuffer.str(); + solver.addBoundedContinuousVariable(variableName, 0, 1); + return std::make_pair(variableName, 1); } /*! @@ -278,10 +283,10 @@ namespace storm { * @param stateInformation The information about the states in the model. * @return A mapping from problematic states to the index of the corresponding variables. */ - static std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> createProblematicStateVariables(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation) { + static std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> createProblematicStateVariables(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation) { std::stringstream variableNameBuffer; uint_fast64_t numberOfVariablesCreated = 0; - std::unordered_map<uint_fast64_t, uint_fast64_t> resultingMap; + std::unordered_map<uint_fast64_t, std::string> resultingMap; for (auto state : stateInformation.problematicStates) { // First check whether there is not already a variable for this state and advance to the next state @@ -290,19 +295,21 @@ namespace storm { variableNameBuffer.str(""); variableNameBuffer.clear(); variableNameBuffer << "r" << state; - resultingMap[state] = solver.createContinuousVariable(variableNameBuffer.str(), storm::solver::LpSolver::BOUNDED, 0, 1, 0); + resultingMap[state] = variableNameBuffer.str(); + solver.addBoundedContinuousVariable(resultingMap[state], 0, 1); ++numberOfVariablesCreated; } std::list<uint_fast64_t> const& relevantChoicesForState = choiceInformation.relevantChoicesForRelevantStates.at(state); for (uint_fast64_t row : relevantChoicesForState) { for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(row)) { - if (stateInformation.relevantStates.get(successorEntry.first)) { - if (resultingMap.find(successorEntry.first) == resultingMap.end()) { + if (stateInformation.relevantStates.get(successorEntry.getColumn())) { + if (resultingMap.find(successorEntry.getColumn()) == resultingMap.end()) { variableNameBuffer.str(""); variableNameBuffer.clear(); - variableNameBuffer << "r" << successorEntry.first; - resultingMap[state] = solver.createContinuousVariable(variableNameBuffer.str(), storm::solver::LpSolver::BOUNDED, 0, 1, 0); + variableNameBuffer << "r" << successorEntry.getColumn(); + resultingMap[successorEntry.getColumn()] = variableNameBuffer.str(); + solver.addBoundedContinuousVariable(resultingMap[successorEntry.getColumn()], 0, 1); ++numberOfVariablesCreated; } } @@ -321,20 +328,21 @@ namespace storm { * @param choiceInformation The information about the choices in the model. * @return A mapping from problematic choices to the index of the corresponding variables. */ - static std::pair<std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, uint_fast64_t, PairHash>, uint_fast64_t> createProblematicChoiceVariables(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation) { + static std::pair<std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, std::string, PairHash>, uint_fast64_t> createProblematicChoiceVariables(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation) { std::stringstream variableNameBuffer; uint_fast64_t numberOfVariablesCreated = 0; - std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, uint_fast64_t, PairHash> resultingMap; + std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, std::string, PairHash> resultingMap; for (auto state : stateInformation.problematicStates) { std::list<uint_fast64_t> const& relevantChoicesForState = choiceInformation.relevantChoicesForRelevantStates.at(state); for (uint_fast64_t row : relevantChoicesForState) { for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(row)) { - if (stateInformation.relevantStates.get(successorEntry.first)) { + if (stateInformation.relevantStates.get(successorEntry.getColumn())) { variableNameBuffer.str(""); variableNameBuffer.clear(); - variableNameBuffer << "t" << state << "to" << successorEntry.first; - resultingMap[std::make_pair(state, successorEntry.first)] = solver.createBinaryVariable(variableNameBuffer.str(), 0); + variableNameBuffer << "t" << state << "to" << successorEntry.getColumn(); + resultingMap[std::make_pair(state, successorEntry.getColumn())] = variableNameBuffer.str(); + solver.addBinaryVariable(resultingMap[std::make_pair(state, successorEntry.getColumn())]); ++numberOfVariablesCreated; } } @@ -358,44 +366,44 @@ namespace storm { VariableInformation result; // Create variables for involved labels. - std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> labelVariableResult = createLabelVariables(solver, choiceInformation.allRelevantLabels); - result.labelToVariableIndexMap = std::move(labelVariableResult.first); + std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> labelVariableResult = createLabelVariables(solver, choiceInformation.allRelevantLabels); + result.labelToVariableMap = std::move(labelVariableResult.first); result.numberOfVariables += labelVariableResult.second; LOG4CPLUS_DEBUG(logger, "Created variables for labels."); // Create scheduler variables for relevant states and their actions. - std::pair<std::unordered_map<uint_fast64_t, std::list<uint_fast64_t>>, uint_fast64_t> schedulerVariableResult = createSchedulerVariables(solver, stateInformation, choiceInformation); - result.stateToChoiceVariablesIndexMap = std::move(schedulerVariableResult.first); + std::pair<std::unordered_map<uint_fast64_t, std::list<std::string>>, uint_fast64_t> schedulerVariableResult = createSchedulerVariables(solver, stateInformation, choiceInformation); + result.stateToChoiceVariablesMap = std::move(schedulerVariableResult.first); result.numberOfVariables += schedulerVariableResult.second; LOG4CPLUS_DEBUG(logger, "Created variables for nondeterministic choices."); // Create scheduler variables for nondeterministically choosing an initial state. - std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> initialChoiceVariableResult = createInitialChoiceVariables(solver, labeledMdp, stateInformation); - result.initialStateToChoiceVariableIndexMap = std::move(initialChoiceVariableResult.first); + std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> initialChoiceVariableResult = createInitialChoiceVariables(solver, labeledMdp, stateInformation); + result.initialStateToChoiceVariableMap = std::move(initialChoiceVariableResult.first); result.numberOfVariables += initialChoiceVariableResult.second; LOG4CPLUS_DEBUG(logger, "Created variables for the nondeterministic choice of the initial state."); // Create variables for probabilities for all relevant states. - std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> probabilityVariableResult = createProbabilityVariables(solver, stateInformation); - result.stateToProbabilityVariableIndexMap = std::move(probabilityVariableResult.first); + std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> probabilityVariableResult = createProbabilityVariables(solver, stateInformation); + result.stateToProbabilityVariableMap = std::move(probabilityVariableResult.first); result.numberOfVariables += probabilityVariableResult.second; LOG4CPLUS_DEBUG(logger, "Created variables for the reachability probabilities."); // Create a probability variable for a virtual initial state that nondeterministically chooses one of the system's real initial states as its target state. - std::pair<uint_fast64_t, uint_fast64_t> virtualInitialStateVariableResult = createVirtualInitialStateVariable(solver); - result.virtualInitialStateVariableIndex = virtualInitialStateVariableResult.first; + std::pair<std::string, uint_fast64_t> virtualInitialStateVariableResult = createVirtualInitialStateVariable(solver); + result.virtualInitialStateVariable = virtualInitialStateVariableResult.first; result.numberOfVariables += virtualInitialStateVariableResult.second; LOG4CPLUS_DEBUG(logger, "Created variables for the virtual initial state."); // Create variables for problematic states. - std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> problematicStateVariableResult = createProblematicStateVariables(solver, labeledMdp, stateInformation, choiceInformation); - result.problematicStateToVariableIndexMap = std::move(problematicStateVariableResult.first); + std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> problematicStateVariableResult = createProblematicStateVariables(solver, labeledMdp, stateInformation, choiceInformation); + result.problematicStateToVariableMap = std::move(problematicStateVariableResult.first); result.numberOfVariables += problematicStateVariableResult.second; LOG4CPLUS_DEBUG(logger, "Created variables for the problematic states."); // Create variables for problematic choices. - std::pair<std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, uint_fast64_t, PairHash>, uint_fast64_t> problematicTransitionVariableResult = createProblematicChoiceVariables(solver, labeledMdp, stateInformation, choiceInformation); - result.problematicTransitionToVariableIndexMap = problematicTransitionVariableResult.first; + std::pair<std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, std::string, PairHash>, uint_fast64_t> problematicTransitionVariableResult = createProblematicChoiceVariables(solver, labeledMdp, stateInformation, choiceInformation); + result.problematicTransitionToVariableMap = problematicTransitionVariableResult.first; result.numberOfVariables += problematicTransitionVariableResult.second; LOG4CPLUS_DEBUG(logger, "Created variables for the problematic choices."); @@ -419,7 +427,13 @@ namespace storm { * @return The total number of constraints that were created. */ static uint_fast64_t assertProbabilityGreaterThanThreshold(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, VariableInformation const& variableInformation, double probabilityThreshold, bool strictBound) { - solver.addConstraint("ProbGreaterThreshold", {variableInformation.virtualInitialStateVariableIndex}, {1}, strictBound ? storm::solver::LpSolver::GREATER : storm::solver::LpSolver::GREATER_EQUAL, probabilityThreshold); + storm::expressions::Expression constraint; + if (strictBound) { + constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.virtualInitialStateVariable) > storm::expressions::Expression::createDoubleLiteral(probabilityThreshold); + } else { + constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.virtualInitialStateVariable) >= storm::expressions::Expression::createDoubleLiteral(probabilityThreshold); + } + solver.addConstraint("ProbGreaterThreshold", constraint); return 1; } @@ -435,28 +449,28 @@ namespace storm { // Assert that the policy chooses at most one action in each state of the system. uint_fast64_t numberOfConstraintsCreated = 0; for (auto state : stateInformation.relevantStates) { - std::list<uint_fast64_t> const& choiceVariableIndices = variableInformation.stateToChoiceVariablesIndexMap.at(state); - std::vector<uint_fast64_t> variables; - std::vector<double> coefficients(choiceVariableIndices.size(), 1); - variables.reserve(choiceVariableIndices.size()); - for (auto choiceVariableIndex : choiceVariableIndices) { - variables.push_back(choiceVariableIndex); + std::list<std::string> const& choiceVariableIndices = variableInformation.stateToChoiceVariablesMap.at(state); + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleLiteral(0); + + for (auto const& choiceVariable : choiceVariableIndices) { + constraint = constraint + storm::expressions::Expression::createIntegerVariable(choiceVariable); } - solver.addConstraint("ValidPolicy" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS_EQUAL, 1); + constraint = constraint <= storm::expressions::Expression::createDoubleLiteral(1); + + solver.addConstraint("ValidPolicy" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; } // Now assert that the virtual initial state picks exactly one initial state from the system as its // successor state. - std::vector<uint_fast64_t> variables; - variables.reserve(variableInformation.initialStateToChoiceVariableIndexMap.size()); - std::vector<double> coefficients(variableInformation.initialStateToChoiceVariableIndexMap.size(), 1); - for (auto initialStateVariableIndexPair : variableInformation.initialStateToChoiceVariableIndexMap) { - variables.push_back(initialStateVariableIndexPair.second); + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleLiteral(0); + for (auto const& initialStateVariablePair : variableInformation.initialStateToChoiceVariableMap) { + constraint = constraint + storm::expressions::Expression::createIntegerVariable(initialStateVariablePair.second); } + constraint = constraint == storm::expressions::Expression::createDoubleLiteral(1); - solver.addConstraint("VirtualInitialStateChoosesOneInitialState", variables, coefficients, storm::solver::LpSolver::EQUAL, 1); + solver.addConstraint("VirtualInitialStateChoosesOneInitialState", constraint); ++numberOfConstraintsCreated; return numberOfConstraintsCreated; @@ -478,13 +492,14 @@ namespace storm { std::vector<boost::container::flat_set<uint_fast64_t>> const& choiceLabeling = labeledMdp.getChoiceLabeling(); for (auto state : stateInformation.relevantStates) { - std::list<uint_fast64_t>::const_iterator choiceVariableIndicesIterator = variableInformation.stateToChoiceVariablesIndexMap.at(state).begin(); + std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesMap.at(state).begin(); for (auto choice : choiceInformation.relevantChoicesForRelevantStates.at(state)) { for (auto label : choiceLabeling[choice]) { - solver.addConstraint("ChoicesImplyLabels" + std::to_string(numberOfConstraintsCreated), {variableInformation.labelToVariableIndexMap.at(label), *choiceVariableIndicesIterator}, {1, -1}, storm::solver::LpSolver::GREATER_EQUAL, 0); + storm::expressions::Expression constraint = storm::expressions::Expression::createIntegerVariable(variableInformation.labelToVariableMap.at(label)) - storm::expressions::Expression::createIntegerVariable(*choiceVariableIterator) >= storm::expressions::Expression::createDoubleLiteral(0); + solver.addConstraint("ChoicesImplyLabels" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; } - ++choiceVariableIndicesIterator; + ++choiceVariableIterator; } } return numberOfConstraintsCreated; @@ -503,16 +518,12 @@ namespace storm { static uint_fast64_t assertZeroProbabilityWithoutChoice(storm::solver::LpSolver& solver, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation, VariableInformation const& variableInformation) { uint_fast64_t numberOfConstraintsCreated = 0; for (auto state : stateInformation.relevantStates) { - std::list<uint_fast64_t> const& choiceVariableIndices = variableInformation.stateToChoiceVariablesIndexMap.at(state); - - std::vector<double> coefficients(choiceVariableIndices.size() + 1, -1); - coefficients[0] = 1; - std::vector<uint_fast64_t> variables; - variables.reserve(variableInformation.stateToChoiceVariablesIndexMap.at(state).size() + 1); - variables.push_back(variableInformation.stateToProbabilityVariableIndexMap.at(state)); - variables.insert(variables.end(), choiceVariableIndices.begin(), choiceVariableIndices.end()); - - solver.addConstraint("ProbabilityIsZeroIfNoAction" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS_EQUAL, 0); + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.stateToProbabilityVariableMap.at(state)); + for (auto const& choiceVariable : variableInformation.stateToChoiceVariablesMap.at(state)) { + constraint = constraint - storm::expressions::Expression::createIntegerVariable(choiceVariable); + } + constraint = constraint <= storm::expressions::Expression::createDoubleLiteral(0); + solver.addConstraint("ProbabilityIsZeroIfNoAction" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; } return numberOfConstraintsCreated; @@ -532,40 +543,32 @@ namespace storm { static uint_fast64_t assertReachabilityProbabilities(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, storm::storage::BitVector const& psiStates, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation, VariableInformation const& variableInformation) { uint_fast64_t numberOfConstraintsCreated = 0; for (auto state : stateInformation.relevantStates) { - std::vector<double> coefficients; - std::vector<uint_fast64_t> variables; - - std::list<uint_fast64_t>::const_iterator choiceVariableIndicesIterator = variableInformation.stateToChoiceVariablesIndexMap.at(state).begin(); + std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesMap.at(state).begin(); for (auto choice : choiceInformation.relevantChoicesForRelevantStates.at(state)) { - variables.clear(); - coefficients.clear(); - variables.push_back(variableInformation.stateToProbabilityVariableIndexMap.at(state)); - coefficients.push_back(1.0); + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.stateToProbabilityVariableMap.at(state)); double rightHandSide = 1; for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(choice)) { - if (stateInformation.relevantStates.get(successorEntry.first)) { - variables.push_back(static_cast<int>(variableInformation.stateToProbabilityVariableIndexMap.at(successorEntry.first))); - coefficients.push_back(-successorEntry.second); - } else if (psiStates.get(successorEntry.first)) { - rightHandSide += successorEntry.second; + if (stateInformation.relevantStates.get(successorEntry.getColumn())) { + constraint = constraint - storm::expressions::Expression::createDoubleLiteral(successorEntry.getValue()) * storm::expressions::Expression::createDoubleVariable(variableInformation.stateToProbabilityVariableMap.at(successorEntry.getColumn())); + } else if (psiStates.get(successorEntry.getColumn())) { + rightHandSide += successorEntry.getValue(); } } - coefficients.push_back(1); - variables.push_back(*choiceVariableIndicesIterator); - - solver.addConstraint("ReachabilityProbabilities" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS_EQUAL, rightHandSide); + constraint = constraint + storm::expressions::Expression::createIntegerVariable(*choiceVariableIterator) <= storm::expressions::Expression::createDoubleLiteral(rightHandSide); + solver.addConstraint("ReachabilityProbabilities" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; - ++choiceVariableIndicesIterator; + ++choiceVariableIterator; } } // Make sure that the virtual initial state is being assigned the probability from the initial state // that it selected as a successor state. - for (auto initialStateVariableIndexPair : variableInformation.initialStateToChoiceVariableIndexMap) { - solver.addConstraint("VirtualInitialStateHasCorrectProbability" + std::to_string(numberOfConstraintsCreated), {variableInformation.virtualInitialStateVariableIndex, variableInformation.stateToProbabilityVariableIndexMap.at(initialStateVariableIndexPair.first), initialStateVariableIndexPair.second}, {1, -1, 1}, storm::solver::LpSolver::LESS_EQUAL, 1); + for (auto const& initialStateVariablePair : variableInformation.initialStateToChoiceVariableMap) { + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.virtualInitialStateVariable) - storm::expressions::Expression::createDoubleVariable(variableInformation.stateToProbabilityVariableMap.at(initialStateVariablePair.first)) + storm::expressions::Expression::createDoubleVariable(initialStateVariablePair.second) <= storm::expressions::Expression::createDoubleLiteral(1); + solver.addConstraint("VirtualInitialStateHasCorrectProbability" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; } @@ -587,44 +590,34 @@ namespace storm { for (auto stateListPair : choiceInformation.problematicChoicesForProblematicStates) { for (auto problematicChoice : stateListPair.second) { - std::list<uint_fast64_t>::const_iterator choiceVariableIndicesIterator = variableInformation.stateToChoiceVariablesIndexMap.at(stateListPair.first).begin(); + std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesMap.at(stateListPair.first).begin(); for (auto relevantChoice : choiceInformation.relevantChoicesForRelevantStates.at(stateListPair.first)) { if (relevantChoice == problematicChoice) { break; } - ++choiceVariableIndicesIterator; + ++choiceVariableIterator; } - std::vector<uint_fast64_t> variables; - std::vector<double> coefficients; - - variables.push_back(*choiceVariableIndicesIterator); - coefficients.push_back(1); - + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(*choiceVariableIterator); for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(problematicChoice)) { - variables.push_back(variableInformation.problematicTransitionToVariableIndexMap.at(std::make_pair(stateListPair.first, successorEntry.first))); - coefficients.push_back(-1); + constraint = constraint - storm::expressions::Expression::createDoubleVariable(variableInformation.problematicTransitionToVariableMap.at(std::make_pair(stateListPair.first, successorEntry.getColumn()))); } + constraint = constraint <= storm::expressions::Expression::createDoubleLiteral(0); - solver.addConstraint("UnproblematicStateReachable" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS_EQUAL, 0); + solver.addConstraint("UnproblematicStateReachable" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; } } for (auto state : stateInformation.problematicStates) { for (auto problematicChoice : choiceInformation.problematicChoicesForProblematicStates.at(state)) { - for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(state)) { - std::vector<uint_fast64_t> variables; - std::vector<double> coefficients; - - variables.push_back(variableInformation.problematicStateToVariableIndexMap.at(state)); - coefficients.push_back(1); - variables.push_back(variableInformation.problematicStateToVariableIndexMap.at(successorEntry.first)); - coefficients.push_back(-1); - variables.push_back(variableInformation.problematicTransitionToVariableIndexMap.at(std::make_pair(state, successorEntry.first))); - coefficients.push_back(1); + for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(problematicChoice)) { + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.problematicStateToVariableMap.at(state)); + constraint = constraint - storm::expressions::Expression::createDoubleVariable(variableInformation.problematicStateToVariableMap.at(successorEntry.getColumn())); + constraint = constraint + storm::expressions::Expression::createDoubleVariable(variableInformation.problematicTransitionToVariableMap.at(std::make_pair(state, successorEntry.getColumn()))); + constraint = constraint < storm::expressions::Expression::createDoubleLiteral(1); - solver.addConstraint("UnproblematicStateReachable" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS, 1); + solver.addConstraint("UnproblematicStateReachable" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; } } @@ -646,7 +639,8 @@ namespace storm { uint_fast64_t numberOfConstraintsCreated = 0; for (auto label : choiceInformation.knownLabels) { - solver.addConstraint("KnownLabels" + std::to_string(numberOfConstraintsCreated), {variableInformation.labelToVariableIndexMap.at(label)}, {1}, storm::solver::LpSolver::EQUAL, 1); + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.labelToVariableMap.at(label)) == storm::expressions::Expression::createDoubleLiteral(1); + solver.addConstraint("KnownLabels" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; } @@ -667,61 +661,52 @@ namespace storm { static uint_fast64_t assertSchedulerCuts(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, storm::storage::BitVector const& psiStates, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation, VariableInformation const& variableInformation) { storm::storage::SparseMatrix<T> backwardTransitions = labeledMdp.getBackwardTransitions(); uint_fast64_t numberOfConstraintsCreated = 0; - std::vector<uint_fast64_t> variables; - std::vector<double> coefficients; for (auto state : stateInformation.relevantStates) { // Assert that all states, that select an action, this action either has a non-zero probability to // go to a psi state directly, or in the successor states, at least one action is selected as well. - std::list<uint_fast64_t>::const_iterator choiceVariableIndicesIterator = variableInformation.stateToChoiceVariablesIndexMap.at(state).begin(); + std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesMap.at(state).begin(); for (auto choice : choiceInformation.relevantChoicesForRelevantStates.at(state)) { bool psiStateReachableInOneStep = false; for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(choice)) { - if (psiStates.get(successorEntry.first)) { + if (psiStates.get(successorEntry.getColumn())) { psiStateReachableInOneStep = true; } } if (!psiStateReachableInOneStep) { - variables.clear(); - coefficients.clear(); - - variables.push_back(static_cast<int>(*choiceVariableIndicesIterator)); - coefficients.push_back(1); - + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(*choiceVariableIterator); for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(choice)) { - if (state != successorEntry.first && stateInformation.relevantStates.get(successorEntry.first)) { - std::list<uint_fast64_t> const& successorChoiceVariableIndices = variableInformation.stateToChoiceVariablesIndexMap.at(successorEntry.first); + if (state != successorEntry.getColumn() && stateInformation.relevantStates.get(successorEntry.getColumn())) { + std::list<std::string> const& successorChoiceVariableIndices = variableInformation.stateToChoiceVariablesMap.at(successorEntry.getColumn()); - for (auto choiceVariableIndex : successorChoiceVariableIndices) { - variables.push_back(choiceVariableIndex); - coefficients.push_back(-1); + for (auto const& choiceVariable : successorChoiceVariableIndices) { + constraint = constraint - storm::expressions::Expression::createDoubleVariable(choiceVariable); } } } + constraint = constraint <= storm::expressions::Expression::createDoubleLiteral(1); - solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS_EQUAL, 1); + solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; } - ++choiceVariableIndicesIterator; + ++choiceVariableIterator; } // For all states assert that there is either a selected incoming transition in the subsystem or the // state is the chosen initial state if there is one selected action in the current state. - variables.clear(); - coefficients.clear(); - - for (auto choiceVariableIndex : variableInformation.stateToChoiceVariablesIndexMap.at(state)) { - variables.push_back(choiceVariableIndex); - coefficients.push_back(1); + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleLiteral(0); + + for (auto const& choiceVariable : variableInformation.stateToChoiceVariablesMap.at(state)) { + constraint = constraint + storm::expressions::Expression::createDoubleVariable(choiceVariable); } // Compute the set of predecessors. std::unordered_set<uint_fast64_t> predecessors; for (auto const& predecessorEntry : backwardTransitions.getRow(state)) { - if (state != predecessorEntry.first) { - predecessors.insert(predecessorEntry.first); + if (state != predecessorEntry.getColumn()) { + predecessors.insert(predecessorEntry.getColumn()); } } @@ -731,13 +716,13 @@ namespace storm { continue; } - std::list<uint_fast64_t>::const_iterator choiceVariableIndicesIterator = variableInformation.stateToChoiceVariablesIndexMap.at(predecessor).begin(); + std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesMap.at(predecessor).begin(); for (auto relevantChoice : choiceInformation.relevantChoicesForRelevantStates.at(predecessor)) { bool choiceTargetsCurrentState = false; // Check if the current choice targets the current state. for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(relevantChoice)) { - if (state == successorEntry.first) { + if (state == successorEntry.getColumn()) { choiceTargetsCurrentState = true; break; } @@ -745,45 +730,42 @@ namespace storm { // If it does, we can add the choice to the sum. if (choiceTargetsCurrentState) { - variables.push_back(static_cast<int>(*choiceVariableIndicesIterator)); - coefficients.push_back(-1); + constraint = constraint - storm::expressions::Expression::createDoubleVariable(*choiceVariableIterator); } - ++choiceVariableIndicesIterator; + ++choiceVariableIterator; } } // If the current state is an initial state and is selected as a successor state by the virtual // initial state, then this also justifies making a choice in the current state. if (labeledMdp.getLabeledStates("init").get(state)) { - variables.push_back(variableInformation.initialStateToChoiceVariableIndexMap.at(state)); - coefficients.push_back(-1); + constraint = constraint - storm::expressions::Expression::createDoubleVariable(variableInformation.initialStateToChoiceVariableMap.at(state)); } + constraint = constraint <= storm::expressions::Expression::createDoubleLiteral(0); - solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS_EQUAL, 0); + solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; } // Assert that at least one initial state selects at least one action. - variables.clear(); - coefficients.clear(); + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleLiteral(0); for (auto initialState : labeledMdp.getLabeledStates("init")) { - for (auto choiceVariableIndex : variableInformation.stateToChoiceVariablesIndexMap.at(initialState)) { - variables.push_back(choiceVariableIndex); - coefficients.push_back(1); + for (auto const& choiceVariable : variableInformation.stateToChoiceVariablesMap.at(initialState)) { + constraint = constraint + storm::expressions::Expression::createDoubleVariable(choiceVariable); } } - solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::GREATER_EQUAL, 1); + constraint = constraint >= storm::expressions::Expression::createDoubleLiteral(1); + solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; // Add constraints that ensure at least one choice is selected that targets a psi state. - variables.clear(); - coefficients.clear(); + constraint = storm::expressions::Expression::createDoubleLiteral(0); std::unordered_set<uint_fast64_t> predecessors; for (auto psiState : psiStates) { // Compute the set of predecessors. for (auto const& predecessorEntry : backwardTransitions.getRow(psiState)) { - if (psiState != predecessorEntry.first) { - predecessors.insert(predecessorEntry.first); + if (psiState != predecessorEntry.getColumn()) { + predecessors.insert(predecessorEntry.getColumn()); } } } @@ -794,13 +776,13 @@ namespace storm { continue; } - std::list<uint_fast64_t>::const_iterator choiceVariableIndicesIterator = variableInformation.stateToChoiceVariablesIndexMap.at(predecessor).begin(); + std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesMap.at(predecessor).begin(); for (auto relevantChoice : choiceInformation.relevantChoicesForRelevantStates.at(predecessor)) { bool choiceTargetsPsiState = false; // Check if the current choice targets the current state. for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(relevantChoice)) { - if (psiStates.get(successorEntry.first)) { + if (psiStates.get(successorEntry.getColumn())) { choiceTargetsPsiState = true; break; } @@ -808,14 +790,14 @@ namespace storm { // If it does, we can add the choice to the sum. if (choiceTargetsPsiState) { - variables.push_back(*choiceVariableIndicesIterator); - coefficients.push_back(1); + constraint = constraint + storm::expressions::Expression::createDoubleVariable(*choiceVariableIterator); } - ++choiceVariableIndicesIterator; + ++choiceVariableIterator; } } + constraint = constraint >= storm::expressions::Expression::createDoubleLiteral(1); - solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::GREATER_EQUAL, 1); + solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; return numberOfConstraintsCreated; @@ -872,6 +854,9 @@ namespace storm { LOG4CPLUS_DEBUG(logger, "Asserted scheduler cuts."); } + // Finally, we can tell the solver to incorporate the latest changes. + solver.update(); + LOG4CPLUS_INFO(logger, "Successfully created " << numberOfConstraints << " MILP constraints."); } @@ -884,7 +869,7 @@ namespace storm { static boost::container::flat_set<uint_fast64_t> getUsedLabelsInSolution(storm::solver::LpSolver const& solver, VariableInformation const& variableInformation) { boost::container::flat_set<uint_fast64_t> result; - for (auto labelVariablePair : variableInformation.labelToVariableIndexMap) { + for (auto const& labelVariablePair : variableInformation.labelToVariableMap) { bool labelTaken = solver.getBinaryValue(labelVariablePair.second); if (labelTaken) { @@ -908,10 +893,10 @@ namespace storm { std::map<uint_fast64_t, uint_fast64_t> result; for (auto state : stateInformation.relevantStates) { - std::list<uint_fast64_t>::const_iterator choiceVariableIndicesIterator = variableInformation.stateToChoiceVariablesIndexMap.at(state).begin(); + std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesIndexMap.at(state).begin(); for (auto choice : choiceInformation.relevantChoicesForRelevantStates.at(state)) { - bool choiceTaken = solver.getBinaryValue(*choiceVariableIndicesIterator); - ++choiceVariableIndicesIterator; + bool choiceTaken = solver.getBinaryValue(*choiceVariableIterator); + ++choiceVariableIterator; if (choiceTaken) { result.emplace_hint(result.end(), state, choice); } @@ -930,20 +915,20 @@ namespace storm { */ static std::pair<uint_fast64_t, double> getReachabilityProbability(storm::solver::LpSolver const& solver, storm::models::Mdp<T> const& labeledMdp, VariableInformation const& variableInformation) { uint_fast64_t selectedInitialState = 0; - for (auto initialStateVariableIndexPair : variableInformation.initialStateToChoiceVariableIndexMap) { - bool initialStateChosen = solver.getBinaryValue(initialStateVariableIndexPair.second); + for (auto const& initialStateVariablePair : variableInformation.initialStateToChoiceVariableMap) { + bool initialStateChosen = solver.getBinaryValue(initialStateVariablePair.second); if (initialStateChosen) { - selectedInitialState = initialStateVariableIndexPair.first; + selectedInitialState = initialStateVariablePair.first; break; } } - double reachabilityProbability = solver.getContinuousValue(variableInformation.virtualInitialStateVariableIndex); + double reachabilityProbability = solver.getContinuousValue(variableInformation.virtualInitialStateVariable); return std::make_pair(selectedInitialState, reachabilityProbability); } public: - + static boost::container::flat_set<uint_fast64_t> getMinimalLabelSet(storm::models::Mdp<T> const& labeledMdp, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, double probabilityThreshold, bool strictBound, bool checkThresholdFeasible = false, bool includeSchedulerCuts = false) { // (0) Check whether the MDP is indeed labeled. if (!labeledMdp.hasChoiceLabeling()) { diff --git a/src/counterexamples/PathBasedSubsystemGenerator.h b/src/counterexamples/PathBasedSubsystemGenerator.h index 11e24d00d..2325eebe8 100644 --- a/src/counterexamples/PathBasedSubsystemGenerator.h +++ b/src/counterexamples/PathBasedSubsystemGenerator.h @@ -69,31 +69,31 @@ public: for(auto const& trans : transMat.getRow(init)) { //save transition only if it's no 'virtual transition of prob 0 and it doesn't go from init state to init state. - if(trans.second != (T) 0 && !subSysStates.get(trans.first)) { + if(trans.getValue() != (T) 0 && !subSysStates.get(trans.getColumn())) { //new state? - if(distances[trans.first].second == (T) -1) { - distances[trans.first].first = init; - distances[trans.first].second = trans.second; + if(distances[trans.getColumn()].second == (T) -1) { + distances[trans.getColumn()].first = init; + distances[trans.getColumn()].second = trans.getValue(); - activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, distances[trans.first].second)); + activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), distances[trans.getColumn()].second)); } - else if(distances[trans.first].second < trans.second){ + else if(distances[trans.getColumn()].second < trans.getValue()){ //This state has already been discovered //And the distance can be improved by using this transition. //find state in set, remove it, reenter it with new and correct values. - auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.first, distances[trans.first].second)); + auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.getColumn(), distances[trans.getColumn()].second)); for(;range.first != range.second; range.first++) { - if(trans.first == range.first->first) { + if(trans.getColumn() == range.first->first) { activeSet.erase(range.first); break; } } - distances[trans.first].first = init; - distances[trans.first].second = trans.second; + distances[trans.getColumn()].first = init; + distances[trans.getColumn()].second = trans.getValue(); - activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, trans.second)); + activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), trans.getValue())); } } } @@ -115,36 +115,36 @@ public: // Look at all neighbors for(auto const& trans : transMat.getRow(activeState.first)) { // Only consider the transition if it's not virtual - if(trans.second != (T) 0) { + if(trans.getValue() != (T) 0) { - T distance = activeState.second * trans.second; + T distance = activeState.second * trans.getValue(); //not discovered or initial terminal state - if(distances[trans.first].second == (T)-1) { + if(distances[trans.getColumn()].second == (T)-1) { //New state discovered -> save it - distances[trans.first].first = activeState.first; - distances[trans.first].second = distance; + distances[trans.getColumn()].first = activeState.first; + distances[trans.getColumn()].second = distance; // push newly discovered state into activeSet - activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, distance)); + activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), distance)); } - else if(distances[trans.first].second < distance ){ + else if(distances[trans.getColumn()].second < distance) { //This state has already been discovered //And the distance can be improved by using this transition. //find state in set, remove it, reenter it with new and correct values. - auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.first, distances[trans.first].second)); + auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.getColumn(), distances[trans.getColumn()].second)); for(;range.first != range.second; range.first++) { - if(trans.first == range.first->first) { + if(trans.getColumn() == range.first->first) { activeSet.erase(range.first); break; } } - distances[trans.first].first = activeState.first; - distances[trans.first].second = distance; - activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, distance)); + distances[trans.getColumn()].first = activeState.first; + distances[trans.getColumn()].second = distance; + activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), distance)); } } } @@ -182,33 +182,33 @@ public: for(auto const& trans : transMat.getRow(init)) { //save transition only if it's no 'virtual transition of prob 0 and it doesn't go from init state to init state. - if(trans.second != (T) 0 && !subSysStates.get(trans.first)) { + if(trans.getValue() != (T) 0 && !subSysStates.get(trans.getColumn())) { //new state? - if(distances[trans.first].second == (T) -1) { + if(distances[trans.getColumn()].second == (T) -1) { //for initialization of subsys -> subsys search use prob (init -> subsys state -> found state) instead of prob(subsys state -> found state) - distances[trans.first].first = init; - distances[trans.first].second = trans.second * (itDistances[init].second == -1 ? 1 : itDistances[init].second); + distances[trans.getColumn()].first = init; + distances[trans.getColumn()].second = trans.getValue() * (itDistances[init].second == -1 ? 1 : itDistances[init].second); - activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, distances[trans.first].second)); + activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), distances[trans.getColumn()].second)); } - else if(distances[trans.first].second < trans.second * itDistances[init].second){ + else if(distances[trans.getColumn()].second < trans.getValue() * itDistances[init].second){ //This state has already been discovered //And the distance can be improved by using this transition. //find state in set, remove it, reenter it with new and correct values. - auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.first, distances[trans.first].second)); + auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.getColumn(), distances[trans.getColumn()].second)); for(;range.first != range.second; range.first++) { - if(trans.first == range.first->first) { + if(trans.getColumn() == range.first->first) { activeSet.erase(range.first); break; } } //for initialization of subsys -> subsys search use prob (init -> subsys state -> found state) instead of prob(subsys state -> found state) - distances[trans.first].first = init; - distances[trans.first].second = trans.second * (itDistances[init].second == -1 ? 1 : itDistances[init].second); + distances[trans.getColumn()].first = init; + distances[trans.getColumn()].second = trans.getValue() * (itDistances[init].second == -1 ? 1 : itDistances[init].second); - activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, trans.second)); + activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), trans.getValue())); } } } @@ -225,7 +225,7 @@ public: activeSet.erase(--activeSet.end()); // Always stop at first target/terminal state - //if(terminalStates.get(activeState.first) || subSysStates.get(activeState.first)) break; + //if(terminalStates.get(activeState.getColumn()) || subSysStates.get(activeState.getColumn())) break; // If this is an initial state, do not consider its outgoing transitions, since all relevant ones have already been considered // Same goes for forbidden states since they may not be used on a path, except as last node. @@ -233,36 +233,36 @@ public: // Look at all neighbors for(auto const& trans : transMat.getRow(activeState.first)) { // Only consider the transition if it's not virtual - if(trans.second != (T) 0) { + if(trans.getValue() != (T) 0) { - T distance = activeState.second * trans.second; + T distance = activeState.second * trans.getValue(); //not discovered or initial terminal state - if(distances[trans.first].second == (T)-1) { + if(distances[trans.getColumn()].second == (T)-1) { //New state discovered -> save it - distances[trans.first].first = activeState.first; - distances[trans.first].second = distance; + distances[trans.getColumn()].first = activeState.first; + distances[trans.getColumn()].second = distance; // push newly discovered state into activeSet - activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, distance)); + activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), distance)); } - else if(distances[trans.first].second < distance ){ + else if(distances[trans.getColumn()].second < distance) { //This state has already been discovered //And the distance can be improved by using this transition. //find state in set, remove it, reenter it with new and correct values. - auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.first, distances[trans.first].second)); + auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.getColumn(), distances[trans.getColumn()].second)); for(;range.first != range.second; range.first++) { - if(trans.first == range.first->first) { + if(trans.getColumn() == range.first->first) { activeSet.erase(range.first); break; } } - distances[trans.first].first = activeState.first; - distances[trans.first].second = distance; - activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, distance)); + distances[trans.getColumn()].first = activeState.first; + distances[trans.getColumn()].second = distance; + activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), distance)); } } } @@ -292,8 +292,8 @@ public: // if there is a terminal state that is an initial state then prob == 1 and return if(initStates.get(*target)){ - distances[*target].first = *target; - distances[*target].second = (T) 1; + distances[*target].getColumn() = *target; + distances[*target].getValue() = (T) 1; return; } @@ -302,19 +302,19 @@ public: //only use if allowed and not in subsys and not terminal if(allowedStates.get(*iter) && !subSysStates.get(*iter) && !terminalStates.get(*iter)) { //new state? - if(distances[*iter].second == (T) -1) { + if(distances[*iter].getValue() == (T) -1) { // save as discovered and push into active set - distances[*iter].first = *target; //successor - distances[*iter].second = transMat.getValue(*iter, *target); //prob of shortest path + distances[*iter].getColumn() = *target; //successor + distances[*iter].getValue() = transMat.getValue(*iter, *target); //prob of shortest path activeSet.insert(std::pair<uint_fast64_t, T>(*iter, probabilities[*iter])); //prob of reaching some terminal state from pred. } else { // state was already discovered // is this the better transition? - if(distances[*iter].second > transMat.getValue(*iter, *target)) { - distances[*iter].first = *target; - distances[*iter].second = transMat.getValue(*iter, *target); + if(distances[*iter].getValue() > transMat.getValue(*iter, *target)) { + distances[*iter].getColumn() = *target; + distances[*iter].getValue() = transMat.getValue(*iter, *target); } } } @@ -328,19 +328,19 @@ public: //only use if allowed and not in subsys and not terminal if(allowedStates.get(*iter) && !subSysStates.get(*iter) && !terminalStates.get(*iter)) { //new state? - if(distances[*iter].second == (T) -1) { + if(distances[*iter].getValue() == (T) -1) { // save as discovered and push into active set - distances[*iter].first = *sysState; //successor - distances[*iter].second = transMat.getValue(*iter, *sysState); //prob of shortest path + distances[*iter].getColumn() = *sysState; //successor + distances[*iter].getValue() = transMat.getValue(*iter, *sysState); //prob of shortest path activeSet.insert(std::pair<uint_fast64_t, T>(*iter, probabilities[*iter])); //prob of reaching some terminal state from pred. } else { // state was already discovered // is this the better transition? - if(distances[*iter].second > transMat.getValue(*iter, *sysState)) { - distances[*iter].first = *sysState; - distances[*iter].second = transMat.getValue(*iter, *sysState); + if(distances[*iter].getValue() > transMat.getValue(*iter, *sysState)) { + distances[*iter].getColumn() = *sysState; + distances[*iter].getValue() = transMat.getValue(*iter, *sysState); } } } @@ -355,7 +355,7 @@ public: while(!activeSet.empty()) { // copy here since using a reference leads to segfault state = *(--activeSet.end()); - activeState = state.first; + activeState = state.getColumn(); activeSet.erase(--activeSet.end()); //stop on the first subsys/init state @@ -368,19 +368,19 @@ public: //only if transition is not "virtual" and no selfloop if(*iter != activeState && transMat.getValue(*iter, activeState) != (T) 0) { //new state? - if(distances[*iter].second == (T) -1) { + if(distances[*iter].getValue() == (T) -1) { // save as discovered and push into active set - distances[*iter].first = activeState; - distances[*iter].second = transMat.getValue(*iter, activeState) * distances[activeState].second; + distances[*iter].getColumn() = activeState; + distances[*iter].getValue() = transMat.getValue(*iter, activeState) * distances[activeState].getValue(); activeSet.insert(std::pair<uint_fast64_t, T>(*iter, probabilities[*iter])); } else { // state was already discovered // is this the better transition? - if(distances[*iter].second < transMat.getValue(*iter, activeState) * distances[activeState].second) { - distances[*iter].first = activeState; - distances[*iter].second = transMat.getValue(*iter, activeState) * distances[activeState].second; + if(distances[*iter].getValue() < transMat.getValue(*iter, activeState) * distances[activeState].getValue()) { + distances[*iter].getColumn() = activeState; + distances[*iter].getValue() = transMat.getValue(*iter, activeState) * distances[activeState].getValue(); } } } @@ -389,15 +389,15 @@ public: } //get path probability - probability = distances[activeState].second; + probability = distances[activeState].getValue(); if(probability == (T) -1) probability = 1; // iterate over the successors until reaching the end of the finite path shortestPath.push_back(activeState); - activeState = distances[activeState].first; + activeState = distances[activeState].getColumn(); while(!terminalStates.get(activeState) && !subSysStates.get(activeState)) { shortestPath.push_back(activeState); - activeState = distances[activeState].first; + activeState = distances[activeState].getColumn(); } shortestPath.push_back(activeState); } @@ -482,7 +482,7 @@ public: shortestPath.push_back(bestIndex); //At last compensate for the distance between init and source state - probability = itSearch ? probability : probability / itDistances[bestIndex].second; + probability = itSearch ? probability : probability / itDistances[bestIndex].first; } private: diff --git a/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp b/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp index 82590e31e..b5fa4bad4 100644 --- a/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp +++ b/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp @@ -5,4 +5,4 @@ bool SparseMarkovAutomatonCslModelCheckerOptionsRegistered = storm::settings::Se instance->addOption(storm::settings::OptionBuilder("GmmxxLinearEquationSolver", "digiprecision", "", "Precision used for iterative solving of linear equation systems").addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("precision value", "Precision").setDefaultValueDouble(1e-4).addValidationFunctionDouble(storm::settings::ArgumentValidators::doubleRangeValidatorExcluding(0.0, 1.0)).build()).build()); return true; -}); \ No newline at end of file +}); diff --git a/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h b/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h index a4cd0844a..dbcf4d8b1 100644 --- a/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h +++ b/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h @@ -103,10 +103,10 @@ namespace storm { for (auto state : markovianNonGoalStates) { for (auto& element : aMarkovian.getRow(rowIndex)) { ValueType eTerm = std::exp(-exitRates[state] * delta); - if (element.first == rowIndex) { - element.second = (storm::utility::constantOne<ValueType>() - eTerm) * element.second + eTerm; + if (element.getColumn() == rowIndex) { + element.getValue() = (storm::utility::constantOne<ValueType>() - eTerm) * element.getValue() + eTerm; } else { - element.second = (storm::utility::constantOne<ValueType>() - eTerm) * element.second; + element.getValue() = (storm::utility::constantOne<ValueType>() - eTerm) * element.getValue(); } } ++rowIndex; @@ -116,7 +116,7 @@ namespace storm { rowIndex = 0; for (auto state : markovianNonGoalStates) { for (auto& element : aMarkovianToProbabilistic.getRow(rowIndex)) { - element.second = (1 - std::exp(-exitRates[state] * delta)) * element.second; + element.getValue() = (1 - std::exp(-exitRates[state] * delta)) * element.getValue(); } ++rowIndex; } @@ -133,8 +133,8 @@ namespace storm { bMarkovianFixed.push_back(storm::utility::constantZero<ValueType>()); for (auto& element : transitionMatrix.getRowGroup(state)) { - if (goalStates.get(element.first)) { - bMarkovianFixed.back() += (1 - std::exp(-exitRates[state] * delta)) * element.second; + if (goalStates.get(element.getColumn())) { + bMarkovianFixed.back() += (1 - std::exp(-exitRates[state] * delta)) * element.getValue(); } } } @@ -314,13 +314,13 @@ namespace storm { b.push_back(storm::utility::constantZero<ValueType>()); for (auto element : transitionMatrix.getRow(choice)) { - if (statesNotContainedInAnyMec.get(element.first)) { + if (statesNotContainedInAnyMec.get(element.getColumn())) { // If the target state is not contained in an MEC, we can copy over the entry. - sspMatrixBuilder.addNextValue(currentChoice, statesNotInMecsBeforeIndex[element.first], element.second); + sspMatrixBuilder.addNextValue(currentChoice, statesNotInMecsBeforeIndex[element.getColumn()], element.getValue()); } else { // If the target state is contained in MEC i, we need to add the probability to the corresponding field in the vector // so that we are able to write the cumulative probability to the MEC into the matrix. - auxiliaryStateToProbabilityMap[stateToMecIndexMap[element.first]] += element.second; + auxiliaryStateToProbabilityMap[stateToMecIndexMap[element.getColumn()]] += element.getValue(); } } @@ -350,13 +350,13 @@ namespace storm { b.push_back(storm::utility::constantZero<ValueType>()); for (auto element : transitionMatrix.getRow(choice)) { - if (statesNotContainedInAnyMec.get(element.first)) { + if (statesNotContainedInAnyMec.get(element.getColumn())) { // If the target state is not contained in an MEC, we can copy over the entry. - sspMatrixBuilder.addNextValue(currentChoice, statesNotInMecsBeforeIndex[element.first], element.second); + sspMatrixBuilder.addNextValue(currentChoice, statesNotInMecsBeforeIndex[element.getColumn()], element.getValue()); } else { // If the target state is contained in MEC i, we need to add the probability to the corresponding field in the vector // so that we are able to write the cumulative probability to the MEC into the matrix. - auxiliaryStateToProbabilityMap[stateToMecIndexMap[element.first]] += element.second; + auxiliaryStateToProbabilityMap[stateToMecIndexMap[element.getColumn()]] += element.getValue(); } } @@ -428,61 +428,61 @@ namespace storm { */ static ValueType computeLraForMaximalEndComponent(bool min, storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<uint_fast64_t> const& nondeterministicChoiceIndices, storm::storage::BitVector const& markovianStates, std::vector<ValueType> const& exitRates, storm::storage::BitVector const& goalStates, storm::storage::MaximalEndComponent const& mec, uint_fast64_t mecIndex = 0) { std::shared_ptr<storm::solver::LpSolver> solver = storm::utility::solver::getLpSolver("LRA for MEC"); - solver->setModelSense(min ? storm::solver::LpSolver::MAXIMIZE : storm::solver::LpSolver::MINIMIZE); + solver->setModelSense(min ? storm::solver::LpSolver::ModelSense::Maximize : storm::solver::LpSolver::ModelSense::Minimize); // First, we need to create the variables for the problem. - std::map<uint_fast64_t, uint_fast64_t> stateToVariableIndexMap; + std::map<uint_fast64_t, std::string> stateToVariableNameMap; for (auto const& stateChoicesPair : mec) { - stateToVariableIndexMap[stateChoicesPair.first] = solver->createContinuousVariable("x" + std::to_string(stateChoicesPair.first), storm::solver::LpSolver::UNBOUNDED, 0, 0, 0); + std::string variableName = "x" + std::to_string(stateChoicesPair.first); + stateToVariableNameMap[stateChoicesPair.first] = variableName; + solver->addUnboundedContinuousVariable(variableName); } - uint_fast64_t lraValueVariableIndex = solver->createContinuousVariable("k", storm::solver::LpSolver::UNBOUNDED, 0, 0, 1); + solver->addUnboundedContinuousVariable("k", 1); solver->update(); // Now we encode the problem as constraints. - std::vector<uint_fast64_t> variables; - std::vector<double> coefficients; for (auto const& stateChoicesPair : mec) { uint_fast64_t state = stateChoicesPair.first; // Now, based on the type of the state, create a suitable constraint. if (markovianStates.get(state)) { - variables.clear(); - coefficients.clear(); - - variables.push_back(stateToVariableIndexMap.at(state)); - coefficients.push_back(1); + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(stateToVariableNameMap.at(state)); for (auto element : transitionMatrix.getRow(nondeterministicChoiceIndices[state])) { - variables.push_back(stateToVariableIndexMap.at(element.first)); - coefficients.push_back(-element.second); + constraint = constraint - storm::expressions::Expression::createDoubleVariable(stateToVariableNameMap.at(element.getColumn())); } - variables.push_back(lraValueVariableIndex); - coefficients.push_back(storm::utility::constantOne<ValueType>() / exitRates[state]); - - solver->addConstraint("state" + std::to_string(state), variables, coefficients, min ? storm::solver::LpSolver::LESS_EQUAL : storm::solver::LpSolver::GREATER_EQUAL, goalStates.get(state) ? storm::utility::constantOne<ValueType>() / exitRates[state] : storm::utility::constantZero<ValueType>()); + constraint = constraint + storm::expressions::Expression::createDoubleLiteral(storm::utility::constantOne<ValueType>() / exitRates[state]) * storm::expressions::Expression::createDoubleVariable("k"); + storm::expressions::Expression rightHandSide = goalStates.get(state) ? storm::expressions::Expression::createDoubleLiteral(storm::utility::constantOne<ValueType>() / exitRates[state]) : storm::expressions::Expression::createDoubleLiteral(storm::utility::constantZero<ValueType>()); + if (min) { + constraint = constraint <= rightHandSide; + } else { + constraint = constraint >= rightHandSide; + } + solver->addConstraint("state" + std::to_string(state), constraint); } else { // For probabilistic states, we want to add the constraint x_s <= sum P(s, a, s') * x_s' where a is the current action // and the sum ranges over all states s'. for (auto choice : stateChoicesPair.second) { - variables.clear(); - coefficients.clear(); - - variables.push_back(stateToVariableIndexMap.at(state)); - coefficients.push_back(1); - + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(stateToVariableNameMap.at(state)); + for (auto element : transitionMatrix.getRow(choice)) { - variables.push_back(stateToVariableIndexMap.at(element.first)); - coefficients.push_back(-element.second); + constraint = constraint - storm::expressions::Expression::createDoubleVariable(stateToVariableNameMap.at(element.getColumn())); } - solver->addConstraint("state" + std::to_string(state), variables, coefficients, min ? storm::solver::LpSolver::LESS_EQUAL : storm::solver::LpSolver::GREATER_EQUAL, storm::utility::constantZero<ValueType>()); + storm::expressions::Expression rightHandSide = storm::expressions::Expression::createDoubleLiteral(storm::utility::constantZero<ValueType>()); + if (min) { + constraint = constraint <= rightHandSide; + } else { + constraint = constraint >= rightHandSide; + } + solver->addConstraint("state" + std::to_string(state), constraint); } } } solver->optimize(); - return solver->getContinuousValue(lraValueVariableIndex); + return solver->getContinuousValue("k"); } /*! diff --git a/src/models/AbstractDeterministicModel.h b/src/models/AbstractDeterministicModel.h index c72b914de..88e0d1006 100644 --- a/src/models/AbstractDeterministicModel.h +++ b/src/models/AbstractDeterministicModel.h @@ -87,9 +87,9 @@ class AbstractDeterministicModel: public AbstractModel<T> { for (uint_fast64_t i = 0; i < this->transitionMatrix.getRowCount(); ++i, ++rowIt) { typename storm::storage::SparseMatrix<T>::const_rows row = this->transitionMatrix.getRow(i); for (auto const& transition : row) { - if (transition.second != storm::utility::constantZero<T>()) { - if (subsystem == nullptr || subsystem->get(transition.first)) { - outStream << "\t" << i << " -> " << transition.first << " [ label= \"" << transition.second << "\" ];" << std::endl; + if (transition.getValue() != storm::utility::constantZero<T>()) { + if (subsystem == nullptr || subsystem->get(transition.getColumn())) { + outStream << "\t" << i << " -> " << transition.getColumn() << " [ label= \"" << transition.getValue() << "\" ];" << std::endl; } } } diff --git a/src/models/AbstractModel.h b/src/models/AbstractModel.h index 301c806d3..05296c584 100644 --- a/src/models/AbstractModel.h +++ b/src/models/AbstractModel.h @@ -220,7 +220,7 @@ class AbstractModel: public std::enable_shared_from_this<AbstractModel<T>> { * @return The number of (non-zero) transitions of the model. */ virtual uint_fast64_t getNumberOfTransitions() const { - return this->getTransitionMatrix().getEntryCount(); + return this->getTransitionMatrix().getNonzeroEntryCount(); } /*! diff --git a/src/models/AbstractNondeterministicModel.h b/src/models/AbstractNondeterministicModel.h index 0058377a6..3a738cbe6 100644 --- a/src/models/AbstractNondeterministicModel.h +++ b/src/models/AbstractNondeterministicModel.h @@ -189,8 +189,8 @@ namespace storm { // Now draw all probabilitic arcs that belong to this nondeterminstic choice. for (auto const& transition : row) { - if (subsystem == nullptr || subsystem->get(transition.first)) { - outStream << "\t\"" << state << "c" << choice << "\" -> " << transition.first << " [ label= \"" << transition.second << "\" ]"; + if (subsystem == nullptr || subsystem->get(transition.getColumn())) { + outStream << "\t\"" << state << "c" << choice << "\" -> " << transition.getColumn() << " [ label= \"" << transition.getValue() << "\" ]"; // If we were given a scheduler to highlight, we do so now. if (scheduler != nullptr) { diff --git a/src/models/Dtmc.h b/src/models/Dtmc.h index b599774f9..eb0e669dd 100644 --- a/src/models/Dtmc.h +++ b/src/models/Dtmc.h @@ -170,7 +170,7 @@ public: for(uint_fast64_t row = 0; row < origMat.getRowCount(); ++row) { if(subSysStates.get(row)){ for(auto const& entry : origMat.getRow(row)) { - if(subSysStates.get(entry.first)) { + if(subSysStates.get(entry.getColumn())) { subSysTransitionCount++; } } @@ -198,10 +198,10 @@ public: if(subSysStates.get(row)){ // Transfer transitions for(auto& entry : origMat.getRow(row)) { - if(subSysStates.get(entry.first)) { - newMatBuilder.addNextValue(newRow, stateMapping[entry.first], entry.second); + if(subSysStates.get(entry.getColumn())) { + newMatBuilder.addNextValue(newRow, stateMapping[entry.getColumn()], entry.getValue()); } else { - rest += entry.second; + rest += entry.getValue(); } } @@ -251,8 +251,8 @@ public: if(subSysStates.get(row)){ // Transfer transition rewards for(auto& entry : this->getTransitionRewardMatrix().getRow(row)) { - if(subSysStates.get(entry.first)) { - newTransRewardsBuilder.addNextValue(newRow, stateMapping[entry.first], entry.second); + if(subSysStates.get(entry.getColumn())) { + newTransRewardsBuilder.addNextValue(newRow, stateMapping[entry.getColumn()], entry.getValue()); } } diff --git a/src/models/MarkovAutomaton.h b/src/models/MarkovAutomaton.h index 22b6ab13f..89ce7ccee 100644 --- a/src/models/MarkovAutomaton.h +++ b/src/models/MarkovAutomaton.h @@ -147,7 +147,7 @@ namespace storm { for (uint_fast64_t row = this->getTransitionMatrix().getRowGroupIndices()[state] + (this->isHybridState(state) ? 1 : 0); row < this->getTransitionMatrix().getRowGroupIndices()[state + 1]; ++row) { for (auto const& entry : this->transitionMatrix.getRow(row)) { - newTransitionMatrixBuilder.addNextValue(currentChoice, entry.first, entry.second); + newTransitionMatrixBuilder.addNextValue(currentChoice, entry.getColumn(), entry.getValue()); } ++currentChoice; } @@ -220,8 +220,8 @@ namespace storm { // Now draw all probabilitic arcs that belong to this nondeterminstic choice. for (auto const& transition : row) { - if (subsystem == nullptr || subsystem->get(transition.first)) { - outStream << "\t\"" << state << "c" << choice << "\" -> " << transition.first << " [ label= \"" << transition.second << "\" ]"; + if (subsystem == nullptr || subsystem->get(transition.getColumn())) { + outStream << "\t\"" << state << "c" << choice << "\" -> " << transition.getColumn() << " [ label= \"" << transition.getValue() << "\" ]"; // If we were given a scheduler to highlight, we do so now. if (scheduler != nullptr) { @@ -237,8 +237,8 @@ namespace storm { } else { // In this case we are emitting a Markovian choice, so draw the arrows directly to the target states. for (auto const& transition : row) { - if (subsystem == nullptr || subsystem->get(transition.first)) { - outStream << "\t\"" << state << "\" -> " << transition.first << " [ label= \"" << transition.second << " (" << this->exitRates[state] << ")\" ]"; + if (subsystem == nullptr || subsystem->get(transition.getColumn())) { + outStream << "\t\"" << state << "\" -> " << transition.getColumn() << " [ label= \"" << transition.getValue() << " (" << this->exitRates[state] << ")\" ]"; } } } @@ -259,7 +259,7 @@ namespace storm { void turnRatesToProbabilities() { for (auto state : this->markovianStates) { for (auto& transition : this->transitionMatrix.getRowGroup(state)) { - transition.second /= this->exitRates[state]; + transition.getValue() /= this->exitRates[state]; } } } diff --git a/src/solver/GlpkLpSolver.cpp b/src/solver/GlpkLpSolver.cpp index e122fcb16..2a9eec7f6 100644 --- a/src/solver/GlpkLpSolver.cpp +++ b/src/solver/GlpkLpSolver.cpp @@ -4,13 +4,12 @@ #include <iostream> -#include "src/exceptions/InvalidStateException.h" -#include "src/settings/Settings.h" - -#include "log4cplus/logger.h" -#include "log4cplus/loggingmacros.h" +#include "src/storage/expressions/LinearCoefficientVisitor.h" -extern log4cplus::Logger logger; +#include "src/settings/Settings.h" +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/InvalidAccessException.h" +#include "src/exceptions/InvalidStateException.h" bool GlpkLpSolverOptionsRegistered = storm::settings::Settings::registerNewModule([] (storm::settings::Settings* instance) -> bool { instance->addOption(storm::settings::OptionBuilder("GlpkLpSolver", "glpkoutput", "", "If set, the glpk output will be printed to the command line.").build()); @@ -22,7 +21,7 @@ bool GlpkLpSolverOptionsRegistered = storm::settings::Settings::registerNewModul namespace storm { namespace solver { - GlpkLpSolver::GlpkLpSolver(std::string const& name, ModelSense const& modelSense) : LpSolver(modelSense), lp(nullptr), nextVariableIndex(1), nextConstraintIndex(1), modelContainsIntegerVariables(false), isInfeasibleFlag(false), isUnboundedFlag(false), rowIndices(), columnIndices(), coefficientValues() { + GlpkLpSolver::GlpkLpSolver(std::string const& name, ModelSense const& modelSense) : LpSolver(modelSense), lp(nullptr), variableNameToIndexMap(), nextVariableIndex(1), nextConstraintIndex(1), modelContainsIntegerVariables(false), isInfeasibleFlag(false), isUnboundedFlag(false), rowIndices(), columnIndices(), coefficientValues() { // Create the LP problem for glpk. lp = glp_create_prob(); @@ -38,11 +37,11 @@ namespace storm { coefficientValues.push_back(0); } - GlpkLpSolver::GlpkLpSolver(std::string const& name) : GlpkLpSolver(name, MINIMIZE) { + GlpkLpSolver::GlpkLpSolver(std::string const& name) : GlpkLpSolver(name, ModelSense::Minimize) { // Intentionally left empty. } - GlpkLpSolver::GlpkLpSolver() : GlpkLpSolver("", MINIMIZE) { + GlpkLpSolver::GlpkLpSolver() : GlpkLpSolver("", ModelSense::Minimize) { // Intentionally left empty. } @@ -56,76 +55,120 @@ namespace storm { glp_free_env(); } - uint_fast64_t GlpkLpSolver::createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { - glp_add_cols(this->lp, 1); - glp_set_col_name(this->lp, nextVariableIndex, name.c_str()); - switch (variableType) { - case LpSolver::BOUNDED: - glp_set_col_bnds(lp, nextVariableIndex, GLP_DB, lowerBound, upperBound); - break; - case LpSolver::UNBOUNDED: - glp_set_col_bnds(lp, nextVariableIndex, GLP_FR, 0, 0); - break; - case LpSolver::UPPER_BOUND: - glp_set_col_bnds(lp, nextVariableIndex, GLP_UP, 0, upperBound); - break; - case LpSolver::LOWER_BOUND: - glp_set_col_bnds(lp, nextVariableIndex, GLP_LO, lowerBound, 0); - break; - } - glp_set_col_kind(this->lp, nextVariableIndex, GLP_CV); - glp_set_obj_coef(this->lp, nextVariableIndex, objectiveFunctionCoefficient); - ++nextVariableIndex; - - this->currentModelHasBeenOptimized = false; - return nextVariableIndex - 1; + void GlpkLpSolver::addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GLP_CV, GLP_DB, lowerBound, upperBound, objectiveFunctionCoefficient); + } + + void GlpkLpSolver::addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GLP_CV, GLP_LO, lowerBound, 0, objectiveFunctionCoefficient); + } + + void GlpkLpSolver::addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GLP_CV, GLP_UP, 0, upperBound, objectiveFunctionCoefficient); } - uint_fast64_t GlpkLpSolver::createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { - uint_fast64_t index = this->createContinuousVariable(name, variableType, lowerBound, upperBound, objectiveFunctionCoefficient); - glp_set_col_kind(this->lp, index, GLP_IV); + void GlpkLpSolver::addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient) { + this->addVariable(name, GLP_CV, GLP_FR, 0, 0, objectiveFunctionCoefficient); + } + + void GlpkLpSolver::addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GLP_IV, GLP_DB, lowerBound, upperBound, objectiveFunctionCoefficient); + this->modelContainsIntegerVariables = true; + } + + void GlpkLpSolver::addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GLP_IV, GLP_LO, lowerBound, 0, objectiveFunctionCoefficient); + this->modelContainsIntegerVariables = true; + } + + void GlpkLpSolver::addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GLP_IV, GLP_UP, 0, upperBound, objectiveFunctionCoefficient); + this->modelContainsIntegerVariables = true; + } + + void GlpkLpSolver::addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient) { + this->addVariable(name, GLP_IV, GLP_FR, 0, 0, objectiveFunctionCoefficient); this->modelContainsIntegerVariables = true; - return index; } - uint_fast64_t GlpkLpSolver::createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) { - uint_fast64_t index = this->createContinuousVariable(name, UNBOUNDED, 0, 1, objectiveFunctionCoefficient); - glp_set_col_kind(this->lp, index, GLP_BV); + void GlpkLpSolver::addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) { + this->addVariable(name, GLP_BV, GLP_FR, 0, 0, objectiveFunctionCoefficient); this->modelContainsIntegerVariables = true; - return index; + } + + void GlpkLpSolver::addVariable(std::string const& name, int variableType, int boundType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { + // Check whether variable already exists. + auto nameIndexPair = this->variableNameToIndexMap.find(name); + LOG_THROW(nameIndexPair == this->variableNameToIndexMap.end(), storm::exceptions::InvalidArgumentException, "Variable '" << nameIndexPair->first << "' already exists."); + + // Check for valid variable type. + LOG_ASSERT(variableType == GLP_CV || variableType == GLP_IV || variableType == GLP_BV, "Illegal type '" << variableType << "' for glpk variable."); + + // Check for valid bound type. + LOG_ASSERT(boundType == GLP_FR || boundType == GLP_UP || boundType == GLP_LO || boundType == GLP_DB, "Illegal bound type for variable '" << name << "'."); + + // Finally, create the actual variable. + glp_add_cols(this->lp, 1); + glp_set_col_name(this->lp, nextVariableIndex, name.c_str()); + glp_set_col_bnds(lp, nextVariableIndex, boundType, lowerBound, upperBound); + glp_set_col_kind(this->lp, nextVariableIndex, variableType); + glp_set_obj_coef(this->lp, nextVariableIndex, objectiveFunctionCoefficient); + this->variableNameToIndexMap.emplace(name, this->nextVariableIndex); + ++this->nextVariableIndex; } void GlpkLpSolver::update() const { // Intentionally left empty. } - void GlpkLpSolver::addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rightHandSideValue) { - if (variables.size() != coefficients.size()) { - LOG4CPLUS_ERROR(logger, "Sizes of variable indices vector and coefficients vector do not match."); - throw storm::exceptions::InvalidStateException() << "Sizes of variable indices vector and coefficients vector do not match."; - } - + void GlpkLpSolver::addConstraint(std::string const& name, storm::expressions::Expression const& constraint) { // Add the row that will represent this constraint. glp_add_rows(this->lp, 1); glp_set_row_name(this->lp, nextConstraintIndex, name.c_str()); + LOG_THROW(constraint.isRelationalExpression(), storm::exceptions::InvalidArgumentException, "Illegal constraint is not a relational expression."); + LOG_THROW(constraint.getOperator() != storm::expressions::OperatorType::NotEqual, storm::exceptions::InvalidArgumentException, "Illegal constraint uses inequality operator."); + + std::pair<storm::expressions::SimpleValuation, double> leftCoefficients = storm::expressions::LinearCoefficientVisitor().getLinearCoefficients(constraint.getOperand(0)); + std::pair<storm::expressions::SimpleValuation, double> rightCoefficients = storm::expressions::LinearCoefficientVisitor().getLinearCoefficients(constraint.getOperand(1)); + for (auto const& identifier : rightCoefficients.first.getDoubleIdentifiers()) { + if (leftCoefficients.first.containsDoubleIdentifier(identifier)) { + leftCoefficients.first.setDoubleValue(identifier, leftCoefficients.first.getDoubleValue(identifier) - rightCoefficients.first.getDoubleValue(identifier)); + } else { + leftCoefficients.first.addDoubleIdentifier(identifier, -rightCoefficients.first.getDoubleValue(identifier)); + } + } + rightCoefficients.second -= leftCoefficients.second; + + // Now we need to transform the coefficients to the vector representation. + std::vector<int> variables; + std::vector<double> coefficients; + for (auto const& identifier : leftCoefficients.first.getDoubleIdentifiers()) { + auto identifierIndexPair = this->variableNameToIndexMap.find(identifier); + LOG_THROW(identifierIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidArgumentException, "Constraint contains illegal identifier '" << identifier << "'."); + variables.push_back(identifierIndexPair->second); + coefficients.push_back(leftCoefficients.first.getDoubleValue(identifier)); + } + // Determine the type of the constraint and add it properly. - switch (boundType) { - case LESS: - glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_UP, 0, rightHandSideValue - storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); + switch (constraint.getOperator()) { + case storm::expressions::OperatorType::Less: + glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_UP, 0, rightCoefficients.second - storm::settings::Settings::getInstance()->getOptionByLongName("glpkinttol").getArgument(0).getValueAsDouble()); break; - case LESS_EQUAL: - glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_UP, 0, rightHandSideValue); + case storm::expressions::OperatorType::LessOrEqual: + glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_UP, 0, rightCoefficients.second); break; - case GREATER: - glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_LO, rightHandSideValue + storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble(), 0); + case storm::expressions::OperatorType::Greater: + glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_LO, rightCoefficients.second + storm::settings::Settings::getInstance()->getOptionByLongName("glpkinttol").getArgument(0).getValueAsDouble(), 0); break; - case GREATER_EQUAL: - glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_LO, rightHandSideValue, 0); + case storm::expressions::OperatorType::GreaterOrEqual: + glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_LO, rightCoefficients.second, 0); break; - case EQUAL: - glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_FX, rightHandSideValue, rightHandSideValue); + case storm::expressions::OperatorType::Equal: + glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_FX, rightCoefficients.second, rightCoefficients.second); break; + default: + LOG_ASSERT(false, "Illegal operator in LP solver constraint."); } // Record the variables and coefficients in the coefficient matrix. @@ -143,13 +186,13 @@ namespace storm { this->isUnboundedFlag = false; // Start by setting the model sense. - glp_set_obj_dir(this->lp, this->getModelSense() == MINIMIZE ? GLP_MIN : GLP_MAX); + glp_set_obj_dir(this->lp, this->getModelSense() == LpSolver::ModelSense::Minimize ? GLP_MIN : GLP_MAX); glp_load_matrix(this->lp, rowIndices.size() - 1, rowIndices.data(), columnIndices.data(), coefficientValues.data()); int error = 0; if (this->modelContainsIntegerVariables) { - glp_iocp* parameters = new glp_iocp; + glp_iocp* parameters = new glp_iocp(); glp_init_iocp(parameters); parameters->presolve = GLP_ON; parameters->tol_int = storm::settings::Settings::getInstance()->getOptionByLongName("glpkinttol").getArgument(0).getValueAsDouble(); @@ -171,11 +214,7 @@ namespace storm { error = glp_simplex(this->lp, nullptr); } - if (error != 0) { - LOG4CPLUS_ERROR(logger, "Unable to optimize glpk model (" << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to optimize glpk model (" << error << ")."; - } - + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to optimize glpk model (" << error << ")."); this->currentModelHasBeenOptimized = true; } @@ -217,103 +256,75 @@ namespace storm { return status == GLP_OPT; } - int_fast64_t GlpkLpSolver::getIntegerValue(uint_fast64_t variableIndex) const { + double GlpkLpSolver::getContinuousValue(std::string const& name) const { if (!this->isOptimal()) { - if (this->isInfeasible()) { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from infeasible model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model."; - } else if (this->isUnbounded()) { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unbounded model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model."; - } else { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unoptimized model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model."; - } + LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get glpk solution from infeasible model."); + LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unbounded model."); + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unoptimized model."); } - + + auto variableIndexPair = this->variableNameToIndexMap.find(name); + LOG_THROW(variableIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidAccessException, "Accessing value of unknown variable '" << name << "'."); + double value = 0; if (this->modelContainsIntegerVariables) { - value = glp_mip_col_val(this->lp, static_cast<int>(variableIndex)); + value = glp_mip_col_val(this->lp, static_cast<int>(variableIndexPair->second)); } else { - value = glp_get_col_prim(this->lp, static_cast<int>(variableIndex)); - } - - if (std::abs(value - static_cast<int>(value)) <= storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()) { - // Nothing to do in this case. - } else if (std::abs(value) > storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()) { - LOG4CPLUS_ERROR(logger, "Illegal value for integer variable in glpk solution (" << value << ")."); - throw storm::exceptions::InvalidStateException() << "Illegal value for integer variable in glpk solution (" << value << ")."; + value = glp_get_col_prim(this->lp, static_cast<int>(variableIndexPair->second)); } - - return static_cast<int_fast64_t>(value); + return value; } - bool GlpkLpSolver::getBinaryValue(uint_fast64_t variableIndex) const { + int_fast64_t GlpkLpSolver::getIntegerValue(std::string const& name) const { if (!this->isOptimal()) { - if (this->isInfeasible()) { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from infeasible model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model."; - } else if (this->isUnbounded()) { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unbounded model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model."; - } else { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unoptimized model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model."; - } + LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get glpk solution from infeasible model."); + LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unbounded model."); + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unoptimized model."); } + + auto variableIndexPair = this->variableNameToIndexMap.find(name); + LOG_THROW(variableIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidAccessException, "Accessing value of unknown variable '" << name << "'."); double value = 0; if (this->modelContainsIntegerVariables) { - value = glp_mip_col_val(this->lp, static_cast<int>(variableIndex)); + value = glp_mip_col_val(this->lp, static_cast<int>(variableIndexPair->second)); } else { - value = glp_get_col_prim(this->lp, static_cast<int>(variableIndex)); + value = glp_get_col_prim(this->lp, static_cast<int>(variableIndexPair->second)); } - if (std::abs(value - 1) <= storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()) { - // Nothing to do in this case. - } else if (std::abs(value) > storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()) { - LOG4CPLUS_ERROR(logger, "Illegal value for binary variable in Gurobi solution (" << value << ")."); - throw storm::exceptions::InvalidStateException() << "Illegal value for binary variable in Gurobi solution (" << value << ")."; - } + // Now check the desired precision was actually achieved. + LOG_THROW(std::abs(static_cast<int>(value) - value) <= storm::settings::Settings::getInstance()->getOptionByLongName("glpkinttol").getArgument(0).getValueAsDouble(), storm::exceptions::InvalidStateException, "Illegal value for integer variable in glpk solution (" << value << ")."); - return static_cast<bool>(value); + return static_cast<int_fast64_t>(value); } - double GlpkLpSolver::getContinuousValue(uint_fast64_t variableIndex) const { + bool GlpkLpSolver::getBinaryValue(std::string const& name) const { if (!this->isOptimal()) { - if (this->isInfeasible()) { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from infeasible model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model."; - } else if (this->isUnbounded()) { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unbounded model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model."; - } else { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unoptimized model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model."; - } + LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get glpk solution from infeasible model."); + LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unbounded model."); + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unoptimized model."); } + auto variableIndexPair = this->variableNameToIndexMap.find(name); + LOG_THROW(variableIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidAccessException, "Accessing value of unknown variable '" << name << "'."); + double value = 0; if (this->modelContainsIntegerVariables) { - value = glp_mip_col_val(this->lp, static_cast<int>(variableIndex)); + value = glp_mip_col_val(this->lp, static_cast<int>(variableIndexPair->second)); } else { - value = glp_get_col_prim(this->lp, static_cast<int>(variableIndex)); + value = glp_get_col_prim(this->lp, static_cast<int>(variableIndexPair->second)); } - return value; + + LOG_THROW(std::abs(static_cast<int>(value) - value) <= storm::settings::Settings::getInstance()->getOptionByLongName("glpkinttol").getArgument(0).getValueAsDouble(), storm::exceptions::InvalidStateException, "Illegal value for binary variable in glpk solution (" << value << ")."); + + return static_cast<bool>(value); } double GlpkLpSolver::getObjectiveValue() const { if (!this->isOptimal()) { - if (this->isInfeasible()) { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from infeasible model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model."; - } else if (this->isUnbounded()) { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unbounded model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model."; - } else { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unoptimized model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model."; - } + LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get glpk solution from infeasible model."); + LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unbounded model."); + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unoptimized model."); } double value = 0; diff --git a/src/solver/GlpkLpSolver.h b/src/solver/GlpkLpSolver.h index 2bc2bf397..0931e1ca5 100644 --- a/src/solver/GlpkLpSolver.h +++ b/src/solver/GlpkLpSolver.h @@ -14,7 +14,6 @@ namespace storm { namespace solver { #ifdef STORM_HAVE_GLPK - /*! * A class that implements the LpSolver interface using glpk as the background solver. */ @@ -56,34 +55,66 @@ namespace storm { */ virtual ~GlpkLpSolver(); - virtual uint_fast64_t createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override; - virtual uint_fast64_t createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override; - virtual uint_fast64_t createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) override; + // Methods to add continuous variables. + virtual void addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override; + virtual void addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override; + virtual void addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override; + virtual void addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override; + + // Methods to add integer variables. + virtual void addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override; + virtual void addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override; + virtual void addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override; + virtual void addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override; + + // Methods to add binary variables. + virtual void addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override; + + // Methods to incorporate recent changes. virtual void update() const override; - virtual void addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rightHandSideValue) override; + // Methods to add constraints + virtual void addConstraint(std::string const& name, storm::expressions::Expression const& constraint) override; + // Methods to optimize and retrieve optimality status. virtual void optimize() const override; virtual bool isInfeasible() const override; virtual bool isUnbounded() const override; virtual bool isOptimal() const override; - virtual int_fast64_t getIntegerValue(uint_fast64_t variableIndex) const override; - virtual bool getBinaryValue(uint_fast64_t variableIndex) const override; - virtual double getContinuousValue(uint_fast64_t variableIndex) const override; + // Methods to retrieve values of variables and the objective function in the optimal solutions. + virtual double getContinuousValue(std::string const& name) const override; + virtual int_fast64_t getIntegerValue(std::string const& name) const override; + virtual bool getBinaryValue(std::string const& name) const override; virtual double getObjectiveValue() const override; + // Methods to print the LP problem to a file. virtual void writeModelToFile(std::string const& filename) const override; private: + /*! + * Adds a variable with the given name, type, lower and upper bound and objective function coefficient. + * + * @param name The name of the variable. + * @param variableType The type of the variable in terms of glpk's constants. + * @param boundType A glpk flag indicating which bounds apply to the variable. + * @param lowerBound The lower bound of the range of the variable. + * @param upperBound The upper bound of the range of the variable. + * @param objectiveFunctionCoefficient The coefficient of the variable in the objective function. + */ + void addVariable(std::string const& name, int variableType, int boundType, double lowerBound, double upperBound, double objectiveFunctionCoefficient); + // The glpk LP problem. glp_prob* lp; - // A counter that keeps track of the next free variable index. - uint_fast64_t nextVariableIndex; + // A mapping from variable names to their indices. + std::map<std::string, int> variableNameToIndexMap; + + // A counter used for getting the next variable index. + int nextVariableIndex; - // A counter that keeps track of the next free constraint index. - uint_fast64_t nextConstraintIndex; + // A counter used for getting the next constraint index. + int nextConstraintIndex; // A flag storing whether the model is an LP or an MILP. bool modelContainsIntegerVariables; @@ -121,15 +152,39 @@ namespace storm { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } - virtual uint_fast64_t createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override { + virtual void addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; + } + + virtual void addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; + } + + virtual void addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; + } + + virtual void addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; + } + + virtual void addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; + } + + virtual void addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; + } + + virtual void addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } - virtual uint_fast64_t createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override { + virtual void addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } - virtual uint_fast64_t createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) override { + virtual uint_fast64_t addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } @@ -137,14 +192,14 @@ namespace storm { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } - virtual void addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rightHandSideValue) override { + virtual void addConstraint(std::string const& name, storm::expressions::Expression const& constraint) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } - + virtual void optimize() const override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } - + virtual bool isInfeasible() const override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } @@ -157,15 +212,15 @@ namespace storm { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } - virtual int_fast64_t getIntegerValue(uint_fast64_t variableIndex) const override { + virtual double getContinuousValue(std::string const& name) const override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } - virtual bool getBinaryValue(uint_fast64_t variableIndex) const override { + virtual int_fast64_t getIntegerValue(std::string const& name) const override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } - virtual double getContinuousValue(uint_fast64_t variableIndex) const override { + virtual bool getBinaryValue(std::string const& name) const override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } diff --git a/src/solver/GurobiLpSolver.cpp b/src/solver/GurobiLpSolver.cpp index 75a0d5a7e..676867ecb 100644 --- a/src/solver/GurobiLpSolver.cpp +++ b/src/solver/GurobiLpSolver.cpp @@ -3,13 +3,12 @@ #ifdef STORM_HAVE_GUROBI #include <numeric> -#include "src/exceptions/InvalidStateException.h" -#include "src/settings/Settings.h" +#include "src/storage/expressions/LinearCoefficientVisitor.h" -#include "log4cplus/logger.h" -#include "log4cplus/loggingmacros.h" - -extern log4cplus::Logger logger; +#include "src/settings/Settings.h" +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/InvalidStateException.h" +#include "src/exceptions/InvalidAccessException.h" bool GurobiLpSolverOptionsRegistered = storm::settings::Settings::registerNewModule([] (storm::settings::Settings* instance) -> bool { instance->addOption(storm::settings::OptionBuilder("GurobiLpSolver", "gurobithreads", "", "The number of threads that may be used by Gurobi.").addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument("count", "The number of threads.").setDefaultValueUnsignedInteger(1).build()).build()); @@ -43,7 +42,7 @@ namespace storm { } } - GurobiLpSolver::GurobiLpSolver(std::string const& name) : GurobiLpSolver(name, MINIMIZE) { + GurobiLpSolver::GurobiLpSolver(std::string const& name) : GurobiLpSolver(name, ModelSense::Minimize) { // Intentionally left empty. } @@ -51,7 +50,7 @@ namespace storm { // Intentionally left empty. } - GurobiLpSolver::GurobiLpSolver() : GurobiLpSolver("", MINIMIZE) { + GurobiLpSolver::GurobiLpSolver() : GurobiLpSolver("", ModelSense::Minimize) { // Intentionally left empty. } @@ -66,145 +65,124 @@ namespace storm { // Enable the following line to only print the output of Gurobi if the debug flag is set. error = GRBsetintparam(env, "OutputFlag", storm::settings::Settings::getInstance()->isSet("debug") || storm::settings::Settings::getInstance()->isSet("gurobioutput") ? 1 : 0); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to set Gurobi Parameter OutputFlag (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi Parameter OutputFlag (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi Parameter OutputFlag (" << GRBgeterrormsg(env) << ", error code " << error << ")."); // Enable the following line to restrict Gurobi to one thread only. error = GRBsetintparam(env, "Threads", storm::settings::Settings::getInstance()->getOptionByLongName("gurobithreads").getArgument(0).getValueAsUnsignedInteger()); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to set Gurobi Parameter Threads (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi Parameter Threads (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi Parameter Threads (" << GRBgeterrormsg(env) << ", error code " << error << ")."); // Enable the following line to force Gurobi to be as precise about the binary variables as required by the given precision option. error = GRBsetdblparam(env, "IntFeasTol", storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble()); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to set Gurobi Parameter IntFeasTol (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi Parameter IntFeasTol (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi Parameter IntFeasTol (" << GRBgeterrormsg(env) << ", error code " << error << ")."); } void GurobiLpSolver::update() const { int error = GRBupdatemodel(model); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to update Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to update Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to update Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); // Since the model changed, we erase the optimality flag. this->currentModelHasBeenOptimized = false; } - uint_fast64_t GurobiLpSolver::createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { - int error = 0; - switch (variableType) { - case LpSolver::BOUNDED: - error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, lowerBound, upperBound, GRB_CONTINUOUS, name.c_str()); - break; - case LpSolver::UNBOUNDED: - error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, -GRB_INFINITY, GRB_INFINITY, GRB_CONTINUOUS, name.c_str()); - break; - case LpSolver::UPPER_BOUND: - error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, -GRB_INFINITY, upperBound, GRB_CONTINUOUS, name.c_str()); - break; - case LpSolver::LOWER_BOUND: - error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, lowerBound, GRB_INFINITY, GRB_CONTINUOUS, name.c_str()); - break; - } - - if (error) { - LOG4CPLUS_ERROR(logger, "Could not create binary Gurobi variable (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Could not create binary Gurobi variable (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } - ++nextVariableIndex; - return nextVariableIndex - 1; + void GurobiLpSolver::addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GRB_CONTINUOUS, lowerBound, upperBound, objectiveFunctionCoefficient); } - uint_fast64_t GurobiLpSolver::createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { - int error = 0; - switch (variableType) { - case LpSolver::BOUNDED: - error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, lowerBound, upperBound, GRB_INTEGER, name.c_str()); - break; - case LpSolver::UNBOUNDED: - error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, -GRB_INFINITY, GRB_INFINITY, GRB_INTEGER, name.c_str()); - break; - case LpSolver::UPPER_BOUND: - error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, -GRB_INFINITY, upperBound, GRB_INTEGER, name.c_str()); - break; - case LpSolver::LOWER_BOUND: - error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, lowerBound, GRB_INFINITY, GRB_INTEGER, name.c_str()); - break; - } + void GurobiLpSolver::addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GRB_CONTINUOUS, lowerBound, GRB_INFINITY, objectiveFunctionCoefficient); + } - if (error) { - LOG4CPLUS_ERROR(logger, "Could not create binary Gurobi variable (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Could not create binary Gurobi variable (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } - ++nextVariableIndex; - return nextVariableIndex - 1; + void GurobiLpSolver::addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GRB_CONTINUOUS, -GRB_INFINITY, upperBound, objectiveFunctionCoefficient); + } + + void GurobiLpSolver::addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient) { + this->addVariable(name, GRB_CONTINUOUS, -GRB_INFINITY, GRB_INFINITY, objectiveFunctionCoefficient); } - uint_fast64_t GurobiLpSolver::createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) { - int error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, 0.0, 1.0, GRB_BINARY, name.c_str()); - if (error) { - LOG4CPLUS_ERROR(logger, "Could not create binary Gurobi variable (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Could not create binary Gurobi variable (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } - ++nextVariableIndex; - return nextVariableIndex - 1; + void GurobiLpSolver::addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GRB_INTEGER, lowerBound, upperBound, objectiveFunctionCoefficient); } - void GurobiLpSolver::addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rightHandSideValue) { - if (variables.size() != coefficients.size()) { - LOG4CPLUS_ERROR(logger, "Sizes of variable indices vector and coefficients vector do not match."); - throw storm::exceptions::InvalidStateException() << "Sizes of variable indices vector and coefficients vector do not match."; - } + void GurobiLpSolver::addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GRB_INTEGER, lowerBound, GRB_INFINITY, objectiveFunctionCoefficient); + } + + void GurobiLpSolver::addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GRB_INTEGER, -GRB_INFINITY, upperBound, objectiveFunctionCoefficient); + } + + void GurobiLpSolver::addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient) { + this->addVariable(name, GRB_INTEGER, -GRB_INFINITY, GRB_INFINITY, objectiveFunctionCoefficient); + } + + void GurobiLpSolver::addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) { + this->addVariable(name, GRB_BINARY, 0, 1, objectiveFunctionCoefficient); + } + + void GurobiLpSolver::addVariable(std::string const& name, char variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { + // Check whether variable already exists. + auto nameIndexPair = this->variableNameToIndexMap.find(name); + LOG_THROW(nameIndexPair == this->variableNameToIndexMap.end(), storm::exceptions::InvalidArgumentException, "Variable '" << nameIndexPair->first << "' already exists."); - // As Gurobi requires the indices to be unique, we now explicitly make them unique. For this, we sort the - // variables and coefficients and eliminated duplicates by adding the coefficients. + // Check for valid variable type. + LOG_ASSERT(variableType == GRB_CONTINUOUS || variableType == GRB_INTEGER || variableType == GRB_BINARY, "Illegal type '" << variableType << "' for Gurobi variable."); - // We start by sorting both vectors. - std::vector<uint_fast64_t> sortedVariables(variables); - std::vector<double> sortedCoefficients(coefficients); - std::vector<uint_fast64_t> permutation(variables.size()); - std::iota(permutation.begin(), permutation.end(), 0); - std::sort(permutation.begin(), permutation.end(), [&] (uint_fast64_t i, uint_fast64_t j) { return variables[i] < variables[j]; } ); - std::transform(permutation.begin(), permutation.end(), sortedVariables.begin(), [&] (uint_fast64_t i) { return variables[i]; } ); - std::transform(permutation.begin(), permutation.end(), sortedCoefficients.begin(), [&] (uint_fast64_t i) { return coefficients[i]; } ); + // Finally, create the actual variable. + int error = 0; + error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, lowerBound, upperBound, variableType, name.c_str()); + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Could not create binary Gurobi variable (" << GRBgeterrormsg(env) << ", error code " << error << ")."); + this->variableNameToIndexMap.emplace(name, nextVariableIndex); + ++nextVariableIndex; + } + + void GurobiLpSolver::addConstraint(std::string const& name, storm::expressions::Expression const& constraint) { + LOG_THROW(constraint.isRelationalExpression(), storm::exceptions::InvalidArgumentException, "Illegal constraint is not a relational expression."); + LOG_THROW(constraint.getOperator() != storm::expressions::OperatorType::NotEqual, storm::exceptions::InvalidArgumentException, "Illegal constraint uses inequality operator."); - // Now we perform the duplicate elimination step. - std::vector<int> uniqueVariables; - std::vector<double> uniqueCoefficients; - for (uint_fast64_t i = 0; i < sortedVariables.size(); ++i) { - if (!uniqueVariables.empty() && uniqueVariables.back() == sortedVariables[i]) { - uniqueCoefficients.back() += sortedCoefficients[i]; + std::pair<storm::expressions::SimpleValuation, double> leftCoefficients = storm::expressions::LinearCoefficientVisitor().getLinearCoefficients(constraint.getOperand(0)); + std::pair<storm::expressions::SimpleValuation, double> rightCoefficients = storm::expressions::LinearCoefficientVisitor().getLinearCoefficients(constraint.getOperand(1)); + for (auto const& identifier : rightCoefficients.first.getDoubleIdentifiers()) { + if (leftCoefficients.first.containsDoubleIdentifier(identifier)) { + leftCoefficients.first.setDoubleValue(identifier, leftCoefficients.first.getDoubleValue(identifier) - rightCoefficients.first.getDoubleValue(identifier)); } else { - uniqueVariables.push_back(static_cast<int>(sortedVariables[i])); - uniqueCoefficients.push_back(sortedCoefficients[i]); + leftCoefficients.first.addDoubleIdentifier(identifier, -rightCoefficients.first.getDoubleValue(identifier)); } } + rightCoefficients.second -= leftCoefficients.second; - bool strictBound = boundType == LESS || boundType == GREATER; - char sense = boundType == LESS || boundType == LESS_EQUAL ? GRB_LESS_EQUAL : boundType == EQUAL ? GRB_EQUAL : GRB_GREATER_EQUAL; - - // If the constraint enforces a strict bound, we need to do some tweaking of the right-hand side value, because Gurobi only supports - // non-strict bounds. - if (strictBound) { - if (boundType == LESS) { - rightHandSideValue -= storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble(); - } else if (boundType == GREATER) { - rightHandSideValue += storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble(); - } + // Now we need to transform the coefficients to the vector representation. + std::vector<int> variables; + std::vector<double> coefficients; + for (auto const& identifier : leftCoefficients.first.getDoubleIdentifiers()) { + auto identifierIndexPair = this->variableNameToIndexMap.find(identifier); + LOG_THROW(identifierIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidArgumentException, "Constraint contains illegal identifier '" << identifier << "'."); + variables.push_back(identifierIndexPair->second); + coefficients.push_back(leftCoefficients.first.getDoubleValue(identifier)); } - int error = GRBaddconstr(model, uniqueVariables.size(), uniqueVariables.data(), uniqueCoefficients.data(), sense, rightHandSideValue, name == "" ? nullptr : name.c_str()); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to assert Gurobi constraint (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to assert Gurobi constraint (" << GRBgeterrormsg(env) << ", error code " << error << ")."; + // Determine the type of the constraint and add it properly. + int error = 0; + switch (constraint.getOperator()) { + case storm::expressions::OperatorType::Less: + error = GRBaddconstr(model, variables.size(), variables.data(), coefficients.data(), GRB_LESS_EQUAL, rightCoefficients.second - storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble(), name == "" ? nullptr : name.c_str()); + break; + case storm::expressions::OperatorType::LessOrEqual: + error = GRBaddconstr(model, variables.size(), variables.data(), coefficients.data(), GRB_LESS_EQUAL, rightCoefficients.second, name == "" ? nullptr : name.c_str()); + break; + case storm::expressions::OperatorType::Greater: + error = GRBaddconstr(model, variables.size(), variables.data(), coefficients.data(), GRB_GREATER_EQUAL, rightCoefficients.second + storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble(), name == "" ? nullptr : name.c_str()); + break; + case storm::expressions::OperatorType::GreaterOrEqual: + error = GRBaddconstr(model, variables.size(), variables.data(), coefficients.data(), GRB_GREATER_EQUAL, rightCoefficients.second, name == "" ? nullptr : name.c_str()); + break; + case storm::expressions::OperatorType::Equal: + error = GRBaddconstr(model, variables.size(), variables.data(), coefficients.data(), GRB_EQUAL, rightCoefficients.second, name == "" ? nullptr : name.c_str()); + break; + default: + LOG_ASSERT(false, "Illegal operator in LP solver constraint."); } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Could not assert constraint (" << GRBgeterrormsg(env) << ", error code " << error << ")."); } void GurobiLpSolver::optimize() const { @@ -212,18 +190,12 @@ namespace storm { this->update(); // Set the most recently set model sense. - int error = GRBsetintattr(model, "ModelSense", this->getModelSense() == MINIMIZE ? 1 : -1); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to set Gurobi model sense (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi model sense (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + int error = GRBsetintattr(model, "ModelSense", this->getModelSense() == ModelSense::Minimize ? 1 : -1); + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi model sense (" << GRBgeterrormsg(env) << ", error code " << error << ")."); // Then we actually optimize the model. error = GRBoptimize(model); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to optimize Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to optimize Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to optimize Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); this->currentModelHasBeenOptimized = true; } @@ -236,34 +208,21 @@ namespace storm { int optimalityStatus = 0; int error = GRBgetintattr(model, GRB_INT_ATTR_STATUS, &optimalityStatus); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); // By default, Gurobi may tell us only that the model is either infeasible or unbounded. To decide which one // it is, we need to perform an extra step. if (optimalityStatus == GRB_INF_OR_UNBD) { - std::cout << "here" << std::endl; error = GRBsetintparam(GRBgetenv(model), GRB_INT_PAR_DUALREDUCTIONS, 0); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."); this->optimize(); error = GRBgetintattr(model, GRB_INT_ATTR_STATUS, &optimalityStatus); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); error = GRBsetintparam(GRBgetenv(model), GRB_INT_PAR_DUALREDUCTIONS, 1); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."); } return optimalityStatus == GRB_INFEASIBLE; @@ -277,33 +236,21 @@ namespace storm { int optimalityStatus = 0; int error = GRBgetintattr(model, GRB_INT_ATTR_STATUS, &optimalityStatus); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); // By default, Gurobi may tell us only that the model is either infeasible or unbounded. To decide which one // it is, we need to perform an extra step. if (optimalityStatus == GRB_INF_OR_UNBD) { error = GRBsetintparam(GRBgetenv(model), GRB_INT_PAR_DUALREDUCTIONS, 0); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."); this->optimize(); error = GRBgetintattr(model, GRB_INT_ATTR_STATUS, &optimalityStatus); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); error = GRBsetintparam(GRBgetenv(model), GRB_INT_PAR_DUALREDUCTIONS, 1); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."); } return optimalityStatus == GRB_UNBOUNDED; @@ -316,120 +263,79 @@ namespace storm { int optimalityStatus = 0; int error = GRBgetintattr(model, GRB_INT_ATTR_STATUS, &optimalityStatus); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); return optimalityStatus == GRB_OPTIMAL; } - int_fast64_t GurobiLpSolver::getIntegerValue(uint_fast64_t variableIndex) const { + double GurobiLpSolver::getContinuousValue(std::string const& name) const { if (!this->isOptimal()) { - if (this->isInfeasible()) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."; - } else if (this->isUnbounded()) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."; - } else { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."; - } + LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."); + LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."); + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."); } - double value = 0; - int error = GRBgetdblattrelement(model, GRB_DBL_ATTR_X, variableIndex, &value); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + auto variableIndexPair = this->variableNameToIndexMap.find(name); + LOG_THROW(variableIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidAccessException, "Accessing value of unknown variable '" << name << "'."); - if (std::abs(value - static_cast<int>(value)) <= storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble()) { - // Nothing to do in this case. - } else if (std::abs(value) > storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble()) { - LOG4CPLUS_ERROR(logger, "Illegal value for integer variable in Gurobi solution (" << value << ")."); - throw storm::exceptions::InvalidStateException() << "Illegal value for integer variable in Gurobi solution (" << value << ")."; - } + double value = 0; + int error = GRBgetdblattrelement(model, GRB_DBL_ATTR_X, variableIndexPair->second, &value); + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - return static_cast<int_fast64_t>(value); + return value; } - bool GurobiLpSolver::getBinaryValue(uint_fast64_t variableIndex) const { + int_fast64_t GurobiLpSolver::getIntegerValue(std::string const& name) const { if (!this->isOptimal()) { - if (this->isInfeasible()) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."; - } else if (this->isUnbounded()) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."; - } else { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."; - } - } - - double value = 0; - int error = GRBgetdblattrelement(model, GRB_DBL_ATTR_X, variableIndex, &value); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."; + LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."); + LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."); + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."); } - if (std::abs(value - 1) <= storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble()) { - // Nothing to do in this case. - } else if (std::abs(value) > storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble()) { - LOG4CPLUS_ERROR(logger, "Illegal value for binary variable in Gurobi solution (" << value << ")."); - throw storm::exceptions::InvalidStateException() << "Illegal value for binary variable in Gurobi solution (" << value << ")."; - } + auto variableIndexPair = this->variableNameToIndexMap.find(name); + LOG_THROW(variableIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidAccessException, "Accessing value of unknown variable '" << name << "'."); - return static_cast<bool>(value); + double value = 0; + int error = GRBgetdblattrelement(model, GRB_DBL_ATTR_X, variableIndexPair->second, &value); + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."); + LOG_THROW(std::abs(static_cast<int>(value) - value) <= storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble(), storm::exceptions::InvalidStateException, "Illegal value for integer variable in Gurobi solution (" << value << ")."); + + return static_cast<int_fast64_t>(value); } - double GurobiLpSolver::getContinuousValue(uint_fast64_t variableIndex) const { + bool GurobiLpSolver::getBinaryValue(std::string const& name) const { if (!this->isOptimal()) { - if (this->isInfeasible()) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."; - } else if (this->isUnbounded()) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."; - } else { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."; - } + LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."); + LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."); + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."); } + auto variableIndexPair = this->variableNameToIndexMap.find(name); + LOG_THROW(variableIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidAccessException, "Accessing value of unknown variable '" << name << "'."); + double value = 0; - int error = GRBgetdblattrelement(model, GRB_DBL_ATTR_X, variableIndex, &value); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."; + int error = GRBgetdblattrelement(model, GRB_DBL_ATTR_X, variableIndexPair->second, &value); + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."); + + if (value > 0.5) { + LOG_THROW(std::abs(static_cast<int>(value) - 1) <= storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble(), storm::exceptions::InvalidStateException, "Illegal value for integer variable in Gurobi solution (" << value << ")."); + } else { + LOG_THROW(value <= storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble(), storm::exceptions::InvalidStateException, "Illegal value for integer variable in Gurobi solution (" << value << ")."); } - return value; + return static_cast<bool>(value); } double GurobiLpSolver::getObjectiveValue() const { if (!this->isOptimal()) { - if (this->isInfeasible()) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."; - } else if (this->isUnbounded()) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."; - } else { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."; - } + LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."); + LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."); + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."); } double value = 0; int error = GRBgetdblattr(model, GRB_DBL_ATTR_OBJVAL, &value); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."); return value; } diff --git a/src/solver/GurobiLpSolver.h b/src/solver/GurobiLpSolver.h index 18c5bbdbd..90bef47eb 100644 --- a/src/solver/GurobiLpSolver.h +++ b/src/solver/GurobiLpSolver.h @@ -60,23 +60,40 @@ namespace storm { */ virtual ~GurobiLpSolver(); - virtual uint_fast64_t createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override; - virtual uint_fast64_t createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override; - virtual uint_fast64_t createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) override; + // Methods to add continuous variables. + virtual void addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override; + virtual void addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override; + virtual void addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override; + virtual void addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override; + + // Methods to add integer variables. + virtual void addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override; + virtual void addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override; + virtual void addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override; + virtual void addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override; + + // Methods to add binary variables. + virtual void addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override; + + // Methods to incorporate recent changes. virtual void update() const override; - - virtual void addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rightHandSideValue) override; + // Methods to add constraints + virtual void addConstraint(std::string const& name, storm::expressions::Expression const& constraint) override; + + // Methods to optimize and retrieve optimality status. virtual void optimize() const override; virtual bool isInfeasible() const override; virtual bool isUnbounded() const override; virtual bool isOptimal() const override; - - virtual int_fast64_t getIntegerValue(uint_fast64_t variableIndex) const override; - virtual bool getBinaryValue(uint_fast64_t variableIndex) const override; - virtual double getContinuousValue(uint_fast64_t variableIndex) const override; + + // Methods to retrieve values of variables and the objective function in the optimal solutions. + virtual double getContinuousValue(std::string const& name) const override; + virtual int_fast64_t getIntegerValue(std::string const& name) const override; + virtual bool getBinaryValue(std::string const& name) const override; virtual double getObjectiveValue() const override; - + + // Methods to print the LP problem to a file. virtual void writeModelToFile(std::string const& filename) const override; private: @@ -85,14 +102,28 @@ namespace storm { */ void setGurobiEnvironmentProperties() const; + /*! + * Adds a variable with the given name, type, lower and upper bound and objective function coefficient. + * + * @param name The name of the variable. + * @param variableType The type of the variable in terms of Gurobi's constants. + * @param lowerBound The lower bound of the range of the variable. + * @param upperBound The upper bound of the range of the variable. + * @param objectiveFunctionCoefficient The coefficient of the variable in the objective function. + */ + void addVariable(std::string const& name, char variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient); + // The Gurobi environment. GRBenv* env; // The Gurobi model. GRBmodel* model; - // A counter that keeps track of the next free variable index. - uint_fast64_t nextVariableIndex; + // The index of the next variable. + int nextVariableIndex; + + // A mapping from variable names to their indices. + std::map<std::string, int> variableNameToIndexMap; }; #else // If Gurobi is not available, we provide a stub implementation that emits an error if any of its methods is called. @@ -114,27 +145,46 @@ namespace storm { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } - virtual uint_fast64_t createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override { + virtual void addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } - virtual uint_fast64_t createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override { + virtual void addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } + + virtual void addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } - virtual uint_fast64_t createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) override { + virtual void addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } - virtual void update() const override { + virtual void addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; + } + + virtual void addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; + } + + virtual void addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; + } + + virtual void addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } - virtual void addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rightHandSideValue) override { + virtual uint_fast64_t addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } - virtual void setModelSense(ModelSense const& modelSense) { + virtual void update() const override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; + } + + virtual void addConstraint(std::string const& name, storm::expressions::Expression const& constraint) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } @@ -154,15 +204,15 @@ namespace storm { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } - virtual int_fast64_t getIntegerValue(uint_fast64_t variableIndex) const override { + virtual double getContinuousValue(std::string const& name) const override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } - virtual bool getBinaryValue(uint_fast64_t variableIndex) const override { + virtual int_fast64_t getIntegerValue(std::string const& name) const override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } - virtual double getContinuousValue(uint_fast64_t variableIndex) const override { + virtual bool getBinaryValue(std::string const& name) const override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } @@ -175,7 +225,7 @@ namespace storm { } }; #endif - + } } diff --git a/src/solver/LpSolver.h b/src/solver/LpSolver.h index 311ee4b44..8c729a323 100644 --- a/src/solver/LpSolver.h +++ b/src/solver/LpSolver.h @@ -5,42 +5,25 @@ #include <vector> #include <cstdint> +#include "src/storage/expressions/Expression.h" + namespace storm { namespace solver { - /*! * An interface that captures the functionality of an LP solver. */ class LpSolver { public: - // An enumeration to represent the possible types of variables. Variables may be either unbounded, have only - // a lower or an upper bound, respectively, or be bounded from below and from above. - enum VariableType { - UNBOUNDED, - LOWER_BOUND, - UPPER_BOUND, - BOUNDED, - }; - - // An enumeration to represent the possible types of constraints. - enum BoundType { - LESS, - LESS_EQUAL, - GREATER, - GREATER_EQUAL, - EQUAL - }; - // An enumeration to represent whether the objective function is to be minimized or maximized. - enum ModelSense { - MINIMIZE, - MAXIMIZE + enum class ModelSense { + Minimize, + Maximize }; /*! * Creates an empty LP solver. By default the objective function is assumed to be minimized. */ - LpSolver() : currentModelHasBeenOptimized(false), modelSense(MINIMIZE) { + LpSolver() : currentModelHasBeenOptimized(false), modelSense(ModelSense::Minimize) { // Intentionally left empty. } @@ -55,52 +38,99 @@ namespace storm { } /*! - * Creates a continuous variable, i.e. a variable that may take all real values within its bounds. + * Registers an upper- and lower-bounded continuous variable, i.e. a variable that may take all real values + * within its bounds. + * + * @param name The name of the variable. + * @param lowerBound The lower bound of the variable. + * @param upperBound The upper bound of the variable. + * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective + * function. If omitted (or set to zero), the variable is irrelevant for the objective value. + */ + virtual void addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) = 0; + + /*! + * Registers a lower-bounded continuous variable, i.e. a variable that may take all real values up to its + * lower bound. + * + * @param name The name of the variable. + * @param lowerBound The lower bound of the variable. + * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective + * function. If omitted (or set to zero), the variable is irrelevant for the objective value. + */ + virtual void addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) = 0; + + /*! + * Registers an upper-bounded continuous variable, i.e. a variable that may take all real values up to its + * upper bound. + * + * @param name The name of the variable. + * @param upperBound The upper bound of the variable. + * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective + * function. If omitted (or set to zero), the variable is irrelevant for the objective value. + */ + virtual void addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) = 0; + + /*! + * Registers a unbounded continuous variable, i.e. a variable that may take all real values. * * @param name The name of the variable. - * @param variableType The type of the variable. - * @param lowerBound The lower bound of the variable. Note that this parameter is ignored if the variable - * is not bounded from below. - * @param upperBound The upper bound of the variable. Note that this parameter is ignored if the variable - * is not bounded from above. * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective - * function. If set to zero, the variable is irrelevant for the objective value. - * @return The index of the newly created variable. This index can be used to retrieve the variables value - * in a solution after the model has been optimized. + * function. If omitted (or set to zero), the variable is irrelevant for the objective value. */ - virtual uint_fast64_t createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) = 0; + virtual void addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient = 0) = 0; /*! - * Creates an integer variable. + * Registers an upper- and lower-bounded integer variable, i.e. a variable that may take all integer values + * within its bounds. + * + * @param name The name of the variable. + * @param lowerBound The lower bound of the variable. + * @param upperBound The upper bound of the variable. + * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective + * function. If omitted (or set to zero), the variable is irrelevant for the objective value. + */ + virtual void addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) = 0; + + /*! + * Registers a lower-bounded integer variable, i.e. a variable that may take all integer values up to its + * lower bound. + * + * @param name The name of the variable. + * @param lowerBound The lower bound of the variable. + * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective + * function. If omitted (or set to zero), the variable is irrelevant for the objective value. + */ + virtual void addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) = 0; + + /*! + * Registers an upper-bounded integer variable, i.e. a variable that may take all integer values up to its + * lower bound. + * + * @param name The name of the variable. + * @param upperBound The upper bound of the variable. + * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective + * function. If omitted (or set to zero), the variable is irrelevant for the objective value. + */ + virtual void addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) = 0; + + /*! + * Registers an unbounded integer variable, i.e. a variable that may take all integer values. * * @param name The name of the variable. - * @param variableType The type of the variable. - * @param lowerBound The lower bound of the variable. Note that this parameter is ignored if the variable - * is not bounded from below. - * @param upperBound The upper bound of the variable. Note that this parameter is ignored if the variable - * is not bounded from above. * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective - * function. If set to zero, the variable is irrelevant for the objective value. - * @return The index of the newly created variable. This index can be used to retrieve the variables value - * in a solution after the model has been optimized. + * function. If omitted (or set to zero), the variable is irrelevant for the objective value. */ - virtual uint_fast64_t createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) = 0; + virtual void addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient = 0) = 0; /*! - * Creates a binary variable, i.e. a variable that may be either zero or one. + * Registers a boolean variable, i.e. a variable that may be either 0 or 1. * * @param name The name of the variable. - * @param variableType The type of the variable. - * @param lowerBound The lower bound of the variable. Note that this parameter is ignored if the variable - * is not bounded from below. - * @param upperBound The upper bound of the variable. Note that this parameter is ignored if the variable - * is not bounded from above. * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective - * function. If set to zero, the variable is irrelevant for the objective value. - * @return The index of the newly created variable. This index can be used to retrieve the variables value - * in a solution after the model has been optimized. + * function. If omitted (or set to zero), the variable is irrelevant for the objective value. */ - virtual uint_fast64_t createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) = 0; + virtual void addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient = 0) = 0; /*! * Updates the model to make the variables that have been declared since the last call to <code>update</code> @@ -109,18 +139,13 @@ namespace storm { virtual void update() const = 0; /*! - * Adds a constraint of the form - * a_1*x_1 + ... + a_n*x_n op c - * to the LP problem. + * Adds a the given constraint to the LP problem. * * @param name The name of the constraint. If empty, the constraint has no name. - * @param variables A vector of variable indices that define the appearing variables x_1, ..., x_n. - * @param coefficients A vector of coefficients that define the a_1, ..., a_n. The i-ith entry specifies the - * coefficient of the variable whose index appears at the i-th position in the vector of variable indices. - * @param boundType The bound type specifies the operator op used in the constraint. - * @param rhs This defines the value of the constant appearing on the right-hand side of the constraint. + * @param constraint An expression that represents the constraint. The given constraint must be a linear + * (in)equality over the registered variables. */ - virtual void addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rhs) = 0; + virtual void addConstraint(std::string const& name, storm::expressions::Expression const& constraint) = 0; /*! * Optimizes the LP problem previously constructed. Afterwards, the methods isInfeasible, isUnbounded and @@ -154,34 +179,31 @@ namespace storm { virtual bool isOptimal() const = 0; /*! - * Retrieves the value of the integer variable with the given index. Note that this may only be called, if + * Retrieves the value of the integer variable with the given name. Note that this may only be called, if * the model was found to be optimal, i.e. iff isOptimal() returns true. * - * @param variableIndex The index of the integer variable whose value to query. If this index does not - * belong to a previously declared integer variable, the behaviour is undefined. + * @param name The name of the variable whose integer value (in the optimal solution) to retrieve. * @return The value of the integer variable in the optimal solution. */ - virtual int_fast64_t getIntegerValue(uint_fast64_t variableIndex) const = 0; + virtual int_fast64_t getIntegerValue(std::string const& name) const = 0; /*! - * Retrieves the value of the binary variable with the given index. Note that this may only be called, if + * Retrieves the value of the binary variable with the given name. Note that this may only be called, if * the model was found to be optimal, i.e. iff isOptimal() returns true. * - * @param variableIndex The index of the binary variable whose value to query. If this index does not - * belong to a previously declared binary variable, the behaviour is undefined. + * @param name The name of the variable whose binary (boolean) value (in the optimal solution) to retrieve. * @return The value of the binary variable in the optimal solution. */ - virtual bool getBinaryValue(uint_fast64_t variableIndex) const = 0; + virtual bool getBinaryValue(std::string const& name) const = 0; /*! - * Retrieves the value of the continuous variable with the given index. Note that this may only be called, + * Retrieves the value of the continuous variable with the given name. Note that this may only be called, * if the model was found to be optimal, i.e. iff isOptimal() returns true. * - * @param variableIndex The index of the continuous variable whose value to query. If this index does not - * belong to a previously declared continuous variable, the behaviour is undefined. + * @param name The name of the variable whose continuous value (in the optimal solution) to retrieve. * @return The value of the continuous variable in the optimal solution. */ - virtual double getContinuousValue(uint_fast64_t variableIndex) const = 0; + virtual double getContinuousValue(std::string const& name) const = 0; /*! * Retrieves the value of the objective function. Note that this may only be called, if the model was found diff --git a/src/storage/MaximalEndComponentDecomposition.cpp b/src/storage/MaximalEndComponentDecomposition.cpp index 56ebbd34c..0f6a44f65 100644 --- a/src/storage/MaximalEndComponentDecomposition.cpp +++ b/src/storage/MaximalEndComponentDecomposition.cpp @@ -88,7 +88,7 @@ namespace storm { for (uint_fast64_t choice = nondeterministicChoiceIndices[state]; choice < nondeterministicChoiceIndices[state + 1]; ++choice) { bool choiceContainedInMEC = true; for (auto const& entry : transitionMatrix.getRow(choice)) { - if (scc.find(entry.first) == scc.end()) { + if (scc.find(entry.getColumn()) == scc.end()) { choiceContainedInMEC = false; break; } @@ -116,8 +116,8 @@ namespace storm { statesToCheck.clear(); for (auto state : statesToRemove) { for (auto const& entry : backwardTransitions.getRow(state)) { - if (scc.find(entry.first) != scc.end()) { - statesToCheck.set(entry.first); + if (scc.find(entry.getColumn()) != scc.end()) { + statesToCheck.set(entry.getColumn()); } } } @@ -154,7 +154,7 @@ namespace storm { for (uint_fast64_t choice = nondeterministicChoiceIndices[state]; choice < nondeterministicChoiceIndices[state + 1]; ++choice) { bool choiceContained = true; for (auto const& entry : transitionMatrix.getRow(choice)) { - if (mecStateSet.find(entry.first) == mecStateSet.end()) { + if (mecStateSet.find(entry.getColumn()) == mecStateSet.end()) { choiceContained = false; break; } diff --git a/src/storage/SparseMatrix.cpp b/src/storage/SparseMatrix.cpp index 6955ec053..c6bfd0214 100644 --- a/src/storage/SparseMatrix.cpp +++ b/src/storage/SparseMatrix.cpp @@ -17,6 +17,41 @@ extern log4cplus::Logger logger; namespace storm { namespace storage { + template<typename T> + MatrixEntry<T>::MatrixEntry(uint_fast64_t column, T value) : entry(column, value) { + // Intentionally left empty. + } + + template<typename T> + MatrixEntry<T>::MatrixEntry(std::pair<uint_fast64_t, T>&& pair) : entry(std::move(pair)) { + // Intentionally left empty. + } + + template<typename T> + uint_fast64_t const& MatrixEntry<T>::getColumn() const { + return this->entry.first; + } + + template<typename T> + uint_fast64_t& MatrixEntry<T>::getColumn() { + return this->entry.first; + } + + template<typename T> + T const& MatrixEntry<T>::getValue() const { + return this->entry.second; + } + + template<typename T> + T& MatrixEntry<T>::getValue() { + return this->entry.second; + } + + template<typename T> + std::pair<uint_fast64_t, T> const& MatrixEntry<T>::getColumnValuePair() const { + return this->entry; + } + template<typename T> SparseMatrixBuilder<T>::SparseMatrixBuilder(uint_fast64_t rows, uint_fast64_t columns, uint_fast64_t entries, bool hasCustomRowGrouping, uint_fast64_t rowGroups) : rowCountSet(rows != 0), rowCount(rows), columnCountSet(columns != 0), columnCount(columns), entryCount(entries), hasCustomRowGrouping(hasCustomRowGrouping), rowGroupCountSet(rowGroups != 0), rowGroupCount(rowGroups), rowGroupIndices(), storagePreallocated(rows != 0 && columns != 0 && entries != 0), columnsAndValues(), rowIndications(), currentEntryCount(0), lastRow(0), lastColumn(0), currentRowGroup(0) { this->prepareInternalStorage(); @@ -168,7 +203,7 @@ namespace storm { void SparseMatrixBuilder<T>::prepareInternalStorage() { // Only allocate the memory for the matrix contents if the dimensions of the matrix are already known. if (storagePreallocated) { - columnsAndValues = std::vector<std::pair<uint_fast64_t, T>>(entryCount, std::make_pair(0, storm::utility::constantZero<T>())); + columnsAndValues = std::vector<MatrixEntry<T>>(entryCount, MatrixEntry<T>(0, storm::utility::constantZero<T>())); rowIndications = std::vector<uint_fast64_t>(rowCount + 1, 0); } else { rowIndications.push_back(0); @@ -218,17 +253,17 @@ namespace storm { } template<typename T> - SparseMatrix<T>::SparseMatrix() : rowCount(0), columnCount(0), entryCount(0), columnsAndValues(), rowIndications(), rowGroupIndices() { + SparseMatrix<T>::SparseMatrix() : rowCount(0), columnCount(0), entryCount(0), nonzeroEntryCount(0), columnsAndValues(), rowIndications(), rowGroupIndices() { // Intentionally left empty. } template<typename T> - SparseMatrix<T>::SparseMatrix(SparseMatrix<T> const& other) : rowCount(other.rowCount), columnCount(other.columnCount), entryCount(other.entryCount), columnsAndValues(other.columnsAndValues), rowIndications(other.rowIndications), rowGroupIndices(other.rowGroupIndices) { + SparseMatrix<T>::SparseMatrix(SparseMatrix<T> const& other) : rowCount(other.rowCount), columnCount(other.columnCount), entryCount(other.entryCount), nonzeroEntryCount(other.nonzeroEntryCount), columnsAndValues(other.columnsAndValues), rowIndications(other.rowIndications), rowGroupIndices(other.rowGroupIndices) { // Intentionally left empty. } template<typename T> - SparseMatrix<T>::SparseMatrix(SparseMatrix<T>&& other) : rowCount(other.rowCount), columnCount(other.columnCount), entryCount(other.entryCount), columnsAndValues(std::move(other.columnsAndValues)), rowIndications(std::move(other.rowIndications)), rowGroupIndices(std::move(other.rowGroupIndices)) { + SparseMatrix<T>::SparseMatrix(SparseMatrix<T>&& other) : rowCount(other.rowCount), columnCount(other.columnCount), entryCount(other.entryCount), nonzeroEntryCount(other.nonzeroEntryCount), columnsAndValues(std::move(other.columnsAndValues)), rowIndications(std::move(other.rowIndications)), rowGroupIndices(std::move(other.rowGroupIndices)) { // Now update the source matrix other.rowCount = 0; other.columnCount = 0; @@ -236,13 +271,21 @@ namespace storm { } template<typename T> - SparseMatrix<T>::SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t> const& rowIndications, std::vector<std::pair<uint_fast64_t, T>> const& columnsAndValues, std::vector<uint_fast64_t> const& rowGroupIndices) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), columnsAndValues(columnsAndValues), rowIndications(rowIndications), rowGroupIndices(rowGroupIndices) { - // Intentionally left empty. + SparseMatrix<T>::SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t> const& rowIndications, std::vector<MatrixEntry<T>> const& columnsAndValues, std::vector<uint_fast64_t> const& rowGroupIndices) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), nonzeroEntryCount(0), columnsAndValues(columnsAndValues), rowIndications(rowIndications), rowGroupIndices(rowGroupIndices) { + for (auto const& element : *this) { + if (element.getValue() != storm::utility::constantZero<T>()) { + ++this->nonzeroEntryCount; + } + } } template<typename T> - SparseMatrix<T>::SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t>&& rowIndications, std::vector<std::pair<uint_fast64_t, T>>&& columnsAndValues, std::vector<uint_fast64_t>&& rowGroupIndices) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), columnsAndValues(std::move(columnsAndValues)), rowIndications(std::move(rowIndications)), rowGroupIndices(std::move(rowGroupIndices)) { - // Intentionally left empty. + SparseMatrix<T>::SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t>&& rowIndications, std::vector<MatrixEntry<T>>&& columnsAndValues, std::vector<uint_fast64_t>&& rowGroupIndices) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), nonzeroEntryCount(0), columnsAndValues(std::move(columnsAndValues)), rowIndications(std::move(rowIndications)), rowGroupIndices(std::move(rowGroupIndices)) { + for (auto const& element : *this) { + if (element.getValue() != storm::utility::constantZero<T>()) { + ++this->nonzeroEntryCount; + } + } } template<typename T> @@ -252,6 +295,7 @@ namespace storm { rowCount = other.rowCount; columnCount = other.columnCount; entryCount = other.entryCount; + nonzeroEntryCount = other.nonzeroEntryCount; columnsAndValues = other.columnsAndValues; rowIndications = other.rowIndications; @@ -268,6 +312,7 @@ namespace storm { rowCount = other.rowCount; columnCount = other.columnCount; entryCount = other.entryCount; + nonzeroEntryCount = other.nonzeroEntryCount; columnsAndValues = std::move(other.columnsAndValues); rowIndications = std::move(other.rowIndications); @@ -294,17 +339,17 @@ namespace storm { for (uint_fast64_t row = 0; row < this->getRowCount(); ++row) { for (const_iterator it1 = this->begin(row), ite1 = this->end(row), it2 = other.begin(row), ite2 = other.end(row); it1 != ite1 && it2 != ite2; ++it1, ++it2) { // Skip over all zero entries in both matrices. - while (it1 != ite1 && it1->second == storm::utility::constantZero<T>()) { + while (it1 != ite1 && it1->getValue() == storm::utility::constantZero<T>()) { ++it1; } - while (it2 != ite2 && it2->second == storm::utility::constantZero<T>()) { + while (it2 != ite2 && it2->getValue() == storm::utility::constantZero<T>()) { ++it2; } if ((it1 == ite1) || (it2 == ite2)) { equalityResult = (it1 == ite1) ^ (it2 == ite2); break; } else { - if (it1->first != it2->first || it1->second != it2->second) { + if (it1->getColumn() != it2->getColumn() || it1->getValue() != it2->getValue()) { equalityResult = false; break; } @@ -330,6 +375,11 @@ namespace storm { return entryCount; } + template<typename T> + uint_fast64_t SparseMatrix<T>::getNonzeroEntryCount() const { + return nonzeroEntryCount; + } + template<typename T> uint_fast64_t SparseMatrix<T>::getRowGroupCount() const { return rowGroupIndices.size() - 1; @@ -374,12 +424,12 @@ namespace storm { // If there is at least one entry in this row, we can just set it to one, modify its column value to the // one given by the parameter and set all subsequent elements of this row to zero. - columnValuePtr->first = column; - columnValuePtr->second = storm::utility::constantOne<T>(); + columnValuePtr->getColumn() = column; + columnValuePtr->getValue() = storm::utility::constantOne<T>(); ++columnValuePtr; for (; columnValuePtr != columnValuePtrEnd; ++columnValuePtr) { - columnValuePtr->first = 0; - columnValuePtr->second = storm::utility::constantZero<T>(); + columnValuePtr->getColumn() = 0; + columnValuePtr->getValue() = storm::utility::constantZero<T>(); } } @@ -387,8 +437,8 @@ namespace storm { T SparseMatrix<T>::getConstrainedRowSum(uint_fast64_t row, storm::storage::BitVector const& constraint) const { T result(0); for (const_iterator it = this->begin(row), ite = this->end(row); it != ite; ++it) { - if (constraint.get(it->first)) { - result += it->second; + if (constraint.get(it->getColumn())) { + result += it->getValue(); } } return result; @@ -442,10 +492,10 @@ namespace storm { bool foundDiagonalElement = false; for (const_iterator it = this->begin(i), ite = this->end(i); it != ite; ++it) { - if (columnConstraint.get(it->first)) { + if (columnConstraint.get(it->getColumn())) { ++subEntries; - if (index == it->first) { + if (index == it->getColumn()) { foundDiagonalElement = true; } } @@ -492,14 +542,14 @@ namespace storm { bool insertedDiagonalElement = false; for (const_iterator it = this->begin(i), ite = this->end(i); it != ite; ++it) { - if (columnConstraint.get(it->first)) { - if (index == it->first) { + if (columnConstraint.get(it->getColumn())) { + if (index == it->getColumn()) { insertedDiagonalElement = true; - } else if (insertDiagonalEntries && !insertedDiagonalElement && it->first > index) { + } else if (insertDiagonalEntries && !insertedDiagonalElement && it->getColumn() > index) { matrixBuilder.addNextValue(rowCount, bitsSetBeforeIndex[index], storm::utility::constantZero<T>()); insertedDiagonalElement = true; } - matrixBuilder.addNextValue(rowCount, bitsSetBeforeIndex[it->first], it->second); + matrixBuilder.addNextValue(rowCount, bitsSetBeforeIndex[it->getColumn()], it->getValue()); } } if (insertDiagonalEntries && !insertedDiagonalElement) { @@ -525,7 +575,7 @@ namespace storm { // Iterate through that row and count the number of slots we have to reserve for copying. bool foundDiagonalElement = false; for (const_iterator it = this->begin(rowToCopy), ite = this->end(rowToCopy); it != ite; ++it) { - if (it->first == rowGroupIndex) { + if (it->getColumn() == rowGroupIndex) { foundDiagonalElement = true; } ++subEntries; @@ -547,13 +597,13 @@ namespace storm { // there is no entry yet. bool insertedDiagonalElement = false; for (const_iterator it = this->begin(rowToCopy), ite = this->end(rowToCopy); it != ite; ++it) { - if (it->first == rowGroupIndex) { + if (it->getColumn() == rowGroupIndex) { insertedDiagonalElement = true; - } else if (insertDiagonalEntries && !insertedDiagonalElement && it->first > rowGroupIndex) { + } else if (insertDiagonalEntries && !insertedDiagonalElement && it->getColumn() > rowGroupIndex) { matrixBuilder.addNextValue(rowGroupIndex, rowGroupIndex, storm::utility::constantZero<T>()); insertedDiagonalElement = true; } - matrixBuilder.addNextValue(rowGroupIndex, it->first, it->second); + matrixBuilder.addNextValue(rowGroupIndex, it->getColumn(), it->getValue()); } if (insertDiagonalEntries && !insertedDiagonalElement) { matrixBuilder.addNextValue(rowGroupIndex, rowGroupIndex, storm::utility::constantZero<T>()); @@ -571,13 +621,13 @@ namespace storm { uint_fast64_t entryCount = this->getEntryCount(); std::vector<uint_fast64_t> rowIndications(rowCount + 1); - std::vector<std::pair<uint_fast64_t, T>> columnsAndValues(entryCount); + std::vector<MatrixEntry<T>> columnsAndValues(entryCount); // First, we need to count how many entries each column has. for (uint_fast64_t group = 0; group < columnCount; ++group) { for (auto const& transition : joinGroups ? this->getRowGroup(group) : this->getRow(group)) { - if (transition.second != storm::utility::constantZero<T>()) { - ++rowIndications[transition.first + 1]; + if (transition.getValue() != storm::utility::constantZero<T>()) { + ++rowIndications[transition.getColumn() + 1]; } } } @@ -595,9 +645,9 @@ namespace storm { // Now we are ready to actually fill in the values of the transposed matrix. for (uint_fast64_t group = 0; group < columnCount; ++group) { for (auto const& transition : joinGroups ? this->getRowGroup(group) : this->getRow(group)) { - if (transition.second != storm::utility::constantZero<T>()) { - columnsAndValues[nextIndices[transition.first]] = std::make_pair(group, transition.second); - nextIndices[transition.first]++; + if (transition.getValue() != storm::utility::constantZero<T>()) { + columnsAndValues[nextIndices[transition.getColumn()]] = std::make_pair(group, transition.getValue()); + nextIndices[transition.getColumn()]++; } } } @@ -626,8 +676,8 @@ namespace storm { bool foundDiagonalElement = false; for (uint_fast64_t group = 0; group < this->getRowGroupCount(); ++group) { for (auto& entry : this->getRowGroup(group)) { - if (entry.first == group) { - entry.second = one - entry.second; + if (entry.getColumn() == group) { + entry.getValue() = one - entry.getValue(); foundDiagonalElement = true; } } @@ -644,8 +694,8 @@ namespace storm { // Iterate over all row groups and negate all the elements that are not on the diagonal. for (uint_fast64_t group = 0; group < this->getRowGroupCount(); ++group) { for (auto& entry : this->getRowGroup(group)) { - if (entry.first != group) { - entry.second = -entry.second; + if (entry.getColumn() != group) { + entry.getValue() = -entry.getValue(); } } } @@ -656,8 +706,8 @@ namespace storm { // Iterate over all rows and negate all the elements that are not on the diagonal. for (uint_fast64_t group = 0; group < this->getRowGroupCount(); ++group) { for (auto& entry : this->getRowGroup(group)) { - if (entry.first == group) { - entry.second = storm::utility::constantZero<T>(); + if (entry.getColumn() == group) { + entry.getValue() = storm::utility::constantZero<T>(); } } } @@ -680,9 +730,9 @@ namespace storm { // to invert the entry. T diagonalValue = storm::utility::constantZero<T>(); for (const_iterator it = this->begin(rowNumber), ite = this->end(rowNumber); it != ite; ++it) { - if (it->first == rowNumber) { - diagonalValue += it->second; - } else if (it->first > rowNumber) { + if (it->getColumn() == rowNumber) { + diagonalValue += it->getValue(); + } else if (it->getColumn() > rowNumber) { break; } } @@ -701,13 +751,13 @@ namespace storm { // add the result to the corresponding position in the vector. for (uint_fast64_t row = 0; row < rowCount && row < otherMatrix.rowCount; ++row) { for (const_iterator it1 = this->begin(row), ite1 = this->end(row), it2 = otherMatrix.begin(row), ite2 = otherMatrix.end(row); it1 != ite1 && it2 != ite2; ++it1) { - if (it1->first < it2->first) { + if (it1->getColumn() < it2->getColumn()) { continue; } else { // If the precondition of this method (i.e. that the given matrix is a submatrix // of the current one) was fulfilled, we know now that the two elements are in // the same column, so we can multiply and add them to the row sum vector. - result[row] += it2->second * it1->second; + result[row] += it2->getValue() * it1->getValue(); ++it2; } } @@ -735,7 +785,7 @@ namespace storm { *resultIterator = storm::utility::constantZero<T>(); for (ite = this->begin() + *(rowIterator + 1); it != ite; ++it) { - *resultIterator += it->second * vector[it->first]; + *resultIterator += it->getValue() * vector[it->getColumn()]; } } }); @@ -750,7 +800,7 @@ namespace storm { *resultIterator = storm::utility::constantZero<T>(); for (ite = this->begin() + *(rowIterator + 1); it != ite; ++it) { - *resultIterator += it->second * vector[it->first]; + *resultIterator += it->getValue() * vector[it->getColumn()]; } } #endif @@ -761,7 +811,7 @@ namespace storm { uint_fast64_t size = sizeof(*this); // Add size of columns and values. - size += sizeof(std::pair<uint_fast64_t, T>) * columnsAndValues.capacity(); + size += sizeof(MatrixEntry<T>) * columnsAndValues.capacity(); // Add row_indications size. size += sizeof(uint_fast64_t) * rowIndications.capacity(); @@ -833,7 +883,7 @@ namespace storm { T SparseMatrix<T>::getRowSum(uint_fast64_t row) const { T sum = storm::utility::constantZero<T>(); for (const_iterator it = this->begin(row), ite = this->end(row); it != ite; ++it) { - sum += it->second; + sum += it->getValue(); } return sum; } @@ -849,10 +899,10 @@ namespace storm { for (uint_fast64_t row = 0; row < this->getRowCount(); ++row) { for (const_iterator it1 = this->begin(row), ite1 = this->end(row), it2 = matrix.begin(row), ite2 = matrix.end(row); it1 != ite1; ++it1) { // Skip over all entries of the other matrix that are before the current entry in the current matrix. - while (it2 != ite2 && it2->first < it1->first) { + while (it2 != ite2 && it2->getColumn() < it1->getColumn()) { ++it2; } - if (it2 == ite2 || it1->first != it2->first) { + if (it2 == ite2 || it1->getColumn() != it2->getColumn()) { return false; } } @@ -879,8 +929,8 @@ namespace storm { out << i << "\t(\t"; uint_fast64_t currentRealIndex = 0; while (currentRealIndex < matrix.columnCount) { - if (nextIndex < matrix.rowIndications[i + 1] && currentRealIndex == matrix.columnsAndValues[nextIndex].first) { - out << matrix.columnsAndValues[nextIndex].second << "\t"; + if (nextIndex < matrix.rowIndications[i + 1] && currentRealIndex == matrix.columnsAndValues[nextIndex].getColumn()) { + out << matrix.columnsAndValues[nextIndex].getValue() << "\t"; ++nextIndex; } else { out << "0\t"; @@ -915,10 +965,12 @@ namespace storm { return result; } - // Explicitly instantiate the builder and the matrix. + // Explicitly instantiate the entry, builder and the matrix. + template class MatrixEntry<double>; template class SparseMatrixBuilder<double>; template class SparseMatrix<double>; template std::ostream& operator<<(std::ostream& out, SparseMatrix<double> const& matrix); + template class MatrixEntry<int>; template class SparseMatrixBuilder<int>; template class SparseMatrix<int>; template std::ostream& operator<<(std::ostream& out, SparseMatrix<int> const& matrix); diff --git a/src/storage/SparseMatrix.h b/src/storage/SparseMatrix.h index f85dfdd52..b60cbdc16 100644 --- a/src/storage/SparseMatrix.h +++ b/src/storage/SparseMatrix.h @@ -8,6 +8,7 @@ #include "src/storage/BitVector.h" #include "src/utility/constants.h" +#include "src/utility/OsDetection.h" #include "src/exceptions/InvalidArgumentException.h" #include "src/exceptions/OutOfRangeException.h" @@ -27,6 +28,83 @@ namespace storm { // Forward declare matrix class. template<typename T> class SparseMatrix; + template<typename T> + class MatrixEntry { + public: + /*! + * Constructs a matrix entry with the given column and value. + * + * @param column The column of the matrix entry. + * @param value The value of the matrix entry. + */ + MatrixEntry(uint_fast64_t column, T value); + + /*! + * Move-constructs the matrix entry fro the given column-value pair. + * + * @param pair The column-value pair from which to move-construct the matrix entry. + */ + MatrixEntry(std::pair<uint_fast64_t, T>&& pair); + + MatrixEntry() = default; + MatrixEntry(MatrixEntry const& other) = default; + MatrixEntry& operator=(MatrixEntry const& other) = default; +#ifndef WINDOWS + MatrixEntry(MatrixEntry&& other) = default; + MatrixEntry& operator=(MatrixEntry&& other) = default; +#endif + + /*! + * Retrieves the column of the matrix entry. + * + * @return The column of the matrix entry. + */ + uint_fast64_t const& getColumn() const; + + /*! + * Retrieves the column of the matrix entry. + * + * @return The column of the matrix entry. + */ + uint_fast64_t& getColumn(); + + /*! + * Retrieves the value of the matrix entry. + * + * @return The value of the matrix entry. + */ + T const& getValue() const; + + /*! + * Retrieves the value of the matrix entry. + * + * @return The value of the matrix entry. + */ + T& getValue(); + + /*! + * Retrieves a pair of column and value that characterizes this entry. + * + * @return A column-value pair that characterizes this entry. + */ + std::pair<uint_fast64_t, T> const& getColumnValuePair() const; + + private: + // The actual matrix entry. + std::pair<uint_fast64_t, T> entry; + }; + + /*! + * Computes the hash value of a matrix entry. + */ + template<typename T> + std::size_t hash_value(MatrixEntry<T> const& matrixEntry) { + std::size_t seed = 0; + boost::hash_combine(seed, matrixEntry.getColumn()); + boost::hash_combine(seed, matrixEntry.getValue()); + return seed; + } + /*! * A class that can be used to build a sparse matrix by adding value by value. */ @@ -128,7 +206,7 @@ namespace storm { bool storagePreallocated; // The storage for the columns and values of all entries in the matrix. - std::vector<std::pair<uint_fast64_t, T>> columnsAndValues; + std::vector<MatrixEntry<T>> columnsAndValues; // A vector containing the indices at which each given row begins. This index is to be interpreted as an // index in the valueStorage and the columnIndications vectors. Put differently, the values of the entries @@ -175,8 +253,8 @@ namespace storm { friend class storm::adapters::EigenAdapter; friend class storm::adapters::StormAdapter; - typedef typename std::vector<std::pair<uint_fast64_t, T>>::iterator iterator; - typedef typename std::vector<std::pair<uint_fast64_t, T>>::const_iterator const_iterator; + typedef typename std::vector<MatrixEntry<T>>::iterator iterator; + typedef typename std::vector<MatrixEntry<T>>::const_iterator const_iterator; /*! * This class represents a number of consecutive rows of the matrix. @@ -284,7 +362,7 @@ namespace storm { * @param columnsAndValues The vector containing the columns and values of the entries in the matrix. * @param rowGroupIndices The vector representing the row groups in the matrix. */ - SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t> const& rowIndications, std::vector<std::pair<uint_fast64_t, T>> const& columnsAndValues, std::vector<uint_fast64_t> const& rowGroupIndices); + SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t> const& rowIndications, std::vector<MatrixEntry<T>> const& columnsAndValues, std::vector<uint_fast64_t> const& rowGroupIndices); /*! * Constructs a sparse matrix by moving the given contents. @@ -294,7 +372,7 @@ namespace storm { * @param columnsAndValues The vector containing the columns and values of the entries in the matrix. * @param rowGroupIndices The vector representing the row groups in the matrix. */ - SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t>&& rowIndications, std::vector<std::pair<uint_fast64_t, T>>&& columnsAndValues, std::vector<uint_fast64_t>&& rowGroupIndices); + SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t>&& rowIndications, std::vector<MatrixEntry<T>>&& columnsAndValues, std::vector<uint_fast64_t>&& rowGroupIndices); /*! * Assigns the contents of the given matrix to the current one by deep-copying its contents. @@ -339,6 +417,13 @@ namespace storm { */ uint_fast64_t getEntryCount() const; + /*! + * Returns the number of nonzero entries in the matrix. + * + * @return The number of nonzero entries in the matrix. + */ + uint_fast64_t getNonzeroEntryCount() const; + /*! * Returns the number of row groups in the matrix. * @@ -651,8 +736,11 @@ namespace storm { // The number of entries in the matrix. uint_fast64_t entryCount; + // The number of nonzero entries in the matrix. + uint_fast64_t nonzeroEntryCount; + // The storage for the columns and values of all entries in the matrix. - std::vector<std::pair<uint_fast64_t, T>> columnsAndValues; + std::vector<MatrixEntry<T>> columnsAndValues; // A vector containing the indices at which each given row begins. This index is to be interpreted as an // index in the valueStorage and the columnIndications vectors. Put differently, the values of the entries diff --git a/src/storage/StronglyConnectedComponentDecomposition.cpp b/src/storage/StronglyConnectedComponentDecomposition.cpp index 6e9f08386..b14559bcb 100644 --- a/src/storage/StronglyConnectedComponentDecomposition.cpp +++ b/src/storage/StronglyConnectedComponentDecomposition.cpp @@ -114,33 +114,33 @@ namespace storm { // Now, traverse all successors of the current state. for(; successorIt != model.getRows(currentState).end(); ++successorIt) { // Record if the current state has a self-loop if we are to drop naive SCCs later. - if (dropNaiveSccs && currentState == successorIt->first) { + if (dropNaiveSccs && currentState == successorIt->getColumn()) { statesWithSelfloop.set(currentState, true); } // If we have not visited the successor already, we need to perform the procedure recursively on the // newly found state, but only if it belongs to the subsystem in which we are interested. - if (subsystem.get(successorIt->first)) { - if (!visitedStates.get(successorIt->first)) { + if (subsystem.get(successorIt->getColumn())) { + if (!visitedStates.get(successorIt->getColumn())) { // Save current iterator position so we can continue where we left off later. recursionIteratorStack.pop_back(); recursionIteratorStack.push_back(successorIt); // Put unvisited successor on top of our recursion stack and remember that. - recursionStateStack.push_back(successorIt->first); - statesInStack[successorIt->first] = true; + recursionStateStack.push_back(successorIt->getColumn()); + statesInStack[successorIt->getColumn()] = true; // Also, put initial value for iterator on corresponding recursion stack. - recursionIteratorStack.push_back(model.getRows(successorIt->first).begin()); + recursionIteratorStack.push_back(model.getRows(successorIt->getColumn()).begin()); // Perform the actual recursion step in an iterative way. goto recursionStepForward; recursionStepBackward: - lowlinks[currentState] = std::min(lowlinks[currentState], lowlinks[successorIt->first]); - } else if (tarjanStackStates.get(successorIt->first)) { + lowlinks[currentState] = std::min(lowlinks[currentState], lowlinks[successorIt->getColumn()]); + } else if (tarjanStackStates.get(successorIt->getColumn())) { // Update the lowlink of the current state. - lowlinks[currentState] = std::min(lowlinks[currentState], stateIndices[successorIt->first]); + lowlinks[currentState] = std::min(lowlinks[currentState], stateIndices[successorIt->getColumn()]); } } } @@ -165,7 +165,7 @@ namespace storm { if (onlyBottomSccs) { for (auto const& state : scc) { for (auto const& successor : model.getRows(state)) { - if (scc.find(successor.first) == scc.end()) { + if (scc.find(successor.getColumn()) == scc.end()) { isBottomScc = false; break; } diff --git a/src/storage/dd/CuddDd.cpp b/src/storage/dd/CuddDd.cpp index 72abb87bf..cdff92894 100644 --- a/src/storage/dd/CuddDd.cpp +++ b/src/storage/dd/CuddDd.cpp @@ -473,15 +473,15 @@ namespace storm { return this->ddManager; } - DdForwardIterator<DdType::CUDD> Dd<DdType::CUDD>::begin() const { + DdForwardIterator<DdType::CUDD> Dd<DdType::CUDD>::begin(bool enumerateDontCareMetaVariables) const { int* cube; double value; DdGen* generator = this->getCuddAdd().FirstCube(&cube, &value); - return DdForwardIterator<DdType::CUDD>(this->getDdManager(), generator, cube, value, Cudd_IsGenEmpty(generator), &this->getContainedMetaVariableNames()); + return DdForwardIterator<DdType::CUDD>(this->getDdManager(), generator, cube, value, Cudd_IsGenEmpty(generator), &this->getContainedMetaVariableNames(), enumerateDontCareMetaVariables); } - DdForwardIterator<DdType::CUDD> Dd<DdType::CUDD>::end() const { - return DdForwardIterator<DdType::CUDD>(this->getDdManager(), nullptr, nullptr, 0, true, nullptr); + DdForwardIterator<DdType::CUDD> Dd<DdType::CUDD>::end(bool enumerateDontCareMetaVariables) const { + return DdForwardIterator<DdType::CUDD>(this->getDdManager(), nullptr, nullptr, 0, true, nullptr, enumerateDontCareMetaVariables); } std::ostream & operator<<(std::ostream& out, const Dd<DdType::CUDD>& dd) { diff --git a/src/storage/dd/CuddDd.h b/src/storage/dd/CuddDd.h index f345c28a2..9f638d329 100644 --- a/src/storage/dd/CuddDd.h +++ b/src/storage/dd/CuddDd.h @@ -440,16 +440,20 @@ namespace storm { /*! * Retrieves an iterator that points to the first meta variable assignment with a non-zero function value. * + * @param enumerateDontCareMetaVariables If set to true, all meta variable assignments are enumerated, even + * if a meta variable does not at all influence the the function value. * @return An iterator that points to the first meta variable assignment with a non-zero function value. */ - DdForwardIterator<DdType::CUDD> begin() const; + DdForwardIterator<DdType::CUDD> begin(bool enumerateDontCareMetaVariables = true) const; /*! * Retrieves an iterator that points past the end of the container. * + * @param enumerateDontCareMetaVariables If set to true, all meta variable assignments are enumerated, even + * if a meta variable does not at all influence the the function value. * @return An iterator that points past the end of the container. */ - DdForwardIterator<DdType::CUDD> end() const; + DdForwardIterator<DdType::CUDD> end(bool enumerateDontCareMetaVariables = true) const; friend std::ostream & operator<<(std::ostream& out, const Dd<DdType::CUDD>& dd); private: diff --git a/src/storage/dd/CuddDdForwardIterator.cpp b/src/storage/dd/CuddDdForwardIterator.cpp index c0e754c8b..cce5c112c 100644 --- a/src/storage/dd/CuddDdForwardIterator.cpp +++ b/src/storage/dd/CuddDdForwardIterator.cpp @@ -5,7 +5,11 @@ namespace storm { namespace dd { - DdForwardIterator<DdType::CUDD>::DdForwardIterator(std::shared_ptr<DdManager<DdType::CUDD>> ddManager, DdGen* generator, int* cube, double value, bool isAtEnd, std::set<std::string> const* metaVariables) : ddManager(ddManager), generator(generator), cube(cube), value(value), isAtEnd(isAtEnd), metaVariables(metaVariables), cubeCounter(), relevantDontCareDdVariables(), currentValuation() { + DdForwardIterator<DdType::CUDD>::DdForwardIterator() : ddManager(), generator(), cube(), value(), isAtEnd(), metaVariables(), enumerateDontCareMetaVariables(), cubeCounter(), relevantDontCareDdVariables(), currentValuation() { + // Intentionally left empty. + } + + DdForwardIterator<DdType::CUDD>::DdForwardIterator(std::shared_ptr<DdManager<DdType::CUDD>> ddManager, DdGen* generator, int* cube, double value, bool isAtEnd, std::set<std::string> const* metaVariables, bool enumerateDontCareMetaVariables) : ddManager(ddManager), generator(generator), cube(cube), value(value), isAtEnd(isAtEnd), metaVariables(metaVariables), enumerateDontCareMetaVariables(enumerateDontCareMetaVariables), cubeCounter(), relevantDontCareDdVariables(), currentValuation() { // If the given generator is not yet at its end, we need to create the current valuation from the cube from // scratch. if (!this->isAtEnd) { @@ -49,6 +53,7 @@ namespace storm { } DdForwardIterator<DdType::CUDD>::~DdForwardIterator() { + // We free the pointers sind Cudd allocates them using malloc rather than new/delete. if (this->cube != nullptr) { free(this->cube); } @@ -108,29 +113,60 @@ namespace storm { // don't cares. In the latter case, we add them to a special list, so we can iterate over their concrete // valuations later. for (auto const& metaVariableName : *this->metaVariables) { + bool metaVariableAppearsInCube = false; + std::vector<std::tuple<ADD, std::string, uint_fast64_t>> localRelenvantDontCareDdVariables; auto const& metaVariable = this->ddManager->getMetaVariable(metaVariableName); if (metaVariable.getType() == DdMetaVariable<DdType::CUDD>::MetaVariableType::Bool) { if (this->cube[metaVariable.getDdVariables()[0].getCuddAdd().NodeReadIndex()] == 0) { - currentValuation.setBooleanValue(metaVariableName, false); + metaVariableAppearsInCube = true; + if (!currentValuation.containsBooleanIdentifier(metaVariableName)) { + currentValuation.addBooleanIdentifier(metaVariableName, false); + } else { + currentValuation.setBooleanValue(metaVariableName, false); + } } else if (this->cube[metaVariable.getDdVariables()[0].getCuddAdd().NodeReadIndex()] == 1) { - currentValuation.setBooleanValue(metaVariableName, true); + metaVariableAppearsInCube = true; + if (!currentValuation.containsBooleanIdentifier(metaVariableName)) { + currentValuation.addBooleanIdentifier(metaVariableName, true); + } else { + currentValuation.setBooleanValue(metaVariableName, true); + } } else { - relevantDontCareDdVariables.push_back(std::make_tuple(metaVariable.getDdVariables()[0].getCuddAdd(), metaVariableName, 0)); + localRelenvantDontCareDdVariables.push_back(std::make_tuple(metaVariable.getDdVariables()[0].getCuddAdd(), metaVariableName, 0)); } } else { int_fast64_t intValue = 0; for (uint_fast64_t bitIndex = 0; bitIndex < metaVariable.getNumberOfDdVariables(); ++bitIndex) { if (cube[metaVariable.getDdVariables()[bitIndex].getCuddAdd().NodeReadIndex()] == 0) { // Leave bit unset. + metaVariableAppearsInCube = true; } else if (cube[metaVariable.getDdVariables()[bitIndex].getCuddAdd().NodeReadIndex()] == 1) { intValue |= 1ull << (metaVariable.getNumberOfDdVariables() - bitIndex - 1); + metaVariableAppearsInCube = true; } else { // Temporarily leave bit unset so we can iterate trough the other option later. // Add the bit to the relevant don't care bits. - this->relevantDontCareDdVariables.push_back(std::make_tuple(metaVariable.getDdVariables()[bitIndex].getCuddAdd(), metaVariableName, metaVariable.getNumberOfDdVariables() - bitIndex - 1)); + localRelenvantDontCareDdVariables.push_back(std::make_tuple(metaVariable.getDdVariables()[bitIndex].getCuddAdd(), metaVariableName, metaVariable.getNumberOfDdVariables() - bitIndex - 1)); } } - currentValuation.setIntegerValue(metaVariableName, intValue + metaVariable.getLow()); + if (this->enumerateDontCareMetaVariables || metaVariableAppearsInCube) { + if (!currentValuation.containsIntegerIdentifier(metaVariableName)) { + currentValuation.addIntegerIdentifier(metaVariableName); + } + currentValuation.setIntegerValue(metaVariableName, intValue + metaVariable.getLow()); + } + } + + // If all meta variables are to be enumerated or the meta variable appeared in the cube, we register the + // missing bits to later enumerate all possible valuations. + if (this->enumerateDontCareMetaVariables || metaVariableAppearsInCube) { + relevantDontCareDdVariables.insert(relevantDontCareDdVariables.end(), localRelenvantDontCareDdVariables.begin(), localRelenvantDontCareDdVariables.end()); + } + + // If the meta variable does not appear in the cube and we're not supposed to enumerate such meta variables + // we remove the meta variable from the valuation. + if (!this->enumerateDontCareMetaVariables && !metaVariableAppearsInCube) { + currentValuation.removeIdentifier(metaVariableName); } } diff --git a/src/storage/dd/CuddDdForwardIterator.h b/src/storage/dd/CuddDdForwardIterator.h index 054438d2e..8b08af929 100644 --- a/src/storage/dd/CuddDdForwardIterator.h +++ b/src/storage/dd/CuddDdForwardIterator.h @@ -25,7 +25,7 @@ namespace storm { friend class Dd<DdType::CUDD>; // Default-instantiate the constructor. - DdForwardIterator() = default; + DdForwardIterator(); // Forbid copy-construction and copy assignment, because ownership of the internal pointer is unclear then. DdForwardIterator(DdForwardIterator<DdType::CUDD> const& other) = delete; @@ -82,8 +82,10 @@ namespace storm { * @param isAtEnd A flag that indicates whether the iterator is at its end and may not be moved forward any * more. * @param metaVariables The meta variables that appear in the DD. + * @param enumerateDontCareMetaVariables If set to true, all meta variable assignments are enumerated, even + * if a meta variable does not at all influence the the function value. */ - DdForwardIterator(std::shared_ptr<DdManager<DdType::CUDD>> ddManager, DdGen* generator, int* cube, double value, bool isAtEnd, std::set<std::string> const* metaVariables = nullptr); + DdForwardIterator(std::shared_ptr<DdManager<DdType::CUDD>> ddManager, DdGen* generator, int* cube, double value, bool isAtEnd, std::set<std::string> const* metaVariables = nullptr, bool enumerateDontCareMetaVariables = true); /*! * Recreates the internal information when a new cube needs to be treated. @@ -114,6 +116,10 @@ namespace storm { // The set of meta variables appearing in the DD. std::set<std::string> const* metaVariables; + // A flag that indicates whether the iterator is supposed to enumerate meta variable valuations even if + // they don't influence the function value. + bool enumerateDontCareMetaVariables; + // A number that represents how many assignments of the current cube have already been returned previously. // This is needed, because cubes may represent many assignments (if they have don't care variables). uint_fast64_t cubeCounter; diff --git a/src/storage/dd/CuddDdManager.cpp b/src/storage/dd/CuddDdManager.cpp index 64261d0aa..087382c71 100644 --- a/src/storage/dd/CuddDdManager.cpp +++ b/src/storage/dd/CuddDdManager.cpp @@ -32,7 +32,7 @@ bool CuddOptionsRegistered = storm::settings::Settings::registerNewModule([] (st namespace storm { namespace dd { DdManager<DdType::CUDD>::DdManager() : metaVariableMap(), cuddManager() { - this->cuddManager.SetMaxMemory(storm::settings::Settings::getInstance()->getOptionByLongName("cuddmaxmem").getArgument(0).getValueAsUnsignedInteger() * 1024); + this->cuddManager.SetMaxMemory(storm::settings::Settings::getInstance()->getOptionByLongName("cuddmaxmem").getArgument(0).getValueAsUnsignedInteger() * 1024 * 1024); this->cuddManager.SetEpsilon(storm::settings::Settings::getInstance()->getOptionByLongName("cuddprec").getArgument(0).getValueAsDouble()); } @@ -224,6 +224,10 @@ namespace storm { Cudd& DdManager<DdType::CUDD>::getCuddManager() { return this->cuddManager; } + + Cudd const& DdManager<DdType::CUDD>::getCuddManager() const { + return this->cuddManager; + } std::vector<std::string> DdManager<DdType::CUDD>::getDdVariableNames() const { // First, we initialize a list DD variables and their names. @@ -246,5 +250,22 @@ namespace storm { return result; } + + void DdManager<DdType::CUDD>::allowDynamicReordering(bool value) { + if (value) { + this->getCuddManager().AutodynEnable(CUDD_REORDER_GROUP_SIFT_CONV); + } else { + this->getCuddManager().AutodynDisable(); + } + } + + bool DdManager<DdType::CUDD>::isDynamicReorderingAllowed() const { + Cudd_ReorderingType type; + return this->getCuddManager().ReorderingStatus(&type); + } + + void DdManager<DdType::CUDD>::triggerReordering() { + this->getCuddManager().ReduceHeap(CUDD_REORDER_GROUP_SIFT_CONV, 0); + } } } \ No newline at end of file diff --git a/src/storage/dd/CuddDdManager.h b/src/storage/dd/CuddDdManager.h index 25844598c..6fba4d254 100644 --- a/src/storage/dd/CuddDdManager.h +++ b/src/storage/dd/CuddDdManager.h @@ -138,6 +138,25 @@ namespace storm { */ bool hasMetaVariable(std::string const& metaVariableName) const; + /*! + * Sets whether or not dynamic reordering is allowed for the DDs managed by this manager. + * + * @param value If set to true, dynamic reordering is allowed and forbidden otherwise. + */ + void allowDynamicReordering(bool value); + + /*! + * Retrieves whether dynamic reordering is currently allowed. + * + * @return True iff dynamic reordering is currently allowed. + */ + bool isDynamicReorderingAllowed() const; + + /*! + * Triggers a reordering of the DDs managed by this manager. + */ + void triggerReordering(); + private: /*! * Retrieves a list of names of the DD variables in the order of their index. @@ -153,6 +172,13 @@ namespace storm { */ Cudd& getCuddManager(); + /*! + * Retrieves the underlying CUDD manager. + * + * @return The underlying CUDD manager. + */ + Cudd const& getCuddManager() const; + // A mapping from variable names to the meta variable information. std::unordered_map<std::string, DdMetaVariable<DdType::CUDD>> metaVariableMap; diff --git a/src/storage/expressions/BaseExpression.cpp b/src/storage/expressions/BaseExpression.cpp index 2aef5ab6e..38a0604e2 100644 --- a/src/storage/expressions/BaseExpression.cpp +++ b/src/storage/expressions/BaseExpression.cpp @@ -49,6 +49,10 @@ namespace storm { LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to access identifier of non-constant, non-variable expression."); } + OperatorType BaseExpression::getOperator() const { + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to access operator of non-function application expression."); + } + bool BaseExpression::containsVariables() const { return false; } @@ -69,18 +73,12 @@ namespace storm { return false; } - std::shared_ptr<BaseExpression const> BaseExpression::getSharedPointer() const { - return this->shared_from_this(); + bool BaseExpression::isFunctionApplication() const { + return false; } - std::ostream& operator<<(std::ostream& stream, ExpressionReturnType const& enumValue) { - switch (enumValue) { - case ExpressionReturnType::Undefined: stream << "undefined"; break; - case ExpressionReturnType::Bool: stream << "bool"; break; - case ExpressionReturnType::Int: stream << "int"; break; - case ExpressionReturnType::Double: stream << "double"; break; - } - return stream; + std::shared_ptr<BaseExpression const> BaseExpression::getSharedPointer() const { + return this->shared_from_this(); } std::ostream& operator<<(std::ostream& stream, BaseExpression const& expression) { diff --git a/src/storage/expressions/BaseExpression.h b/src/storage/expressions/BaseExpression.h index 5a9cdba75..8afe11b91 100644 --- a/src/storage/expressions/BaseExpression.h +++ b/src/storage/expressions/BaseExpression.h @@ -7,20 +7,15 @@ #include <map> #include <iostream> +#include "src/storage/expressions/ExpressionReturnType.h" #include "src/storage/expressions/Valuation.h" #include "src/storage/expressions/ExpressionVisitor.h" +#include "src/storage/expressions/OperatorType.h" #include "src/exceptions/InvalidArgumentException.h" #include "src/utility/OsDetection.h" namespace storm { - namespace expressions { - /*! - * Each node in an expression tree has a uniquely defined type from this enum. - */ - enum class ExpressionReturnType {Undefined, Bool, Int, Double}; - - std::ostream& operator<<(std::ostream& stream, ExpressionReturnType const& enumValue); - + namespace expressions { /*! * The base class of all expression classes. */ @@ -97,6 +92,14 @@ namespace storm { */ virtual std::string const& getIdentifier() const; + /*! + * Retrieves the operator of a function application. This is only legal to call if the expression is + * function application. + * + * @return The operator associated with the function application. + */ + virtual OperatorType getOperator() const; + /*! * Retrieves whether the expression contains a variable. * @@ -132,6 +135,13 @@ namespace storm { */ virtual bool isFalse() const; + /*! + * Checks if the expression is a function application (of any sort). + * + * @return True iff the expression is a function application. + */ + virtual bool isFunctionApplication() const; + /*! * Retrieves the set of all variables that appear in the expression. * diff --git a/src/storage/expressions/BinaryBooleanFunctionExpression.cpp b/src/storage/expressions/BinaryBooleanFunctionExpression.cpp index e270293a8..061c231af 100644 --- a/src/storage/expressions/BinaryBooleanFunctionExpression.cpp +++ b/src/storage/expressions/BinaryBooleanFunctionExpression.cpp @@ -13,6 +13,16 @@ namespace storm { return this->operatorType; } + storm::expressions::OperatorType BinaryBooleanFunctionExpression::getOperator() const { + switch (this->getOperatorType()) { + case OperatorType::And: return storm::expressions::OperatorType::And; break; + case OperatorType::Or: return storm::expressions::OperatorType::Or; break; + case OperatorType::Xor: return storm::expressions::OperatorType::Xor; break; + case OperatorType::Implies: return storm::expressions::OperatorType::Implies; break; + case OperatorType::Iff: return storm::expressions::OperatorType::Iff; break; + } + } + bool BinaryBooleanFunctionExpression::evaluateAsBool(Valuation const* valuation) const { LOG_THROW(this->hasBooleanReturnType(), storm::exceptions::InvalidTypeException, "Unable to evaluate expression as boolean."); diff --git a/src/storage/expressions/BinaryBooleanFunctionExpression.h b/src/storage/expressions/BinaryBooleanFunctionExpression.h index 8b1bb7437..2a1d2417d 100644 --- a/src/storage/expressions/BinaryBooleanFunctionExpression.h +++ b/src/storage/expressions/BinaryBooleanFunctionExpression.h @@ -33,6 +33,7 @@ namespace storm { virtual ~BinaryBooleanFunctionExpression() = default; // Override base class methods. + virtual storm::expressions::OperatorType getOperator() const override; virtual bool evaluateAsBool(Valuation const* valuation = nullptr) const override; virtual std::shared_ptr<BaseExpression const> simplify() const override; virtual void accept(ExpressionVisitor* visitor) const override; diff --git a/src/storage/expressions/BinaryExpression.cpp b/src/storage/expressions/BinaryExpression.cpp index 09fb1cf70..bdb7fb59f 100644 --- a/src/storage/expressions/BinaryExpression.cpp +++ b/src/storage/expressions/BinaryExpression.cpp @@ -9,6 +9,10 @@ namespace storm { // Intentionally left empty. } + bool BinaryExpression::isFunctionApplication() const { + return true; + } + bool BinaryExpression::containsVariables() const { return this->getFirstOperand()->containsVariables() || this->getSecondOperand()->containsVariables(); } @@ -41,7 +45,7 @@ namespace storm { std::shared_ptr<BaseExpression const> BinaryExpression::getOperand(uint_fast64_t operandIndex) const { LOG_THROW(operandIndex < 2, storm::exceptions::InvalidAccessException, "Unable to access operand " << operandIndex << " in expression of arity 2."); - if (operandIndex == 1) { + if (operandIndex == 0) { return this->getFirstOperand(); } else { return this->getSecondOperand(); diff --git a/src/storage/expressions/BinaryExpression.h b/src/storage/expressions/BinaryExpression.h index 9daf9505d..3f9be4d06 100644 --- a/src/storage/expressions/BinaryExpression.h +++ b/src/storage/expressions/BinaryExpression.h @@ -30,6 +30,7 @@ namespace storm { virtual ~BinaryExpression() = default; // Override base class methods. + virtual bool isFunctionApplication() const override; virtual bool containsVariables() const override; virtual uint_fast64_t getArity() const override; virtual std::shared_ptr<BaseExpression const> getOperand(uint_fast64_t operandIndex) const override; diff --git a/src/storage/expressions/BinaryNumericalFunctionExpression.cpp b/src/storage/expressions/BinaryNumericalFunctionExpression.cpp index 225bea963..f6172821d 100644 --- a/src/storage/expressions/BinaryNumericalFunctionExpression.cpp +++ b/src/storage/expressions/BinaryNumericalFunctionExpression.cpp @@ -14,6 +14,17 @@ namespace storm { return this->operatorType; } + storm::expressions::OperatorType BinaryNumericalFunctionExpression::getOperator() const { + switch (this->getOperatorType()) { + case OperatorType::Plus: return storm::expressions::OperatorType::Plus; break; + case OperatorType::Minus: return storm::expressions::OperatorType::Minus; break; + case OperatorType::Times: return storm::expressions::OperatorType::Times; break; + case OperatorType::Divide: return storm::expressions::OperatorType::Divide; break; + case OperatorType::Min: return storm::expressions::OperatorType::Min; break; + case OperatorType::Max: return storm::expressions::OperatorType::Max; break; + } + } + int_fast64_t BinaryNumericalFunctionExpression::evaluateAsInt(Valuation const* valuation) const { LOG_THROW(this->hasIntegralReturnType(), storm::exceptions::InvalidTypeException, "Unable to evaluate expression as integer."); diff --git a/src/storage/expressions/BinaryNumericalFunctionExpression.h b/src/storage/expressions/BinaryNumericalFunctionExpression.h index 4e8573a4c..13ee489df 100644 --- a/src/storage/expressions/BinaryNumericalFunctionExpression.h +++ b/src/storage/expressions/BinaryNumericalFunctionExpression.h @@ -33,6 +33,7 @@ namespace storm { virtual ~BinaryNumericalFunctionExpression() = default; // Override base class methods. + virtual storm::expressions::OperatorType getOperator() const override; virtual int_fast64_t evaluateAsInt(Valuation const* valuation = nullptr) const override; virtual double evaluateAsDouble(Valuation const* valuation = nullptr) const override; virtual std::shared_ptr<BaseExpression const> simplify() const override; diff --git a/src/storage/expressions/BinaryRelationExpression.cpp b/src/storage/expressions/BinaryRelationExpression.cpp index 7e48c2286..f31f97fce 100644 --- a/src/storage/expressions/BinaryRelationExpression.cpp +++ b/src/storage/expressions/BinaryRelationExpression.cpp @@ -8,7 +8,18 @@ namespace storm { BinaryRelationExpression::BinaryRelationExpression(ExpressionReturnType returnType, std::shared_ptr<BaseExpression const> const& firstOperand, std::shared_ptr<BaseExpression const> const& secondOperand, RelationType relationType) : BinaryExpression(returnType, firstOperand, secondOperand), relationType(relationType) { // Intentionally left empty. } - + + storm::expressions::OperatorType BinaryRelationExpression::getOperator() const { + switch (this->getRelationType()) { + case RelationType::Equal: return storm::expressions::OperatorType::Equal; break; + case RelationType::NotEqual: return storm::expressions::OperatorType::NotEqual; break; + case RelationType::Less: return storm::expressions::OperatorType::Less; break; + case RelationType::LessOrEqual: return storm::expressions::OperatorType::LessOrEqual; break; + case RelationType::Greater: return storm::expressions::OperatorType::Greater; break; + case RelationType::GreaterOrEqual: return storm::expressions::OperatorType::GreaterOrEqual; break; + } + } + bool BinaryRelationExpression::evaluateAsBool(Valuation const* valuation) const { LOG_THROW(this->hasBooleanReturnType(), storm::exceptions::InvalidTypeException, "Unable to evaluate expression as boolean."); diff --git a/src/storage/expressions/BinaryRelationExpression.h b/src/storage/expressions/BinaryRelationExpression.h index b88ef71f9..4e03a598e 100644 --- a/src/storage/expressions/BinaryRelationExpression.h +++ b/src/storage/expressions/BinaryRelationExpression.h @@ -33,6 +33,7 @@ namespace storm { virtual ~BinaryRelationExpression() = default; // Override base class methods. + virtual storm::expressions::OperatorType getOperator() const override; virtual bool evaluateAsBool(Valuation const* valuation = nullptr) const override; virtual std::shared_ptr<BaseExpression const> simplify() const override; virtual void accept(ExpressionVisitor* visitor) const override; diff --git a/src/storage/expressions/Expression.cpp b/src/storage/expressions/Expression.cpp index ca2616b95..09810bb31 100644 --- a/src/storage/expressions/Expression.cpp +++ b/src/storage/expressions/Expression.cpp @@ -5,6 +5,7 @@ #include "src/storage/expressions/SubstitutionVisitor.h" #include "src/storage/expressions/IdentifierSubstitutionVisitor.h" #include "src/storage/expressions/TypeCheckVisitor.h" +#include "src/storage/expressions/LinearityCheckVisitor.h" #include "src/storage/expressions/Expressions.h" #include "src/exceptions/InvalidTypeException.h" #include "src/exceptions/ExceptionMacros.h" @@ -16,27 +17,27 @@ namespace storm { } Expression Expression::substitute(std::map<std::string, Expression> const& identifierToExpressionMap) const { - return SubstitutionVisitor<std::map<std::string, Expression>>(identifierToExpressionMap).substitute(this->getBaseExpressionPointer().get()); + return SubstitutionVisitor<std::map<std::string, Expression>>(identifierToExpressionMap).substitute(*this); } Expression Expression::substitute(std::unordered_map<std::string, Expression> const& identifierToExpressionMap) const { - return SubstitutionVisitor<std::unordered_map<std::string, Expression>>(identifierToExpressionMap).substitute(this->getBaseExpressionPointer().get()); + return SubstitutionVisitor<std::unordered_map<std::string, Expression>>(identifierToExpressionMap).substitute(*this); } Expression Expression::substitute(std::map<std::string, std::string> const& identifierToIdentifierMap) const { - return IdentifierSubstitutionVisitor<std::map<std::string, std::string>>(identifierToIdentifierMap).substitute(this->getBaseExpressionPointer().get()); + return IdentifierSubstitutionVisitor<std::map<std::string, std::string>>(identifierToIdentifierMap).substitute(*this); } Expression Expression::substitute(std::unordered_map<std::string, std::string> const& identifierToIdentifierMap) const { - return IdentifierSubstitutionVisitor<std::unordered_map<std::string, std::string>>(identifierToIdentifierMap).substitute(this->getBaseExpressionPointer().get()); + return IdentifierSubstitutionVisitor<std::unordered_map<std::string, std::string>>(identifierToIdentifierMap).substitute(*this); } void Expression::check(std::map<std::string, storm::expressions::ExpressionReturnType> const& identifierToTypeMap) const { - return TypeCheckVisitor<std::map<std::string, storm::expressions::ExpressionReturnType>>(identifierToTypeMap).check(this->getBaseExpressionPointer().get()); + return TypeCheckVisitor<std::map<std::string, storm::expressions::ExpressionReturnType>>(identifierToTypeMap).check(*this); } void Expression::check(std::unordered_map<std::string, storm::expressions::ExpressionReturnType> const& identifierToTypeMap) const { - return TypeCheckVisitor<std::unordered_map<std::string, storm::expressions::ExpressionReturnType>>(identifierToTypeMap).check(this->getBaseExpressionPointer().get()); + return TypeCheckVisitor<std::unordered_map<std::string, storm::expressions::ExpressionReturnType>>(identifierToTypeMap).check(*this); } bool Expression::evaluateAsBool(Valuation const* valuation) const { @@ -55,6 +56,14 @@ namespace storm { return Expression(this->getBaseExpression().simplify()); } + OperatorType Expression::getOperator() const { + return this->getBaseExpression().getOperator(); + } + + bool Expression::isFunctionApplication() const { + return this->getBaseExpression().isFunctionApplication(); + } + uint_fast64_t Expression::getArity() const { return this->getBaseExpression().getArity(); } @@ -102,6 +111,24 @@ namespace storm { } } + bool Expression::isRelationalExpression() const { + if (!this->isFunctionApplication()) { + return false; + } + + return this->getOperator() == OperatorType::Equal || this->getOperator() == OperatorType::NotEqual + || this->getOperator() == OperatorType::Less || this->getOperator() == OperatorType::LessOrEqual + || this->getOperator() == OperatorType::Greater || this->getOperator() == OperatorType::GreaterOrEqual; + } + + bool Expression::isLinear() const { + return LinearityCheckVisitor().check(*this); + } + + std::set<std::string> Expression::getVariables() const { + return this->getBaseExpression().getVariables(); + } + BaseExpression const& Expression::getBaseExpression() const { return *this->expressionPtr; } diff --git a/src/storage/expressions/Expression.h b/src/storage/expressions/Expression.h index 6ad063c91..504e3d96e 100644 --- a/src/storage/expressions/Expression.h +++ b/src/storage/expressions/Expression.h @@ -6,6 +6,7 @@ #include <unordered_map> #include "src/storage/expressions/BaseExpression.h" +#include "src/storage/expressions/ExpressionVisitor.h" #include "src/utility/OsDetection.h" namespace storm { @@ -157,6 +158,21 @@ namespace storm { */ Expression simplify(); + /*! + * Retrieves the operator of a function application. This is only legal to call if the expression is + * function application. + * + * @return The operator associated with the function application. + */ + OperatorType getOperator() const; + + /*! + * Checks if the expression is a function application (of any sort). + * + * @return True iff the expression is a function application. + */ + bool isFunctionApplication() const; + /*! * Retrieves the arity of the expression. * @@ -215,6 +231,21 @@ namespace storm { */ bool isFalse() const; + /*! + * Retrieves whether this expression is a relation expression, i.e., an expression that has a relation + * (equal, not equal, less, less or equal, etc.) as its top-level operator. + * + * @return True iff the expression is a relation expression. + */ + bool isRelationalExpression() const; + + /*! + * Retrieves whether this expression is a linear expression. + * + * @return True iff the expression is linear. + */ + bool isLinear() const; + /*! * Retrieves the set of all variables that appear in the expression. * @@ -271,6 +302,13 @@ namespace storm { */ bool hasBooleanReturnType() const; + /*! + * Accepts the given visitor. + * + * @param visitor The visitor to accept. + */ + void accept(ExpressionVisitor* visitor) const; + friend std::ostream& operator<<(std::ostream& stream, Expression const& expression); private: diff --git a/src/storage/expressions/ExpressionReturnType.cpp b/src/storage/expressions/ExpressionReturnType.cpp new file mode 100644 index 000000000..c10810f6c --- /dev/null +++ b/src/storage/expressions/ExpressionReturnType.cpp @@ -0,0 +1,15 @@ +#include "src/storage/expressions/ExpressionReturnType.h" + +namespace storm { + namespace expressions { + std::ostream& operator<<(std::ostream& stream, ExpressionReturnType const& enumValue) { + switch (enumValue) { + case ExpressionReturnType::Undefined: stream << "undefined"; break; + case ExpressionReturnType::Bool: stream << "bool"; break; + case ExpressionReturnType::Int: stream << "int"; break; + case ExpressionReturnType::Double: stream << "double"; break; + } + return stream; + } + } +} \ No newline at end of file diff --git a/src/storage/expressions/ExpressionReturnType.h b/src/storage/expressions/ExpressionReturnType.h new file mode 100644 index 000000000..0cf928ed8 --- /dev/null +++ b/src/storage/expressions/ExpressionReturnType.h @@ -0,0 +1,17 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_EXPRESSIONRETURNTYPE_H_ +#define STORM_STORAGE_EXPRESSIONS_EXPRESSIONRETURNTYPE_H_ + +#include <iostream> + +namespace storm { + namespace expressions { + /*! + * Each node in an expression tree has a uniquely defined type from this enum. + */ + enum class ExpressionReturnType {Undefined, Bool, Int, Double}; + + std::ostream& operator<<(std::ostream& stream, ExpressionReturnType const& enumValue); + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_EXPRESSIONRETURNTYPE_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/IdentifierSubstitutionVisitor.cpp b/src/storage/expressions/IdentifierSubstitutionVisitor.cpp index a2153af6d..19724dea1 100644 --- a/src/storage/expressions/IdentifierSubstitutionVisitor.cpp +++ b/src/storage/expressions/IdentifierSubstitutionVisitor.cpp @@ -13,8 +13,8 @@ namespace storm { } template<typename MapType> - Expression IdentifierSubstitutionVisitor<MapType>::substitute(BaseExpression const* expression) { - expression->accept(this); + Expression IdentifierSubstitutionVisitor<MapType>::substitute(Expression const& expression) { + expression.getBaseExpression().accept(this); return Expression(this->expressionStack.top()); } diff --git a/src/storage/expressions/IdentifierSubstitutionVisitor.h b/src/storage/expressions/IdentifierSubstitutionVisitor.h index 23dae79de..8e8723a70 100644 --- a/src/storage/expressions/IdentifierSubstitutionVisitor.h +++ b/src/storage/expressions/IdentifierSubstitutionVisitor.h @@ -26,7 +26,7 @@ namespace storm { * @return The expression in which all identifiers in the key set of the previously given mapping are * substituted with the mapped-to expressions. */ - Expression substitute(BaseExpression const* expression); + Expression substitute(Expression const& expression); virtual void visit(IfThenElseExpression const* expression) override; virtual void visit(BinaryBooleanFunctionExpression const* expression) override; diff --git a/src/storage/expressions/IfThenElseExpression.cpp b/src/storage/expressions/IfThenElseExpression.cpp index 6b84cde84..50c71af75 100644 --- a/src/storage/expressions/IfThenElseExpression.cpp +++ b/src/storage/expressions/IfThenElseExpression.cpp @@ -1,11 +1,41 @@ #include "src/storage/expressions/IfThenElseExpression.h" +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/InvalidAccessException.h" + namespace storm { namespace expressions { IfThenElseExpression::IfThenElseExpression(ExpressionReturnType returnType, std::shared_ptr<BaseExpression const> const& condition, std::shared_ptr<BaseExpression const> const& thenExpression, std::shared_ptr<BaseExpression const> const& elseExpression) : BaseExpression(returnType), condition(condition), thenExpression(thenExpression), elseExpression(elseExpression) { // Intentionally left empty. } + std::shared_ptr<BaseExpression const> IfThenElseExpression::getOperand(uint_fast64_t operandIndex) const { + LOG_THROW(operandIndex < 3, storm::exceptions::InvalidAccessException, "Unable to access operand " << operandIndex << " in expression of arity 3."); + if (operandIndex == 0) { + return this->getCondition(); + } else if (operandIndex == 1) { + return this->getThenExpression(); + } else { + return this->getElseExpression(); + } + } + + OperatorType IfThenElseExpression::getOperator() const { + return OperatorType::Ite; + } + + bool IfThenElseExpression::isFunctionApplication() const { + return true; + } + + bool IfThenElseExpression::containsVariables() const { + return this->getCondition()->containsVariables() || this->getThenExpression()->containsVariables() || this->getElseExpression()->containsVariables(); + } + + uint_fast64_t IfThenElseExpression::getArity() const { + return 3; + } + bool IfThenElseExpression::evaluateAsBool(Valuation const* valuation) const { bool conditionValue = this->condition->evaluateAsBool(valuation); if (conditionValue) { diff --git a/src/storage/expressions/IfThenElseExpression.h b/src/storage/expressions/IfThenElseExpression.h index 0e59c097a..63619d852 100644 --- a/src/storage/expressions/IfThenElseExpression.h +++ b/src/storage/expressions/IfThenElseExpression.h @@ -27,6 +27,11 @@ namespace storm { virtual ~IfThenElseExpression() = default; // Override base class methods. + virtual std::shared_ptr<BaseExpression const> getOperand(uint_fast64_t operandIndex) const override; + virtual OperatorType getOperator() const override; + virtual bool isFunctionApplication() const override; + virtual bool containsVariables() const override; + virtual uint_fast64_t getArity() const override; virtual bool evaluateAsBool(Valuation const* valuation = nullptr) const override; virtual int_fast64_t evaluateAsInt(Valuation const* valuation = nullptr) const override; virtual double evaluateAsDouble(Valuation const* valuation = nullptr) const override; diff --git a/src/storage/expressions/LinearCoefficientVisitor.cpp b/src/storage/expressions/LinearCoefficientVisitor.cpp new file mode 100644 index 000000000..7525d6dde --- /dev/null +++ b/src/storage/expressions/LinearCoefficientVisitor.cpp @@ -0,0 +1,151 @@ +#include "src/storage/expressions/LinearCoefficientVisitor.h" + +#include "src/storage/expressions/Expressions.h" +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/InvalidArgumentException.h" + +namespace storm { + namespace expressions { + std::pair<SimpleValuation, double> LinearCoefficientVisitor::getLinearCoefficients(Expression const& expression) { + expression.getBaseExpression().accept(this); + return resultStack.top(); + } + + void LinearCoefficientVisitor::visit(IfThenElseExpression const* expression) { + LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); + } + + void LinearCoefficientVisitor::visit(BinaryBooleanFunctionExpression const* expression) { + LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); + } + + void LinearCoefficientVisitor::visit(BinaryNumericalFunctionExpression const* expression) { + if (expression->getOperatorType() == BinaryNumericalFunctionExpression::OperatorType::Plus) { + expression->getFirstOperand()->accept(this); + std::pair<SimpleValuation, double> leftResult = resultStack.top(); + resultStack.pop(); + expression->getSecondOperand()->accept(this); + std::pair<SimpleValuation, double>& rightResult = resultStack.top(); + + // Now add the left result to the right result. + for (auto const& identifier : leftResult.first.getDoubleIdentifiers()) { + if (rightResult.first.containsDoubleIdentifier(identifier)) { + rightResult.first.setDoubleValue(identifier, leftResult.first.getDoubleValue(identifier) + rightResult.first.getDoubleValue(identifier)); + } else { + rightResult.first.setDoubleValue(identifier, leftResult.first.getDoubleValue(identifier)); + } + } + rightResult.second += leftResult.second; + return; + } else if (expression->getOperatorType() == BinaryNumericalFunctionExpression::OperatorType::Minus) { + expression->getFirstOperand()->accept(this); + std::pair<SimpleValuation, double> leftResult = resultStack.top(); + resultStack.pop(); + expression->getSecondOperand()->accept(this); + std::pair<SimpleValuation, double>& rightResult = resultStack.top(); + + // Now subtract the right result from the left result. + for (auto const& identifier : leftResult.first.getDoubleIdentifiers()) { + if (rightResult.first.containsDoubleIdentifier(identifier)) { + rightResult.first.setDoubleValue(identifier, leftResult.first.getDoubleValue(identifier) - rightResult.first.getDoubleValue(identifier)); + } else { + rightResult.first.setDoubleValue(identifier, leftResult.first.getDoubleValue(identifier)); + } + } + for (auto const& identifier : rightResult.first.getDoubleIdentifiers()) { + if (!leftResult.first.containsDoubleIdentifier(identifier)) { + rightResult.first.setDoubleValue(identifier, -rightResult.first.getDoubleValue(identifier)); + } + } + rightResult.second = leftResult.second - rightResult.second; + return; + } else if (expression->getOperatorType() == BinaryNumericalFunctionExpression::OperatorType::Times) { + expression->getFirstOperand()->accept(this); + std::pair<SimpleValuation, double> leftResult = resultStack.top(); + resultStack.pop(); + expression->getSecondOperand()->accept(this); + std::pair<SimpleValuation, double>& rightResult = resultStack.top(); + + // If the expression is linear, either the left or the right side must not contain variables. + LOG_THROW(leftResult.first.getNumberOfIdentifiers() == 0 || rightResult.first.getNumberOfIdentifiers() == 0, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); + if (leftResult.first.getNumberOfIdentifiers() == 0) { + for (auto const& identifier : rightResult.first.getDoubleIdentifiers()) { + rightResult.first.setDoubleValue(identifier, leftResult.second * rightResult.first.getDoubleValue(identifier)); + } + } else { + for (auto const& identifier : leftResult.first.getDoubleIdentifiers()) { + rightResult.first.addDoubleIdentifier(identifier, rightResult.second * leftResult.first.getDoubleValue(identifier)); + } + } + rightResult.second *= leftResult.second; + return; + } else if (expression->getOperatorType() == BinaryNumericalFunctionExpression::OperatorType::Divide) { + expression->getFirstOperand()->accept(this); + std::pair<SimpleValuation, double> leftResult = resultStack.top(); + resultStack.pop(); + expression->getSecondOperand()->accept(this); + std::pair<SimpleValuation, double>& rightResult = resultStack.top(); + + // If the expression is linear, either the left or the right side must not contain variables. + LOG_THROW(leftResult.first.getNumberOfIdentifiers() == 0 || rightResult.first.getNumberOfIdentifiers() == 0, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); + if (leftResult.first.getNumberOfIdentifiers() == 0) { + for (auto const& identifier : rightResult.first.getDoubleIdentifiers()) { + rightResult.first.setDoubleValue(identifier, leftResult.second / rightResult.first.getDoubleValue(identifier)); + } + } else { + for (auto const& identifier : leftResult.first.getDoubleIdentifiers()) { + rightResult.first.addDoubleIdentifier(identifier, leftResult.first.getDoubleValue(identifier) / rightResult.second); + } + } + rightResult.second = leftResult.second / leftResult.second; + return; + } else { + LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); + } + } + + void LinearCoefficientVisitor::visit(BinaryRelationExpression const* expression) { + LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); + } + + void LinearCoefficientVisitor::visit(VariableExpression const* expression) { + SimpleValuation valuation; + switch (expression->getReturnType()) { + case ExpressionReturnType::Bool: LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); break; + case ExpressionReturnType::Int: + case ExpressionReturnType::Double: valuation.addDoubleIdentifier(expression->getVariableName(), 1); break; + case ExpressionReturnType::Undefined: LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Illegal expression return type."); break; + } + + resultStack.push(std::make_pair(valuation, 0)); + } + + void LinearCoefficientVisitor::visit(UnaryBooleanFunctionExpression const* expression) { + LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); + } + + void LinearCoefficientVisitor::visit(UnaryNumericalFunctionExpression const* expression) { + if (expression->getOperatorType() == UnaryNumericalFunctionExpression::OperatorType::Minus) { + // Here, we need to negate all double identifiers. + std::pair<SimpleValuation, double>& valuationConstantPair = resultStack.top(); + for (auto const& identifier : valuationConstantPair.first.getDoubleIdentifiers()) { + valuationConstantPair.first.setDoubleValue(identifier, -valuationConstantPair.first.getDoubleValue(identifier)); + } + } else { + LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); + } + } + + void LinearCoefficientVisitor::visit(BooleanLiteralExpression const* expression) { + LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); + } + + void LinearCoefficientVisitor::visit(IntegerLiteralExpression const* expression) { + resultStack.push(std::make_pair(SimpleValuation(), static_cast<double>(expression->getValue()))); + } + + void LinearCoefficientVisitor::visit(DoubleLiteralExpression const* expression) { + resultStack.push(std::make_pair(SimpleValuation(), expression->getValue())); + } + } +} \ No newline at end of file diff --git a/src/storage/expressions/LinearCoefficientVisitor.h b/src/storage/expressions/LinearCoefficientVisitor.h new file mode 100644 index 000000000..263e752c8 --- /dev/null +++ b/src/storage/expressions/LinearCoefficientVisitor.h @@ -0,0 +1,46 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_LINEARCOEFFICIENTVISITOR_H_ +#define STORM_STORAGE_EXPRESSIONS_LINEARCOEFFICIENTVISITOR_H_ + +#include <stack> + +#include "src/storage/expressions/Expression.h" +#include "src/storage/expressions/ExpressionVisitor.h" +#include "src/storage/expressions/SimpleValuation.h" + +namespace storm { + namespace expressions { + class LinearCoefficientVisitor : public ExpressionVisitor { + public: + /*! + * Creates a linear coefficient visitor. + */ + LinearCoefficientVisitor() = default; + + /*! + * Computes the (double) coefficients of all identifiers appearing in the expression if the expression + * was rewritten as a sum of atoms.. If the expression is not linear, an exception is thrown. + * + * @param expression The expression for which to compute the coefficients. + * @return A pair consisting of a mapping from identifiers to their coefficients and the coefficient of + * the constant atom. + */ + std::pair<SimpleValuation, double> getLinearCoefficients(Expression const& expression); + + virtual void visit(IfThenElseExpression const* expression) override; + virtual void visit(BinaryBooleanFunctionExpression const* expression) override; + virtual void visit(BinaryNumericalFunctionExpression const* expression) override; + virtual void visit(BinaryRelationExpression const* expression) override; + virtual void visit(VariableExpression const* expression) override; + virtual void visit(UnaryBooleanFunctionExpression const* expression) override; + virtual void visit(UnaryNumericalFunctionExpression const* expression) override; + virtual void visit(BooleanLiteralExpression const* expression) override; + virtual void visit(IntegerLiteralExpression const* expression) override; + virtual void visit(DoubleLiteralExpression const* expression) override; + + private: + std::stack<std::pair<SimpleValuation, double>> resultStack; + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_LINEARCOEFFICIENTVISITOR_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/LinearityCheckVisitor.cpp b/src/storage/expressions/LinearityCheckVisitor.cpp new file mode 100644 index 000000000..9b382ed22 --- /dev/null +++ b/src/storage/expressions/LinearityCheckVisitor.cpp @@ -0,0 +1,112 @@ +#include "src/storage/expressions/LinearityCheckVisitor.h" +#include "src/storage/expressions/Expressions.h" + +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/InvalidTypeException.h" + +namespace storm { + namespace expressions { + LinearityCheckVisitor::LinearityCheckVisitor() : resultStack() { + // Intentionally left empty. + } + + bool LinearityCheckVisitor::check(Expression const& expression) { + expression.getBaseExpression().accept(this); + return resultStack.top() == LinearityStatus::LinearWithoutVariables || resultStack.top() == LinearityStatus::LinearContainsVariables; + } + + void LinearityCheckVisitor::visit(IfThenElseExpression const* expression) { + // An if-then-else expression is never linear. + resultStack.push(LinearityStatus::NonLinear); + } + + void LinearityCheckVisitor::visit(BinaryBooleanFunctionExpression const* expression) { + // Boolean function applications are not allowed in linear expressions. + resultStack.push(LinearityStatus::NonLinear); + } + + void LinearityCheckVisitor::visit(BinaryNumericalFunctionExpression const* expression) { + LinearityStatus leftResult; + LinearityStatus rightResult; + switch (expression->getOperatorType()) { + case BinaryNumericalFunctionExpression::OperatorType::Plus: + case BinaryNumericalFunctionExpression::OperatorType::Minus: + expression->getFirstOperand()->accept(this); + leftResult = resultStack.top(); + + if (leftResult == LinearityStatus::NonLinear) { + return; + } else { + resultStack.pop(); + expression->getSecondOperand()->accept(this); + rightResult = resultStack.top(); + if (rightResult == LinearityStatus::NonLinear) { + return; + } + resultStack.pop(); + } + + resultStack.push(leftResult == LinearityStatus::LinearContainsVariables || rightResult == LinearityStatus::LinearContainsVariables ? LinearityStatus::LinearContainsVariables : LinearityStatus::LinearWithoutVariables); + break; + case BinaryNumericalFunctionExpression::OperatorType::Times: + case BinaryNumericalFunctionExpression::OperatorType::Divide: + expression->getFirstOperand()->accept(this); + leftResult = resultStack.top(); + + if (leftResult == LinearityStatus::NonLinear) { + return; + } else { + resultStack.pop(); + expression->getSecondOperand()->accept(this); + rightResult = resultStack.top(); + if (rightResult == LinearityStatus::NonLinear) { + return; + } + resultStack.pop(); + } + + if (leftResult == LinearityStatus::LinearContainsVariables && rightResult == LinearityStatus::LinearContainsVariables) { + resultStack.push(LinearityStatus::NonLinear); + } + + resultStack.push(leftResult == LinearityStatus::LinearContainsVariables || rightResult == LinearityStatus::LinearContainsVariables ? LinearityStatus::LinearContainsVariables : LinearityStatus::LinearWithoutVariables); + break; + case BinaryNumericalFunctionExpression::OperatorType::Min: resultStack.push(LinearityStatus::NonLinear); break; + case BinaryNumericalFunctionExpression::OperatorType::Max: resultStack.push(LinearityStatus::NonLinear); break; + } + } + + void LinearityCheckVisitor::visit(BinaryRelationExpression const* expression) { + resultStack.push(LinearityStatus::NonLinear); + } + + void LinearityCheckVisitor::visit(VariableExpression const* expression) { + resultStack.push(LinearityStatus::LinearContainsVariables); + } + + void LinearityCheckVisitor::visit(UnaryBooleanFunctionExpression const* expression) { + // Boolean function applications are not allowed in linear expressions. + resultStack.push(LinearityStatus::NonLinear); + } + + void LinearityCheckVisitor::visit(UnaryNumericalFunctionExpression const* expression) { + switch (expression->getOperatorType()) { + case UnaryNumericalFunctionExpression::OperatorType::Minus: break; + case UnaryNumericalFunctionExpression::OperatorType::Floor: + case UnaryNumericalFunctionExpression::OperatorType::Ceil: resultStack.pop(); resultStack.push(LinearityStatus::NonLinear); break; + } + } + + void LinearityCheckVisitor::visit(BooleanLiteralExpression const* expression) { + resultStack.push(LinearityStatus::NonLinear); + } + + void LinearityCheckVisitor::visit(IntegerLiteralExpression const* expression) { + resultStack.push(LinearityStatus::LinearWithoutVariables); + } + + void LinearityCheckVisitor::visit(DoubleLiteralExpression const* expression) { + resultStack.push(LinearityStatus::LinearWithoutVariables); + } + } +} \ No newline at end of file diff --git a/src/storage/expressions/LinearityCheckVisitor.h b/src/storage/expressions/LinearityCheckVisitor.h new file mode 100644 index 000000000..d76b658c8 --- /dev/null +++ b/src/storage/expressions/LinearityCheckVisitor.h @@ -0,0 +1,45 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_LINEARITYCHECKVISITOR_H_ +#define STORM_STORAGE_EXPRESSIONS_LINEARITYCHECKVISITOR_H_ + +#include <stack> + +#include "src/storage/expressions/Expression.h" +#include "src/storage/expressions/ExpressionVisitor.h" + +namespace storm { + namespace expressions { + class LinearityCheckVisitor : public ExpressionVisitor { + public: + /*! + * Creates a linearity check visitor. + */ + LinearityCheckVisitor(); + + /*! + * Checks that the given expression is linear. + * + * @param expression The expression to check for linearity. + */ + bool check(Expression const& expression); + + virtual void visit(IfThenElseExpression const* expression) override; + virtual void visit(BinaryBooleanFunctionExpression const* expression) override; + virtual void visit(BinaryNumericalFunctionExpression const* expression) override; + virtual void visit(BinaryRelationExpression const* expression) override; + virtual void visit(VariableExpression const* expression) override; + virtual void visit(UnaryBooleanFunctionExpression const* expression) override; + virtual void visit(UnaryNumericalFunctionExpression const* expression) override; + virtual void visit(BooleanLiteralExpression const* expression) override; + virtual void visit(IntegerLiteralExpression const* expression) override; + virtual void visit(DoubleLiteralExpression const* expression) override; + + private: + enum class LinearityStatus { NonLinear, LinearContainsVariables, LinearWithoutVariables }; + + // A stack for communicating the results of the subexpressions. + std::stack<LinearityStatus> resultStack; + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_LINEARITYCHECKVISITOR_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/OperatorType.h b/src/storage/expressions/OperatorType.h new file mode 100644 index 000000000..8a4f199aa --- /dev/null +++ b/src/storage/expressions/OperatorType.h @@ -0,0 +1,33 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_OPERATORTYPE_H_ +#define STORM_STORAGE_EXPRESSIONS_OPERATORTYPE_H_ + +namespace storm { + namespace expressions { + // An enum representing all possible operator types. + enum class OperatorType { + And, + Or, + Xor, + Implies, + Iff, + Plus, + Minus, + Times, + Divide, + Min, + Max, + Equal, + NotEqual, + Less, + LessOrEqual, + Greater, + GreaterOrEqual, + Not, + Floor, + Ceil, + Ite + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_OPERATORTYPE_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/SimpleValuation.cpp b/src/storage/expressions/SimpleValuation.cpp index d29e6a7c8..923bbe84b 100644 --- a/src/storage/expressions/SimpleValuation.cpp +++ b/src/storage/expressions/SimpleValuation.cpp @@ -1,103 +1,167 @@ -#include <boost/functional/hash.hpp> #include "src/storage/expressions/SimpleValuation.h" + +#include <set> + +#include <boost/functional/hash.hpp> #include "src/exceptions/ExceptionMacros.h" #include "src/exceptions/InvalidArgumentException.h" +#include "src/exceptions/InvalidAccessException.h" namespace storm { namespace expressions { - SimpleValuation::SimpleValuation() : booleanIdentifierToIndexMap(new std::unordered_map<std::string, uint_fast64_t>()), integerIdentifierToIndexMap(new std::unordered_map<std::string, uint_fast64_t>()), doubleIdentifierToIndexMap(new std::unordered_map<std::string, uint_fast64_t>()), booleanValues(), integerValues(), doubleValues() { - // Intentionally left empty. - } - bool SimpleValuation::operator==(SimpleValuation const& other) const { - return this->booleanIdentifierToIndexMap.get() == other.booleanIdentifierToIndexMap.get() && this->integerIdentifierToIndexMap.get() == other.integerIdentifierToIndexMap.get() && this->doubleIdentifierToIndexMap.get() == other.doubleIdentifierToIndexMap.get() && this->booleanValues == other.booleanValues && this->integerValues == other.integerValues && this->doubleValues == other.doubleValues; + return this->identifierToValueMap == other.identifierToValueMap; } void SimpleValuation::addBooleanIdentifier(std::string const& name, bool initialValue) { - LOG_THROW(this->booleanIdentifierToIndexMap->find(name) == this->booleanIdentifierToIndexMap->end(), storm::exceptions::InvalidArgumentException, "Boolean identifier '" << name << "' already registered."); - - this->booleanIdentifierToIndexMap->emplace(name, this->booleanValues.size()); - this->booleanValues.push_back(initialValue); + LOG_THROW(this->identifierToValueMap.find(name) == this->identifierToValueMap.end(), storm::exceptions::InvalidArgumentException, "Identifier '" << name << "' already registered."); + this->identifierToValueMap.emplace(name, initialValue); } void SimpleValuation::addIntegerIdentifier(std::string const& name, int_fast64_t initialValue) { - LOG_THROW(this->booleanIdentifierToIndexMap->find(name) == this->booleanIdentifierToIndexMap->end(), storm::exceptions::InvalidArgumentException, "Integer identifier '" << name << "' already registered."); - - this->integerIdentifierToIndexMap->emplace(name, this->integerValues.size()); - this->integerValues.push_back(initialValue); + LOG_THROW(this->identifierToValueMap.find(name) == this->identifierToValueMap.end(), storm::exceptions::InvalidArgumentException, "Identifier '" << name << "' already registered."); + this->identifierToValueMap.emplace(name, initialValue); } void SimpleValuation::addDoubleIdentifier(std::string const& name, double initialValue) { - LOG_THROW(this->booleanIdentifierToIndexMap->find(name) == this->booleanIdentifierToIndexMap->end(), storm::exceptions::InvalidArgumentException, "Double identifier '" << name << "' already registered."); - - this->doubleIdentifierToIndexMap->emplace(name, this->doubleValues.size()); - this->doubleValues.push_back(initialValue); + LOG_THROW(this->identifierToValueMap.find(name) == this->identifierToValueMap.end(), storm::exceptions::InvalidArgumentException, "Identifier '" << name << "' already registered."); + this->identifierToValueMap.emplace(name, initialValue); } void SimpleValuation::setBooleanValue(std::string const& name, bool value) { - this->booleanValues[this->booleanIdentifierToIndexMap->at(name)] = value; + this->identifierToValueMap[name] = value; } void SimpleValuation::setIntegerValue(std::string const& name, int_fast64_t value) { - this->integerValues[this->integerIdentifierToIndexMap->at(name)] = value; + this->identifierToValueMap[name] = value; } void SimpleValuation::setDoubleValue(std::string const& name, double value) { - this->doubleValues[this->doubleIdentifierToIndexMap->at(name)] = value; + this->identifierToValueMap[name] = value; + } + + void SimpleValuation::removeIdentifier(std::string const& name) { + auto nameValuePair = this->identifierToValueMap.find(name); + LOG_THROW(nameValuePair != this->identifierToValueMap.end(), storm::exceptions::InvalidArgumentException, "Deleting unknown identifier '" << name << "'."); + this->identifierToValueMap.erase(nameValuePair); + } + + ExpressionReturnType SimpleValuation::getIdentifierType(std::string const& name) const { + auto nameValuePair = this->identifierToValueMap.find(name); + LOG_THROW(nameValuePair != this->identifierToValueMap.end(), storm::exceptions::InvalidAccessException, "Access to unkown identifier '" << name << "'."); + if (nameValuePair->second.type() == typeid(bool)) { + return ExpressionReturnType::Bool; + } else if (nameValuePair->second.type() == typeid(int_fast64_t)) { + return ExpressionReturnType::Int; + } else { + return ExpressionReturnType::Double; + } + } + + bool SimpleValuation::containsBooleanIdentifier(std::string const& name) const { + auto nameValuePair = this->identifierToValueMap.find(name); + if (nameValuePair == this->identifierToValueMap.end()) { + return false; + } + return nameValuePair->second.type() == typeid(bool); + } + + bool SimpleValuation::containsIntegerIdentifier(std::string const& name) const { + auto nameValuePair = this->identifierToValueMap.find(name); + if (nameValuePair == this->identifierToValueMap.end()) { + return false; + } + return nameValuePair->second.type() == typeid(int_fast64_t); + } + + bool SimpleValuation::containsDoubleIdentifier(std::string const& name) const { + auto nameValuePair = this->identifierToValueMap.find(name); + if (nameValuePair == this->identifierToValueMap.end()) { + return false; + } + return nameValuePair->second.type() == typeid(double); } bool SimpleValuation::getBooleanValue(std::string const& name) const { - auto const& nameIndexPair = this->booleanIdentifierToIndexMap->find(name); - return this->booleanValues[nameIndexPair->second]; + auto nameValuePair = this->identifierToValueMap.find(name); + LOG_THROW(nameValuePair != this->identifierToValueMap.end(), storm::exceptions::InvalidAccessException, "Access to unkown identifier '" << name << "'."); + return boost::get<bool>(nameValuePair->second); } int_fast64_t SimpleValuation::getIntegerValue(std::string const& name) const { - auto const& nameIndexPair = this->integerIdentifierToIndexMap->find(name); - return this->integerValues[nameIndexPair->second]; + auto nameValuePair = this->identifierToValueMap.find(name); + LOG_THROW(nameValuePair != this->identifierToValueMap.end(), storm::exceptions::InvalidAccessException, "Access to unkown identifier '" << name << "'."); + return boost::get<int_fast64_t>(nameValuePair->second); } double SimpleValuation::getDoubleValue(std::string const& name) const { - auto const& nameIndexPair = this->doubleIdentifierToIndexMap->find(name); - return this->doubleValues[nameIndexPair->second]; + auto nameValuePair = this->identifierToValueMap.find(name); + LOG_THROW(nameValuePair != this->identifierToValueMap.end(), storm::exceptions::InvalidAccessException, "Access to unkown identifier '" << name << "'."); + return boost::get<double>(nameValuePair->second); } - std::ostream& operator<<(std::ostream& stream, SimpleValuation const& valuation) { - stream << "valuation { bool ["; - if (!valuation.booleanValues.empty()) { - for (uint_fast64_t i = 0; i < valuation.booleanValues.size() - 1; ++i) { - stream << valuation.booleanValues[i] << ", "; + std::size_t SimpleValuation::getNumberOfIdentifiers() const { + return this->identifierToValueMap.size(); + } + + std::set<std::string> SimpleValuation::getIdentifiers() const { + std::set<std::string> result; + for (auto const& nameValuePair : this->identifierToValueMap) { + result.insert(nameValuePair.first); + } + return result; + } + + std::set<std::string> SimpleValuation::getBooleanIdentifiers() const { + std::set<std::string> result; + for (auto const& nameValuePair : this->identifierToValueMap) { + if (nameValuePair.second.type() == typeid(bool)) { + result.insert(nameValuePair.first); + } + } + return result; + } + + std::set<std::string> SimpleValuation::getIntegerIdentifiers() const { + std::set<std::string> result; + for (auto const& nameValuePair : this->identifierToValueMap) { + if (nameValuePair.second.type() == typeid(int_fast64_t)) { + result.insert(nameValuePair.first); } - stream << valuation.booleanValues.back(); } - stream << "] int ["; - if (!valuation.integerValues.empty()) { - for (uint_fast64_t i = 0; i < valuation.integerValues.size() - 1; ++i) { - stream << valuation.integerValues[i] << ", "; + return result; + } + + std::set<std::string> SimpleValuation::getDoubleIdentifiers() const { + std::set<std::string> result; + for (auto const& nameValuePair : this->identifierToValueMap) { + if (nameValuePair.second.type() == typeid(double)) { + result.insert(nameValuePair.first); } - stream << valuation.integerValues.back(); } - stream << "] double ["; - if (!valuation.doubleValues.empty()) { - for (uint_fast64_t i = 0; i < valuation.doubleValues.size() - 1; ++i) { - stream << valuation.doubleValues[i] << ", "; + return result; + } + + std::ostream& operator<<(std::ostream& stream, SimpleValuation const& valuation) { + stream << "{ "; + uint_fast64_t elementIndex = 0; + for (auto const& nameValuePair : valuation.identifierToValueMap) { + stream << nameValuePair.first << " -> " << nameValuePair.second << " "; + ++elementIndex; + if (elementIndex < valuation.identifierToValueMap.size()) { + stream << ", "; } - stream << valuation.doubleValues.back(); } - stream << "] }"; + stream << "}"; return stream; } std::size_t SimpleValuationPointerHash::operator()(SimpleValuation* valuation) const { size_t seed = 0; - for (auto const& value : valuation->booleanValues) { - boost::hash_combine<bool>(seed, value); - } - for (auto const& value : valuation->integerValues) { - boost::hash_combine<int_fast64_t>(seed, value); - } - for (auto const& value : valuation->doubleValues) { - boost::hash_combine<double>(seed, value); + for (auto const& nameValuePair : valuation->identifierToValueMap) { + boost::hash_combine(seed, nameValuePair.first); + boost::hash_combine(seed, nameValuePair.second); } return seed; } @@ -107,21 +171,7 @@ namespace storm { } bool SimpleValuationPointerLess::operator()(SimpleValuation* valuation1, SimpleValuation* valuation2) const { - // Compare boolean variables. - bool less = valuation1->booleanValues < valuation2->booleanValues; - if (less) { - return true; - } - less = valuation1->integerValues < valuation2->integerValues; - if (less) { - return true; - } - less = valuation1->doubleValues < valuation2->doubleValues; - if (less) { - return true; - } else { - return false; - } + return valuation1->identifierToValueMap < valuation2->identifierToValueMap; } } } \ No newline at end of file diff --git a/src/storage/expressions/SimpleValuation.h b/src/storage/expressions/SimpleValuation.h index a196921a6..fccfe2faa 100644 --- a/src/storage/expressions/SimpleValuation.h +++ b/src/storage/expressions/SimpleValuation.h @@ -1,12 +1,12 @@ #ifndef STORM_STORAGE_EXPRESSIONS_SIMPLEVALUATION_H_ #define STORM_STORAGE_EXPRESSIONS_SIMPLEVALUATION_H_ -#include <memory> -#include <vector> -#include <unordered_map> +#include <boost/container/flat_map.hpp> +#include <boost/variant.hpp> #include <iostream> #include "src/storage/expressions/Valuation.h" +#include "src/storage/expressions/ExpressionReturnType.h" #include "src/utility/OsDetection.h" namespace storm { @@ -16,12 +16,8 @@ namespace storm { friend class SimpleValuationPointerHash; friend class SimpleValuationPointerLess; - /*! - * Creates a simple valuation without any identifiers. - */ - SimpleValuation(); - // Instantiate some constructors and assignments with their default implementations. + SimpleValuation() = default; SimpleValuation(SimpleValuation const&) = default; SimpleValuation& operator=(SimpleValuation const&) = default; #ifndef WINDOWS @@ -83,7 +79,30 @@ namespace storm { */ void setDoubleValue(std::string const& name, double value); + /*! + * Removes the given identifier from this valuation. + * + * @param name The name of the identifier that is to be removed. + */ + void removeIdentifier(std::string const& name); + + /*! + * Retrieves the type of the identifier with the given name. + * + * @param name The name of the identifier whose type to retrieve. + * @return The type of the identifier with the given name. + */ + ExpressionReturnType getIdentifierType(std::string const& name) const; + // Override base class methods. + virtual bool containsBooleanIdentifier(std::string const& name) const override; + virtual bool containsIntegerIdentifier(std::string const& name) const override; + virtual bool containsDoubleIdentifier(std::string const& name) const override; + virtual std::size_t getNumberOfIdentifiers() const override; + virtual std::set<std::string> getIdentifiers() const override; + virtual std::set<std::string> getBooleanIdentifiers() const override; + virtual std::set<std::string> getIntegerIdentifiers() const override; + virtual std::set<std::string> getDoubleIdentifiers() const override; virtual bool getBooleanValue(std::string const& name) const override; virtual int_fast64_t getIntegerValue(std::string const& name) const override; virtual double getDoubleValue(std::string const& name) const override; @@ -92,22 +111,7 @@ namespace storm { private: // A mapping of boolean identifiers to their local indices in the value container. - std::shared_ptr<std::unordered_map<std::string, uint_fast64_t>> booleanIdentifierToIndexMap; - - // A mapping of integer identifiers to their local indices in the value container. - std::shared_ptr<std::unordered_map<std::string, uint_fast64_t>> integerIdentifierToIndexMap; - - // A mapping of double identifiers to their local indices in the value container. - std::shared_ptr<std::unordered_map<std::string, uint_fast64_t>> doubleIdentifierToIndexMap; - - // The value container for all boolean identifiers. - std::vector<bool> booleanValues; - - // The value container for all integer identifiers. - std::vector<int_fast64_t> integerValues; - - // The value container for all double identifiers. - std::vector<double> doubleValues; + boost::container::flat_map<std::string, boost::variant<bool, int_fast64_t, double>> identifierToValueMap; }; /*! diff --git a/src/storage/expressions/SubstitutionVisitor.cpp b/src/storage/expressions/SubstitutionVisitor.cpp index 2559b474b..43aa01ab3 100644 --- a/src/storage/expressions/SubstitutionVisitor.cpp +++ b/src/storage/expressions/SubstitutionVisitor.cpp @@ -13,8 +13,8 @@ namespace storm { } template<typename MapType> - Expression SubstitutionVisitor<MapType>::substitute(BaseExpression const* expression) { - expression->accept(this); + Expression SubstitutionVisitor<MapType>::substitute(Expression const& expression) { + expression.getBaseExpression().accept(this); return Expression(this->expressionStack.top()); } diff --git a/src/storage/expressions/SubstitutionVisitor.h b/src/storage/expressions/SubstitutionVisitor.h index bc58148e3..0ebc0941e 100644 --- a/src/storage/expressions/SubstitutionVisitor.h +++ b/src/storage/expressions/SubstitutionVisitor.h @@ -26,7 +26,7 @@ namespace storm { * @return The expression in which all identifiers in the key set of the previously given mapping are * substituted with the mapped-to expressions. */ - Expression substitute(BaseExpression const* expression); + Expression substitute(Expression const& expression); virtual void visit(IfThenElseExpression const* expression) override; virtual void visit(BinaryBooleanFunctionExpression const* expression) override; diff --git a/src/storage/expressions/TypeCheckVisitor.cpp b/src/storage/expressions/TypeCheckVisitor.cpp index 1c5899d61..521453042 100644 --- a/src/storage/expressions/TypeCheckVisitor.cpp +++ b/src/storage/expressions/TypeCheckVisitor.cpp @@ -12,8 +12,8 @@ namespace storm { } template<typename MapType> - void TypeCheckVisitor<MapType>::check(BaseExpression const* expression) { - expression->accept(this); + void TypeCheckVisitor<MapType>::check(Expression const& expression) { + expression.getBaseExpression().accept(this); } template<typename MapType> diff --git a/src/storage/expressions/TypeCheckVisitor.h b/src/storage/expressions/TypeCheckVisitor.h index 7772e0e7e..0cbf40f92 100644 --- a/src/storage/expressions/TypeCheckVisitor.h +++ b/src/storage/expressions/TypeCheckVisitor.h @@ -24,7 +24,7 @@ namespace storm { * * @param expression The expression in which to check the types. */ - void check(BaseExpression const* expression); + void check(Expression const& expression); virtual void visit(IfThenElseExpression const* expression) override; virtual void visit(BinaryBooleanFunctionExpression const* expression) override; diff --git a/src/storage/expressions/UnaryBooleanFunctionExpression.cpp b/src/storage/expressions/UnaryBooleanFunctionExpression.cpp index 7e405bb29..1d9e59119 100644 --- a/src/storage/expressions/UnaryBooleanFunctionExpression.cpp +++ b/src/storage/expressions/UnaryBooleanFunctionExpression.cpp @@ -13,6 +13,12 @@ namespace storm { return this->operatorType; } + storm::expressions::OperatorType UnaryBooleanFunctionExpression::getOperator() const { + switch (this->getOperatorType()) { + case OperatorType::Not: return storm::expressions::OperatorType::Not; + } + } + bool UnaryBooleanFunctionExpression::evaluateAsBool(Valuation const* valuation) const { LOG_THROW(this->hasBooleanReturnType(), storm::exceptions::InvalidTypeException, "Unable to evaluate expression as boolean."); diff --git a/src/storage/expressions/UnaryBooleanFunctionExpression.h b/src/storage/expressions/UnaryBooleanFunctionExpression.h index 1f2800d8e..c69c6f886 100644 --- a/src/storage/expressions/UnaryBooleanFunctionExpression.h +++ b/src/storage/expressions/UnaryBooleanFunctionExpression.h @@ -32,6 +32,7 @@ namespace storm { virtual ~UnaryBooleanFunctionExpression() = default; // Override base class methods. + virtual storm::expressions::OperatorType getOperator() const override; virtual bool evaluateAsBool(Valuation const* valuation = nullptr) const override; virtual std::shared_ptr<BaseExpression const> simplify() const override; virtual void accept(ExpressionVisitor* visitor) const override; diff --git a/src/storage/expressions/UnaryExpression.cpp b/src/storage/expressions/UnaryExpression.cpp index cc75e6fa3..169466003 100644 --- a/src/storage/expressions/UnaryExpression.cpp +++ b/src/storage/expressions/UnaryExpression.cpp @@ -9,6 +9,10 @@ namespace storm { // Intentionally left empty. } + bool UnaryExpression::isFunctionApplication() const { + return true; + } + bool UnaryExpression::containsVariables() const { return this->getOperand()->containsVariables(); } diff --git a/src/storage/expressions/UnaryExpression.h b/src/storage/expressions/UnaryExpression.h index 27b903638..5473d3122 100644 --- a/src/storage/expressions/UnaryExpression.h +++ b/src/storage/expressions/UnaryExpression.h @@ -26,6 +26,7 @@ namespace storm { virtual ~UnaryExpression() = default; // Override base class methods. + virtual bool isFunctionApplication() const override; virtual bool containsVariables() const override; virtual uint_fast64_t getArity() const override; virtual std::shared_ptr<BaseExpression const> getOperand(uint_fast64_t operandIndex) const override; diff --git a/src/storage/expressions/UnaryNumericalFunctionExpression.cpp b/src/storage/expressions/UnaryNumericalFunctionExpression.cpp index 02a4bee30..972f2a24a 100644 --- a/src/storage/expressions/UnaryNumericalFunctionExpression.cpp +++ b/src/storage/expressions/UnaryNumericalFunctionExpression.cpp @@ -14,6 +14,14 @@ namespace storm { return this->operatorType; } + storm::expressions::OperatorType UnaryNumericalFunctionExpression::getOperator() const { + switch (this->getOperatorType()) { + case OperatorType::Minus: return storm::expressions::OperatorType::Minus; break; + case OperatorType::Floor: return storm::expressions::OperatorType::Floor; break; + case OperatorType::Ceil: return storm::expressions::OperatorType::Ceil; break; + } + } + int_fast64_t UnaryNumericalFunctionExpression::evaluateAsInt(Valuation const* valuation) const { LOG_THROW(this->hasIntegralReturnType(), storm::exceptions::InvalidTypeException, "Unable to evaluate expression as integer."); diff --git a/src/storage/expressions/UnaryNumericalFunctionExpression.h b/src/storage/expressions/UnaryNumericalFunctionExpression.h index daa0df40f..10b85e63e 100644 --- a/src/storage/expressions/UnaryNumericalFunctionExpression.h +++ b/src/storage/expressions/UnaryNumericalFunctionExpression.h @@ -32,6 +32,7 @@ namespace storm { virtual ~UnaryNumericalFunctionExpression() = default; // Override base class methods. + virtual storm::expressions::OperatorType getOperator() const override; virtual int_fast64_t evaluateAsInt(Valuation const* valuation = nullptr) const override; virtual double evaluateAsDouble(Valuation const* valuation = nullptr) const override; virtual std::shared_ptr<BaseExpression const> simplify() const override; diff --git a/src/storage/expressions/Valuation.h b/src/storage/expressions/Valuation.h index 7aa32a859..a09c4f07c 100644 --- a/src/storage/expressions/Valuation.h +++ b/src/storage/expressions/Valuation.h @@ -2,6 +2,7 @@ #define STORM_STORAGE_EXPRESSIONS_VALUATION_H_ #include <string> +#include <set> namespace storm { namespace expressions { @@ -34,6 +35,66 @@ namespace storm { * @return The value of the double identifier. */ virtual double getDoubleValue(std::string const& name) const = 0; + + /*! + * Retrieves whether there exists a boolean identifier with the given name in the valuation. + * + * @param name The name of the boolean identifier to query. + * @return True iff the identifier exists and is of boolean type. + */ + virtual bool containsBooleanIdentifier(std::string const& name) const = 0; + + /*! + * Retrieves whether there exists a integer identifier with the given name in the valuation. + * + * @param name The name of the integer identifier to query. + * @return True iff the identifier exists and is of boolean type. + */ + virtual bool containsIntegerIdentifier(std::string const& name) const = 0; + + /*! + * Retrieves whether there exists a double identifier with the given name in the valuation. + * + * @param name The name of the double identifier to query. + * @return True iff the identifier exists and is of boolean type. + */ + virtual bool containsDoubleIdentifier(std::string const& name) const = 0; + + /*! + * Retrieves the number of identifiers in this valuation. + * + * @return The number of identifiers in this valuation. + */ + virtual std::size_t getNumberOfIdentifiers() const = 0; + + /*! + * Retrieves the set of all identifiers contained in this valuation. + * + * @return The set of all identifiers contained in this valuation. + */ + virtual std::set<std::string> getIdentifiers() const = 0; + + /*! + * Retrieves the set of boolean identifiers contained in this valuation. + * + * @return The set of boolean identifiers contained in this valuation. + */ + virtual std::set<std::string> getBooleanIdentifiers() const = 0; + + /*! + * Retrieves the set of integer identifiers contained in this valuation. + * + * @return The set of integer identifiers contained in this valuation. + */ + virtual std::set<std::string> getIntegerIdentifiers() const = 0; + + /*! + * Retrieves the set of double identifiers contained in this valuation. + * + * @return The set of double identifiers contained in this valuation. + */ + virtual std::set<std::string> getDoubleIdentifiers() const = 0; + }; } } diff --git a/src/storage/prism/Program.cpp b/src/storage/prism/Program.cpp index 10a1114e6..617a1c52b 100644 --- a/src/storage/prism/Program.cpp +++ b/src/storage/prism/Program.cpp @@ -13,7 +13,7 @@ namespace storm { Program::Program(ModelType modelType, std::vector<Constant> const& constants, std::vector<BooleanVariable> const& globalBooleanVariables, std::vector<IntegerVariable> const& globalIntegerVariables, std::vector<Formula> const& formulas, std::vector<Module> const& modules, std::vector<RewardModel> const& rewardModels, bool fixInitialConstruct, storm::prism::InitialConstruct const& initialConstruct, std::vector<Label> const& labels, std::string const& filename, uint_fast64_t lineNumber, bool checkValidity) : LocatedInformation(filename, lineNumber), modelType(modelType), constants(constants), constantToIndexMap(), globalBooleanVariables(globalBooleanVariables), globalBooleanVariableToIndexMap(), globalIntegerVariables(globalIntegerVariables), globalIntegerVariableToIndexMap(), formulas(formulas), formulaToIndexMap(), modules(modules), moduleToIndexMap(), rewardModels(rewardModels), rewardModelToIndexMap(), initialConstruct(initialConstruct), labels(labels), labelToIndexMap(), actions(), actionsToModuleIndexMap(), variableToModuleIndexMap() { this->createMappings(); - // Create a new initial construct if none was given explicitly. + // Create a new initial construct if the corresponding flag was set. if (fixInitialConstruct) { if (this->getInitialConstruct().getInitialStatesExpression().isFalse()) { storm::expressions::Expression newInitialExpression = storm::expressions::Expression::createTrue(); diff --git a/src/utility/counterexamples.h b/src/utility/counterexamples.h index 23b5b372e..8b233e402 100644 --- a/src/utility/counterexamples.h +++ b/src/utility/counterexamples.h @@ -36,8 +36,8 @@ namespace storm { // for (auto state : psiStates) { // analysisInformation[state] = boost::container::flat_set<uint_fast64_t>(); // for (auto const& predecessorEntry : backwardTransitions.getRow(state)) { -// if (predecessorEntry.first != state && !psiStates.get(predecessorEntry.first)) { -// worklist.push(std::make_pair(predecessorEntry.first, state)); +// if (predecessorEntry.getColumn() != state && !psiStates.get(predecessorEntry.getColumn())) { +// worklist.push(std::make_pair(predecessorEntry.getColumn(), state)); // } // } // } @@ -57,7 +57,7 @@ namespace storm { // bool choiceTargetsTargetState = false; // // for (auto& entry : transitionMatrix.getRow(currentChoice)) { -// if (entry.first == targetState) { +// if (entry.getColumn() == targetState) { // choiceTargetsTargetState = true; // break; // } @@ -79,8 +79,8 @@ namespace storm { // if (analysisInformation[currentState].size() != analysisInformationSizeBefore) { // for (auto& predecessorEntry : backwardTransitions.getRow(currentState)) { // // Only put the predecessor in the worklist if it's not already a target state. -// if (!psiStates.get(predecessorEntry.first)) { -// worklist.push(std::make_pair(predecessorEntry.first, currentState)); +// if (!psiStates.get(predecessorEntry.getColumn())) { +// worklist.push(std::make_pair(predecessorEntry.getColumn(), currentState)); // } // } // } @@ -96,9 +96,9 @@ namespace storm { for (auto state : psiStates) { analysisInformation[state] = boost::container::flat_set<uint_fast64_t>(); for (auto const& predecessorEntry : backwardTransitions.getRow(state)) { - if (predecessorEntry.first != state && !statesInWorkList.get(predecessorEntry.first) && !psiStates.get(predecessorEntry.first)) { - worklist.push(predecessorEntry.first); - statesInWorkList.set(predecessorEntry.first); + if (predecessorEntry.getColumn() != state && !statesInWorkList.get(predecessorEntry.getColumn()) && !psiStates.get(predecessorEntry.getColumn())) { + worklist.push(predecessorEntry.getColumn()); + statesInWorkList.set(predecessorEntry.getColumn()); markedStates.set(state); } } @@ -116,7 +116,7 @@ namespace storm { bool modifiedChoice = false; for (auto const& entry : transitionMatrix.getRow(currentChoice)) { - if (markedStates.get(entry.first)) { + if (markedStates.get(entry.getColumn())) { modifiedChoice = true; break; } @@ -127,9 +127,9 @@ namespace storm { // and the choice labels. if (modifiedChoice) { for (auto const& entry : transitionMatrix.getRow(currentChoice)) { - if (markedStates.get(entry.first)) { + if (markedStates.get(entry.getColumn())) { boost::container::flat_set<uint_fast64_t> tmpIntersection; - std::set_intersection(analysisInformation[currentState].begin(), analysisInformation[currentState].end(), analysisInformation[entry.first].begin(), analysisInformation[entry.first].end(), std::inserter(tmpIntersection, tmpIntersection.begin())); + std::set_intersection(analysisInformation[currentState].begin(), analysisInformation[currentState].end(), analysisInformation[entry.getColumn()].begin(), analysisInformation[entry.getColumn()].end(), std::inserter(tmpIntersection, tmpIntersection.begin())); std::set_intersection(analysisInformation[currentState].begin(), analysisInformation[currentState].end(), choiceLabeling[currentChoice].begin(), choiceLabeling[currentChoice].end(), std::inserter(tmpIntersection, tmpIntersection.begin())); analysisInformation[currentState] = std::move(tmpIntersection); } @@ -142,9 +142,9 @@ namespace storm { if (analysisInformation[currentState].size() != analysisInformationSizeBefore) { for (auto const& predecessorEntry : backwardTransitions.getRow(currentState)) { // Only put the predecessor in the worklist if it's not already a target state. - if (!psiStates.get(predecessorEntry.first) && !statesInWorkList.get(predecessorEntry.first)) { - worklist.push(predecessorEntry.first); - statesInWorkList.set(predecessorEntry.first); + if (!psiStates.get(predecessorEntry.getColumn()) && !statesInWorkList.get(predecessorEntry.getColumn())) { + worklist.push(predecessorEntry.getColumn()); + statesInWorkList.set(predecessorEntry.getColumn()); } } markedStates.set(currentState, true); diff --git a/src/utility/graph.h b/src/utility/graph.h index 01d0a7f4f..3d0d95fb1 100644 --- a/src/utility/graph.h +++ b/src/utility/graph.h @@ -79,16 +79,16 @@ namespace storm { } for (typename storm::storage::SparseMatrix<T>::const_iterator entryIt = backwardTransitions.begin(currentState), entryIte = backwardTransitions.end(currentState); entryIt != entryIte; ++entryIt) { - if (phiStates[entryIt->first] && (!statesWithProbabilityGreater0.get(entryIt->first) || (useStepBound && remainingSteps[entryIt->first] < currentStepBound - 1))) { + if (phiStates[entryIt->getColumn()] && (!statesWithProbabilityGreater0.get(entryIt->getColumn()) || (useStepBound && remainingSteps[entryIt->getColumn()] < currentStepBound - 1))) { // If we don't have a bound on the number of steps to take, just add the state to the stack. if (!useStepBound) { - statesWithProbabilityGreater0.set(entryIt->first, true); - stack.push_back(entryIt->first); + statesWithProbabilityGreater0.set(entryIt->getColumn(), true); + stack.push_back(entryIt->getColumn()); } else if (currentStepBound > 0) { // If there is at least one more step to go, we need to push the state and the new number of steps. - remainingSteps[entryIt->first] = currentStepBound - 1; - statesWithProbabilityGreater0.set(entryIt->first, true); - stack.push_back(entryIt->first); + remainingSteps[entryIt->getColumn()] = currentStepBound - 1; + statesWithProbabilityGreater0.set(entryIt->getColumn(), true); + stack.push_back(entryIt->getColumn()); stepStack.push_back(currentStepBound - 1); } } @@ -211,16 +211,16 @@ namespace storm { } for (typename storm::storage::SparseMatrix<T>::const_iterator entryIt = backwardTransitions.begin(currentState), entryIte = backwardTransitions.end(currentState); entryIt != entryIte; ++entryIt) { - if (phiStates.get(entryIt->first) && (!statesWithProbabilityGreater0.get(entryIt->first) || (useStepBound && remainingSteps[entryIt->first] < currentStepBound - 1))) { + if (phiStates.get(entryIt->getColumn()) && (!statesWithProbabilityGreater0.get(entryIt->getColumn()) || (useStepBound && remainingSteps[entryIt->getColumn()] < currentStepBound - 1))) { // If we don't have a bound on the number of steps to take, just add the state to the stack. if (!useStepBound) { - statesWithProbabilityGreater0.set(entryIt->first, true); - stack.push_back(entryIt->first); + statesWithProbabilityGreater0.set(entryIt->getColumn(), true); + stack.push_back(entryIt->getColumn()); } else if (currentStepBound > 0) { // If there is at least one more step to go, we need to push the state and the new number of steps. - remainingSteps[entryIt->first] = currentStepBound - 1; - statesWithProbabilityGreater0.set(entryIt->first, true); - stack.push_back(entryIt->first); + remainingSteps[entryIt->getColumn()] = currentStepBound - 1; + statesWithProbabilityGreater0.set(entryIt->getColumn(), true); + stack.push_back(entryIt->getColumn()); stepStack.push_back(currentStepBound - 1); } } @@ -290,13 +290,13 @@ namespace storm { stack.pop_back(); for (typename storm::storage::SparseMatrix<T>::const_iterator predecessorEntryIt = backwardTransitions.begin(currentState), predecessorEntryIte = backwardTransitions.end(currentState); predecessorEntryIt != predecessorEntryIte; ++predecessorEntryIt) { - if (phiStates.get(predecessorEntryIt->first) && !nextStates.get(predecessorEntryIt->first)) { + if (phiStates.get(predecessorEntryIt->getColumn()) && !nextStates.get(predecessorEntryIt->getColumn())) { // Check whether the predecessor has only successors in the current state set for one of the // nondeterminstic choices. - for (uint_fast64_t row = nondeterministicChoiceIndices[predecessorEntryIt->first]; row < nondeterministicChoiceIndices[predecessorEntryIt->first + 1]; ++row) { + for (uint_fast64_t row = nondeterministicChoiceIndices[predecessorEntryIt->getColumn()]; row < nondeterministicChoiceIndices[predecessorEntryIt->getColumn() + 1]; ++row) { bool allSuccessorsInCurrentStates = true; for (typename storm::storage::SparseMatrix<T>::const_iterator successorEntryIt = transitionMatrix.begin(row), successorEntryIte = transitionMatrix.end(row); successorEntryIt != successorEntryIte; ++successorEntryIt) { - if (!currentStates.get(successorEntryIt->first)) { + if (!currentStates.get(successorEntryIt->getColumn())) { allSuccessorsInCurrentStates = false; break; } @@ -306,8 +306,8 @@ namespace storm { // add it to the set of states for the next iteration and perform a backward search from // that state. if (allSuccessorsInCurrentStates) { - nextStates.set(predecessorEntryIt->first, true); - stack.push_back(predecessorEntryIt->first); + nextStates.set(predecessorEntryIt->getColumn(), true); + stack.push_back(predecessorEntryIt->getColumn()); break; } } @@ -401,14 +401,14 @@ namespace storm { } for(typename storm::storage::SparseMatrix<T>::const_iterator predecessorEntryIt = backwardTransitions.begin(currentState), predecessorEntryIte = backwardTransitions.end(currentState); predecessorEntryIt != predecessorEntryIte; ++predecessorEntryIt) { - if (phiStates.get(predecessorEntryIt->first) && (!statesWithProbabilityGreater0.get(predecessorEntryIt->first) || (useStepBound && remainingSteps[predecessorEntryIt->first] < currentStepBound - 1))) { + if (phiStates.get(predecessorEntryIt->getColumn()) && (!statesWithProbabilityGreater0.get(predecessorEntryIt->getColumn()) || (useStepBound && remainingSteps[predecessorEntryIt->getColumn()] < currentStepBound - 1))) { // Check whether the predecessor has at least one successor in the current state set for every // nondeterministic choice. bool addToStatesWithProbabilityGreater0 = true; - for (uint_fast64_t row = nondeterministicChoiceIndices[predecessorEntryIt->first]; row < nondeterministicChoiceIndices[predecessorEntryIt->first + 1]; ++row) { + for (uint_fast64_t row = nondeterministicChoiceIndices[predecessorEntryIt->getColumn()]; row < nondeterministicChoiceIndices[predecessorEntryIt->getColumn() + 1]; ++row) { bool hasAtLeastOneSuccessorWithProbabilityGreater0 = false; for (typename storm::storage::SparseMatrix<T>::const_iterator successorEntryIt = transitionMatrix.begin(row), successorEntryIte = transitionMatrix.end(row); successorEntryIt != successorEntryIte; ++successorEntryIt) { - if (statesWithProbabilityGreater0.get(successorEntryIt->first)) { + if (statesWithProbabilityGreater0.get(successorEntryIt->getColumn())) { hasAtLeastOneSuccessorWithProbabilityGreater0 = true; break; } @@ -424,13 +424,13 @@ namespace storm { if (addToStatesWithProbabilityGreater0) { // If we don't have a bound on the number of steps to take, just add the state to the stack. if (!useStepBound) { - statesWithProbabilityGreater0.set(predecessorEntryIt->first, true); - stack.push_back(predecessorEntryIt->first); + statesWithProbabilityGreater0.set(predecessorEntryIt->getColumn(), true); + stack.push_back(predecessorEntryIt->getColumn()); } else if (currentStepBound > 0) { // If there is at least one more step to go, we need to push the state and the new number of steps. - remainingSteps[predecessorEntryIt->first] = currentStepBound - 1; - statesWithProbabilityGreater0.set(predecessorEntryIt->first, true); - stack.push_back(predecessorEntryIt->first); + remainingSteps[predecessorEntryIt->getColumn()] = currentStepBound - 1; + statesWithProbabilityGreater0.set(predecessorEntryIt->getColumn(), true); + stack.push_back(predecessorEntryIt->getColumn()); stepStack.push_back(currentStepBound - 1); } } @@ -501,12 +501,12 @@ namespace storm { stack.pop_back(); for(typename storm::storage::SparseMatrix<T>::const_iterator predecessorEntryIt = backwardTransitions.begin(currentState), predecessorEntryIte = backwardTransitions.end(currentState); predecessorEntryIt != predecessorEntryIte; ++predecessorEntryIt) { - if (phiStates.get(predecessorEntryIt->first) && !nextStates.get(predecessorEntryIt->first)) { + if (phiStates.get(predecessorEntryIt->getColumn()) && !nextStates.get(predecessorEntryIt->getColumn())) { // Check whether the predecessor has only successors in the current state set for all of the // nondeterminstic choices. bool allSuccessorsInCurrentStatesForAllChoices = true; - for (typename storm::storage::SparseMatrix<T>::const_iterator successorEntryIt = transitionMatrix.begin(nondeterministicChoiceIndices[predecessorEntryIt->first]), successorEntryIte = transitionMatrix.begin(nondeterministicChoiceIndices[predecessorEntryIt->first + 1]); successorEntryIt != successorEntryIte; ++successorEntryIt) { - if (!currentStates.get(successorEntryIt->first)) { + for (typename storm::storage::SparseMatrix<T>::const_iterator successorEntryIt = transitionMatrix.begin(nondeterministicChoiceIndices[predecessorEntryIt->getColumn()]), successorEntryIte = transitionMatrix.begin(nondeterministicChoiceIndices[predecessorEntryIt->getColumn() + 1]); successorEntryIt != successorEntryIte; ++successorEntryIt) { + if (!currentStates.get(successorEntryIt->getColumn())) { allSuccessorsInCurrentStatesForAllChoices = false; goto afterCheckLoop; } @@ -517,8 +517,8 @@ namespace storm { // add it to the set of states for the next iteration and perform a backward search from // that state. if (allSuccessorsInCurrentStatesForAllChoices) { - nextStates.set(predecessorEntryIt->first, true); - stack.push_back(predecessorEntryIt->first); + nextStates.set(predecessorEntryIt->getColumn(), true); + stack.push_back(predecessorEntryIt->getColumn()); } } } diff --git a/src/utility/matrix.h b/src/utility/matrix.h index 446fb413f..1027e7e42 100644 --- a/src/utility/matrix.h +++ b/src/utility/matrix.h @@ -32,7 +32,7 @@ namespace storm { // If a valid choice for this state is defined, we copy over the corresponding entries. typename storm::storage::SparseMatrix<T>::const_rows selectedRow = transitionMatrix.getRow(choice); for (auto const& entry : selectedRow) { - matrixBuilder.addNextValue(state, entry.first, entry.second); + matrixBuilder.addNextValue(state, entry.getColumn(), entry.getValue()); } } else { // If no valid choice for the state is defined, we insert a self-loop. diff --git a/test/functional/modelchecker/GmmxxDtmcPrctlModelCheckerTest.cpp b/test/functional/modelchecker/GmmxxDtmcPrctlModelCheckerTest.cpp index fdff14a66..dc2d6d703 100644 --- a/test/functional/modelchecker/GmmxxDtmcPrctlModelCheckerTest.cpp +++ b/test/functional/modelchecker/GmmxxDtmcPrctlModelCheckerTest.cpp @@ -18,7 +18,7 @@ TEST(GmmxxDtmcPrctlModelCheckerTest, Die) { std::shared_ptr<storm::models::Dtmc<double>> dtmc = abstractModel->as<storm::models::Dtmc<double>>(); ASSERT_EQ(dtmc->getNumberOfStates(), 13ull); - ASSERT_EQ(dtmc->getNumberOfTransitions(), 27ull); + ASSERT_EQ(dtmc->getNumberOfTransitions(), 20ull); storm::modelchecker::prctl::SparseDtmcPrctlModelChecker<double> mc(*dtmc, new storm::solver::GmmxxLinearEquationSolver<double>()); @@ -74,7 +74,7 @@ TEST(GmmxxDtmcPrctlModelCheckerTest, Crowds) { std::shared_ptr<storm::models::Dtmc<double>> dtmc = abstractModel->as<storm::models::Dtmc<double>>(); ASSERT_EQ(dtmc->getNumberOfStates(), 8607ull); - ASSERT_EQ(dtmc->getNumberOfTransitions(), 22460ull); + ASSERT_EQ(dtmc->getNumberOfTransitions(), 15113ull); storm::modelchecker::prctl::SparseDtmcPrctlModelChecker<double> mc(*dtmc, new storm::solver::GmmxxLinearEquationSolver<double>()); @@ -119,7 +119,7 @@ TEST(GmmxxDtmcPrctlModelCheckerTest, SynchronousLeader) { std::shared_ptr<storm::models::Dtmc<double>> dtmc = abstractModel->as<storm::models::Dtmc<double>>(); ASSERT_EQ(dtmc->getNumberOfStates(), 12400ull); - ASSERT_EQ(dtmc->getNumberOfTransitions(), 28894ull); + ASSERT_EQ(dtmc->getNumberOfTransitions(), 16495ull); storm::modelchecker::prctl::SparseDtmcPrctlModelChecker<double> mc(*dtmc, new storm::solver::GmmxxLinearEquationSolver<double>()); diff --git a/test/functional/parser/AutoParserTest.cpp b/test/functional/parser/AutoParserTest.cpp index 3c4927ecd..7c3fcb483 100644 --- a/test/functional/parser/AutoParserTest.cpp +++ b/test/functional/parser/AutoParserTest.cpp @@ -24,7 +24,7 @@ TEST(AutoParserTest, BasicParsing) { // Test if parsed correctly. ASSERT_EQ(storm::models::DTMC, modelPtr->getType()); ASSERT_EQ(12, modelPtr->getNumberOfStates()); - ASSERT_EQ(32, modelPtr->getNumberOfTransitions()); + ASSERT_EQ(26, modelPtr->getNumberOfTransitions()); ASSERT_EQ(1, modelPtr->getInitialStates().getNumberOfSetBits()); ASSERT_TRUE(modelPtr->hasAtomicProposition("three")); ASSERT_FALSE(modelPtr->hasStateRewards()); @@ -56,21 +56,21 @@ TEST(AutoParserTest, Decision) { std::shared_ptr<storm::models::AbstractModel<double>> modelPtr = storm::parser::AutoParser::parseModel(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/autoParser/dtmc.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/autoParser.lab"); ASSERT_EQ(storm::models::DTMC, modelPtr->getType()); ASSERT_EQ(12, modelPtr->getNumberOfStates()); - ASSERT_EQ(32, modelPtr->getNumberOfTransitions()); + ASSERT_EQ(26, modelPtr->getNumberOfTransitions()); // Ctmc modelPtr.reset(); modelPtr = storm::parser::AutoParser::parseModel(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/autoParser/ctmc.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/autoParser.lab"); ASSERT_EQ(storm::models::CTMC, modelPtr->getType()); ASSERT_EQ(12, modelPtr->getNumberOfStates()); - ASSERT_EQ(31, modelPtr->getNumberOfTransitions()); + ASSERT_EQ(26, modelPtr->getNumberOfTransitions()); // Mdp modelPtr.reset(); modelPtr = storm::parser::AutoParser::parseModel(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/autoParser/mdp.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/autoParser.lab"); ASSERT_EQ(storm::models::MDP, modelPtr->getType()); ASSERT_EQ(12, modelPtr->getNumberOfStates()); - ASSERT_EQ(36, modelPtr->getNumberOfTransitions()); + ASSERT_EQ(28, modelPtr->getNumberOfTransitions()); // Ctmdp // Note: For now we use the Mdp from above just given the ctmdp hint, since the implementation of the Ctmdp model seems not Quite right yet. @@ -80,12 +80,12 @@ TEST(AutoParserTest, Decision) { modelPtr = storm::parser::AutoParser::parseModel(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/autoParser/ctmdp.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/autoParser.lab"); ASSERT_EQ(storm::models::CTMDP, modelPtr->getType()); ASSERT_EQ(12, modelPtr->getNumberOfStates()); - ASSERT_EQ(36, modelPtr->getNumberOfTransitions()); + ASSERT_EQ(28, modelPtr->getNumberOfTransitions()); // MA modelPtr.reset(); modelPtr = storm::parser::AutoParser::parseModel(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/autoParser/ma.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/autoParser.lab"); ASSERT_EQ(storm::models::MA, modelPtr->getType()); ASSERT_EQ(12, modelPtr->getNumberOfStates()); - ASSERT_EQ(35, modelPtr->getNumberOfTransitions()); + ASSERT_EQ(27, modelPtr->getNumberOfTransitions()); } diff --git a/test/functional/parser/DeterministicModelParserTest.cpp b/test/functional/parser/DeterministicModelParserTest.cpp index 4a62e5e6e..1b6fe71db 100644 --- a/test/functional/parser/DeterministicModelParserTest.cpp +++ b/test/functional/parser/DeterministicModelParserTest.cpp @@ -26,7 +26,7 @@ TEST(DeterministicModelParserTest, BasicDtmcParsing) { storm::models::Dtmc<double> dtmc(storm::parser::DeterministicModelParser::parseDtmc(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/dtmc_general.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/dtmc_general.lab", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/dtmc_general.state.rew", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/dtmc_general.trans.rew")); ASSERT_EQ(8, dtmc.getNumberOfStates()); - ASSERT_EQ(21, dtmc.getNumberOfTransitions()); + ASSERT_EQ(16, dtmc.getNumberOfTransitions()); ASSERT_EQ(2, dtmc.getInitialStates().getNumberOfSetBits()); ASSERT_TRUE(dtmc.getInitialStates().get(0)); @@ -58,7 +58,7 @@ TEST(DeterministicModelParserTest, BasicCtmcParsing) { storm::models::Ctmc<double> ctmc(storm::parser::DeterministicModelParser::parseCtmc(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/dtmc_general.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/dtmc_general.lab", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/dtmc_general.state.rew", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/dtmc_general.trans.rew")); ASSERT_EQ(8, ctmc.getNumberOfStates()); - ASSERT_EQ(21, ctmc.getNumberOfTransitions()); + ASSERT_EQ(16, ctmc.getNumberOfTransitions()); ASSERT_EQ(2, ctmc.getInitialStates().getNumberOfSetBits()); ASSERT_TRUE(ctmc.getInitialStates().get(0)); diff --git a/test/functional/parser/DeterministicSparseTransitionParserTest.cpp b/test/functional/parser/DeterministicSparseTransitionParserTest.cpp index 2b301a1e7..89c5c9c6e 100644 --- a/test/functional/parser/DeterministicSparseTransitionParserTest.cpp +++ b/test/functional/parser/DeterministicSparseTransitionParserTest.cpp @@ -35,68 +35,68 @@ TEST(DeterministicSparseTransitionParserTest, BasicTransitionsParsing) { // Test every entry of the matrix. storm::storage::SparseMatrix<double>::const_iterator cIter = transitionMatrix.begin(0); - ASSERT_EQ(0, cIter->first); - ASSERT_EQ(0, cIter->second); + ASSERT_EQ(0, cIter->getColumn()); + ASSERT_EQ(0, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->first); - ASSERT_EQ(0, cIter->second); + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(0, cIter->getValue()); cIter++; - ASSERT_EQ(2, cIter->first); - ASSERT_EQ(0.5, cIter->second); + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(0.5, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(0.5, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(0.5, cIter->getValue()); cIter++; - ASSERT_EQ(2, cIter->first); - ASSERT_EQ(0, cIter->second); + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(0, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(0.4, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(0.4, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->first); - ASSERT_EQ(0.4, cIter->second); + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0.4, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(0.2, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->first); - ASSERT_EQ(0, cIter->second); + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(0.1, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(0.1, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->first); - ASSERT_EQ(0.1, cIter->second); + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0.1, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(0.1, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(0.1, cIter->getValue()); cIter++; - ASSERT_EQ(6, cIter->first); - ASSERT_EQ(0.7, cIter->second); + ASSERT_EQ(6, cIter->getColumn()); + ASSERT_EQ(0.7, cIter->getValue()); cIter++; - ASSERT_EQ(0, cIter->first); - ASSERT_EQ(0.9, cIter->second); + ASSERT_EQ(0, cIter->getColumn()); + ASSERT_EQ(0.9, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(0, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(0, cIter->getValue()); cIter++; - ASSERT_EQ(6, cIter->first); - ASSERT_EQ(0.1, cIter->second); + ASSERT_EQ(6, cIter->getColumn()); + ASSERT_EQ(0.1, cIter->getValue()); cIter++; - ASSERT_EQ(6, cIter->first); - ASSERT_EQ(0.224653, cIter->second); + ASSERT_EQ(6, cIter->getColumn()); + ASSERT_EQ(0.224653, cIter->getValue()); cIter++; - ASSERT_EQ(7, cIter->first); - ASSERT_EQ(0.775347, cIter->second); + ASSERT_EQ(7, cIter->getColumn()); + ASSERT_EQ(0.775347, cIter->getValue()); } TEST(DeterministicSparseTransitionParserTest, BasicTransitionsRewardsParsing) { @@ -112,56 +112,56 @@ TEST(DeterministicSparseTransitionParserTest, BasicTransitionsRewardsParsing) { // Test every entry of the matrix. storm::storage::SparseMatrix<double>::const_iterator cIter = rewardMatrix.begin(0); - ASSERT_EQ(1, cIter->first); - ASSERT_EQ(10, cIter->second); + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(10, cIter->getValue()); cIter++; - ASSERT_EQ(2, cIter->first); - ASSERT_EQ(5, cIter->second); + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(5, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(5.5, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(5.5, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(21.4, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(21.4, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->first); - ASSERT_EQ(4, cIter->second); + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(4, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(2, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(2, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(0.1, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(0.1, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->first); - ASSERT_EQ(1.1, cIter->second); + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(1.1, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(9.5, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(9.5, cIter->getValue()); cIter++; - ASSERT_EQ(6, cIter->first); - ASSERT_EQ(6.7, cIter->second); + ASSERT_EQ(6, cIter->getColumn()); + ASSERT_EQ(6.7, cIter->getValue()); cIter++; - ASSERT_EQ(0, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(0, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(0, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(0, cIter->getValue()); cIter++; - ASSERT_EQ(6, cIter->first); - ASSERT_EQ(12, cIter->second); + ASSERT_EQ(6, cIter->getColumn()); + ASSERT_EQ(12, cIter->getValue()); cIter++; - ASSERT_EQ(6, cIter->first); - ASSERT_EQ(35.224653, cIter->second); + ASSERT_EQ(6, cIter->getColumn()); + ASSERT_EQ(35.224653, cIter->getValue()); cIter++; - ASSERT_EQ(7, cIter->first); - ASSERT_EQ(9.875347, cIter->second); + ASSERT_EQ(7, cIter->getColumn()); + ASSERT_EQ(9.875347, cIter->getValue()); } @@ -201,17 +201,17 @@ TEST(DeterministicSparseTransitionParserTest, FixDeadlocks) { ASSERT_EQ(23, transitionMatrix.getEntryCount()); storm::storage::SparseMatrix<double>::const_iterator cIter = transitionMatrix.begin(7); - ASSERT_EQ(7, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(7, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(6, cIter->first); - ASSERT_EQ(0.224653, cIter->second); + ASSERT_EQ(6, cIter->getColumn()); + ASSERT_EQ(0.224653, cIter->getValue()); cIter++; - ASSERT_EQ(7, cIter->first); - ASSERT_EQ(0.775347, cIter->second); + ASSERT_EQ(7, cIter->getColumn()); + ASSERT_EQ(0.775347, cIter->getValue()); cIter++; - ASSERT_EQ(8, cIter->first); - ASSERT_EQ(0, cIter->second); + ASSERT_EQ(8, cIter->getColumn()); + ASSERT_EQ(0, cIter->getValue()); } TEST(DeterministicSparseTransitionParserTest, DontFixDeadlocks) { diff --git a/test/functional/parser/MarkovAutomatonSparseTransitionParserTest.cpp b/test/functional/parser/MarkovAutomatonSparseTransitionParserTest.cpp index 81c2ccfb8..38b152cd4 100644 --- a/test/functional/parser/MarkovAutomatonSparseTransitionParserTest.cpp +++ b/test/functional/parser/MarkovAutomatonSparseTransitionParserTest.cpp @@ -79,29 +79,29 @@ TEST(MarkovAutomatonSparseTransitionParserTest, BasicParsing) { // Finally, test the transition matrix itself. storm::storage::SparseMatrix<double>::const_iterator cIter = transitionMatrix.begin(0); - ASSERT_EQ(2, cIter->second); + ASSERT_EQ(2, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(2, cIter->second); + ASSERT_EQ(2, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->second); + ASSERT_EQ(4, cIter->getValue()); cIter++; - ASSERT_EQ(8, cIter->second); + ASSERT_EQ(8, cIter->getValue()); cIter++; - ASSERT_EQ(0.5, cIter->second); + ASSERT_EQ(0.5, cIter->getValue()); cIter++; - ASSERT_EQ(0.5, cIter->second); + ASSERT_EQ(0.5, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(0.5, cIter->second); + ASSERT_EQ(0.5, cIter->getValue()); cIter++; - ASSERT_EQ(0.5, cIter->second); + ASSERT_EQ(0.5, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(1, cIter->getValue()); cIter++; ASSERT_EQ(transitionMatrix.end(), cIter); } @@ -157,29 +157,29 @@ TEST(MarkovAutomatonSparseTransitionParserTest, Whitespaces) { // Finally, test the transition matrix itself. storm::storage::SparseMatrix<double>::const_iterator cIter = transitionMatrix.begin(0); - ASSERT_EQ(2, cIter->second); + ASSERT_EQ(2, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(2, cIter->second); + ASSERT_EQ(2, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->second); + ASSERT_EQ(4, cIter->getValue()); cIter++; - ASSERT_EQ(8, cIter->second); + ASSERT_EQ(8, cIter->getValue()); cIter++; - ASSERT_EQ(0.5, cIter->second); + ASSERT_EQ(0.5, cIter->getValue()); cIter++; - ASSERT_EQ(0.5, cIter->second); + ASSERT_EQ(0.5, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(0.5, cIter->second); + ASSERT_EQ(0.5, cIter->getValue()); cIter++; - ASSERT_EQ(0.5, cIter->second); + ASSERT_EQ(0.5, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(1, cIter->getValue()); cIter++; ASSERT_EQ(transitionMatrix.end(), cIter); } diff --git a/test/functional/parser/NondeterministicSparseTransitionParserTest.cpp b/test/functional/parser/NondeterministicSparseTransitionParserTest.cpp index 21240a9dd..9494c983f 100644 --- a/test/functional/parser/NondeterministicSparseTransitionParserTest.cpp +++ b/test/functional/parser/NondeterministicSparseTransitionParserTest.cpp @@ -48,71 +48,71 @@ TEST(NondeterministicSparseTransitionParserTest, BasicTransitionsParsing) { // Test every entry of the matrix. storm::storage::SparseMatrix<double>::const_iterator cIter = result.begin(0); - ASSERT_EQ(0, cIter->first); - ASSERT_EQ(0.9, cIter->second); + ASSERT_EQ(0, cIter->getColumn()); + ASSERT_EQ(0.9, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->first); - ASSERT_EQ(0.1, cIter->second); + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(0.1, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->first); - ASSERT_EQ(0.2, cIter->second); + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); cIter++; - ASSERT_EQ(2, cIter->first); - ASSERT_EQ(0.2, cIter->second); + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(0.2, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->first); - ASSERT_EQ(0.2, cIter->second); + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(0.2, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(0, cIter->first); - ASSERT_EQ(0.1, cIter->second); + ASSERT_EQ(0, cIter->getColumn()); + ASSERT_EQ(0.1, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->first); - ASSERT_EQ(0.9, cIter->second); + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0.9, cIter->getValue()); cIter++; - ASSERT_EQ(2, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(2, cIter->first); - ASSERT_EQ(0.5, cIter->second); + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(0.5, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(0.5, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(0.5, cIter->getValue()); cIter++; - ASSERT_EQ(2, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(2, cIter->first); - ASSERT_EQ(0.001, cIter->second); + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(0.001, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(0.999, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(0.999, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->first); - ASSERT_EQ(0.7, cIter->second); + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(0.7, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->first); - ASSERT_EQ(0.3, cIter->second); + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0.3, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->first); - ASSERT_EQ(0.2, cIter->second); + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->first); - ASSERT_EQ(0.2, cIter->second); + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(0.6, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(0.6, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); } TEST(NondeterministicSparseTransitionParserTest, BasicTransitionsRewardsParsing) { @@ -128,56 +128,56 @@ TEST(NondeterministicSparseTransitionParserTest, BasicTransitionsRewardsParsing) // Test every entry of the matrix. storm::storage::SparseMatrix<double>::const_iterator cIter = result.begin(0); - ASSERT_EQ(0, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(0, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->first); - ASSERT_EQ(30, cIter->second); + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(30, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->first); - ASSERT_EQ(15.2, cIter->second); + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(15.2, cIter->getValue()); cIter++; - ASSERT_EQ(2, cIter->first); - ASSERT_EQ(75, cIter->second); + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(75, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(2.45, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(2.45, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(0, cIter->first); - ASSERT_EQ(0.114, cIter->second); + ASSERT_EQ(0, cIter->getColumn()); + ASSERT_EQ(0.114, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->first); - ASSERT_EQ(90, cIter->second); + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(90, cIter->getValue()); cIter++; - ASSERT_EQ(2, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(2, cIter->first); - ASSERT_EQ(55, cIter->second); + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(55, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(87, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(87, cIter->getValue()); cIter++; - ASSERT_EQ(2, cIter->first); - ASSERT_EQ(13, cIter->second); + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(13, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(999, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(999, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->first); - ASSERT_EQ(0.7, cIter->second); + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(0.7, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->first); - ASSERT_EQ(0.3, cIter->second); + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0.3, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->first); - ASSERT_EQ(0.1, cIter->second); + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(0.1, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(6, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(6, cIter->getValue()); } TEST(NondeterministicSparseTransitionParserTest, Whitespaces) { @@ -224,26 +224,26 @@ TEST(NondeterministicSparseTransitionParserTest, FixDeadlocks) { storm::storage::SparseMatrix<double>::const_iterator cIter = result.begin(8); - ASSERT_EQ(1, cIter->first); - ASSERT_EQ(0.7, cIter->second); + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(0.7, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->first); - ASSERT_EQ(0.3, cIter->second); + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0.3, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->first); - ASSERT_EQ(0.2, cIter->second); + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->first); - ASSERT_EQ(0.2, cIter->second); + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(0.6, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(0.6, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); } diff --git a/test/functional/solver/GlpkLpSolverTest.cpp b/test/functional/solver/GlpkLpSolverTest.cpp index 30c3e10f9..a6410611d 100644 --- a/test/functional/solver/GlpkLpSolverTest.cpp +++ b/test/functional/solver/GlpkLpSolverTest.cpp @@ -3,34 +3,34 @@ #include "src/solver/GlpkLpSolver.h" #include "src/exceptions/InvalidStateException.h" +#include "src/exceptions/InvalidAccessException.h" #include "src/settings/Settings.h" TEST(GlpkLpSolver, LPOptimizeMax) { #ifdef STORM_HAVE_GLPK - storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - + storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.optimize()); ASSERT_TRUE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); double xValue = 0; - ASSERT_NO_THROW(xValue = solver.getContinuousValue(xIndex)); + ASSERT_NO_THROW(xValue = solver.getContinuousValue("x")); ASSERT_LT(std::abs(xValue - 1), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double yValue = 0; - ASSERT_NO_THROW(yValue = solver.getContinuousValue(yIndex)); + ASSERT_NO_THROW(yValue = solver.getContinuousValue("y")); ASSERT_LT(std::abs(yValue - 6.5), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double zValue = 0; - ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex)); + ASSERT_NO_THROW(zValue = solver.getContinuousValue("z")); ASSERT_LT(std::abs(zValue - 2.75), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double objectiveValue = 0; ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue()); @@ -42,30 +42,29 @@ TEST(GlpkLpSolver, LPOptimizeMax) { TEST(GlpkLpSolver, LPOptimizeMin) { #ifdef STORM_HAVE_GLPK - storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MINIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::BOUNDED, 1, 5.7, -1)); - - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - + storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Minimize); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("z", 1, 5.7, -1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.optimize()); ASSERT_TRUE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); double xValue = 0; - ASSERT_NO_THROW(xValue = solver.getContinuousValue(xIndex)); + ASSERT_NO_THROW(xValue = solver.getContinuousValue("x")); ASSERT_LT(std::abs(xValue - 1), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double yValue = 0; - ASSERT_NO_THROW(yValue = solver.getContinuousValue(yIndex)); + ASSERT_NO_THROW(yValue = solver.getContinuousValue("y")); ASSERT_LT(std::abs(yValue - 0), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double zValue = 0; - ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex)); + ASSERT_NO_THROW(zValue = solver.getContinuousValue("z")); ASSERT_LT(std::abs(zValue - 5.7), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double objectiveValue = 0; ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue()); @@ -77,30 +76,29 @@ TEST(GlpkLpSolver, LPOptimizeMin) { TEST(GlpkLpSolver, MILPOptimizeMax) { #ifdef STORM_HAVE_GLPK - storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - + storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBinaryVariable("x", -1)); + ASSERT_NO_THROW(solver.addLowerBoundedIntegerVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.optimize()); ASSERT_TRUE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); bool xValue = false; - ASSERT_NO_THROW(xValue = solver.getBinaryValue(xIndex)); + ASSERT_NO_THROW(xValue = solver.getBinaryValue("x")); ASSERT_EQ(true, xValue); int_fast64_t yValue = 0; - ASSERT_NO_THROW(yValue = solver.getIntegerValue(yIndex)); + ASSERT_NO_THROW(yValue = solver.getIntegerValue("y")); ASSERT_EQ(6, yValue); double zValue = 0; - ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex)); + ASSERT_NO_THROW(zValue = solver.getContinuousValue("z")); ASSERT_LT(std::abs(zValue - 3), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double objectiveValue = 0; ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue()); @@ -112,30 +110,29 @@ TEST(GlpkLpSolver, MILPOptimizeMax) { TEST(GlpkLpSolver, MILPOptimizeMin) { #ifdef STORM_HAVE_GLPK - storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MINIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createIntegerVariable("z", storm::solver::LpSolver::VariableType::BOUNDED, 0, 5, -1)); - - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - + storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Minimize); + ASSERT_NO_THROW(solver.addBinaryVariable("x", -1)); + ASSERT_NO_THROW(solver.addLowerBoundedIntegerVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("z", 0, 5, -1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.optimize()); ASSERT_TRUE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); bool xValue = false; - ASSERT_NO_THROW(xValue = solver.getBinaryValue(xIndex)); + ASSERT_NO_THROW(xValue = solver.getBinaryValue("x")); ASSERT_EQ(true, xValue); int_fast64_t yValue = 0; - ASSERT_NO_THROW(yValue = solver.getIntegerValue(yIndex)); + ASSERT_NO_THROW(yValue = solver.getIntegerValue("y")); ASSERT_EQ(0, yValue); double zValue = 0; - ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex)); + ASSERT_NO_THROW(zValue = solver.getContinuousValue("z")); ASSERT_LT(std::abs(zValue - 5), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double objectiveValue = 0; ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue()); @@ -147,31 +144,30 @@ TEST(GlpkLpSolver, MILPOptimizeMin) { TEST(GlpkLpSolver, LPInfeasible) { #ifdef STORM_HAVE_GLPK - storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex}, {1}, storm::solver::LpSolver::BoundType::GREATER_EQUAL, 7)); - + storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") > storm::expressions::Expression::createDoubleLiteral(7))); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.optimize()); ASSERT_FALSE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_TRUE(solver.isInfeasible()); double xValue = 0; - ASSERT_THROW(xValue = solver.getContinuousValue(xIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(xValue = solver.getContinuousValue("x"), storm::exceptions::InvalidAccessException); double yValue = 0; - ASSERT_THROW(yValue = solver.getContinuousValue(yIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(yValue = solver.getContinuousValue("y"), storm::exceptions::InvalidAccessException); double zValue = 0; - ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException); double objectiveValue = 0; - ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException); + ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException); #else ASSERT_TRUE(false) << "StoRM built without glpk support."; #endif @@ -179,31 +175,30 @@ TEST(GlpkLpSolver, LPInfeasible) { TEST(GlpkLpSolver, MILPInfeasible) { #ifdef STORM_HAVE_GLPK - storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex}, {1}, storm::solver::LpSolver::BoundType::GREATER_EQUAL, 7)); - + storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBinaryVariable("x", -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") > storm::expressions::Expression::createDoubleLiteral(7))); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.optimize()); ASSERT_FALSE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_TRUE(solver.isInfeasible()); bool xValue = false; - ASSERT_THROW(xValue = solver.getBinaryValue(xIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(xValue = solver.getBinaryValue("x"), storm::exceptions::InvalidAccessException); int_fast64_t yValue = 0; - ASSERT_THROW(yValue = solver.getIntegerValue(yIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(yValue = solver.getIntegerValue("y"), storm::exceptions::InvalidAccessException); double zValue = 0; - ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException); double objectiveValue = 0; - ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException); + ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException); #else ASSERT_TRUE(false) << "StoRM built without glpk support."; #endif @@ -211,29 +206,28 @@ TEST(GlpkLpSolver, MILPInfeasible) { TEST(GlpkLpSolver, LPUnbounded) { #ifdef STORM_HAVE_GLPK - storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - + storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.optimize()); ASSERT_FALSE(solver.isOptimal()); ASSERT_TRUE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); double xValue = 0; - ASSERT_THROW(xValue = solver.getContinuousValue(xIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(xValue = solver.getContinuousValue("x"), storm::exceptions::InvalidAccessException); double yValue = 0; - ASSERT_THROW(yValue = solver.getContinuousValue(yIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(yValue = solver.getContinuousValue("y"), storm::exceptions::InvalidAccessException); double zValue = 0; - ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException); double objectiveValue = 0; - ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException); + ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException); #else ASSERT_TRUE(false) << "StoRM built without glpk support."; #endif @@ -241,30 +235,29 @@ TEST(GlpkLpSolver, LPUnbounded) { TEST(GlpkLpSolver, MILPUnbounded) { #ifdef STORM_HAVE_GLPK - storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - + storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBinaryVariable("x", -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.optimize()); ASSERT_FALSE(solver.isOptimal()); ASSERT_TRUE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); bool xValue = false; - ASSERT_THROW(xValue = solver.getBinaryValue(xIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(xValue = solver.getBinaryValue("x"), storm::exceptions::InvalidAccessException); int_fast64_t yValue = 0; - ASSERT_THROW(yValue = solver.getIntegerValue(yIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(yValue = solver.getIntegerValue("y"), storm::exceptions::InvalidAccessException); double zValue = 0; - ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException); double objectiveValue = 0; - ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException); + ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException); #else ASSERT_TRUE(false) << "StoRM built without glpk support."; #endif -} \ No newline at end of file +} diff --git a/test/functional/solver/GurobiLpSolverTest.cpp b/test/functional/solver/GurobiLpSolverTest.cpp index 1ca524d8e..ee0ebfa03 100644 --- a/test/functional/solver/GurobiLpSolverTest.cpp +++ b/test/functional/solver/GurobiLpSolverTest.cpp @@ -3,36 +3,34 @@ #include "src/solver/GurobiLpSolver.h" #include "src/exceptions/InvalidStateException.h" +#include "src/exceptions/InvalidAccessException.h" #include "src/settings/Settings.h" TEST(GurobiLpSolver, LPOptimizeMax) { #ifdef STORM_HAVE_GUROBI - storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.update()); - - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); + storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.optimize()); ASSERT_TRUE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); double xValue = 0; - ASSERT_NO_THROW(xValue = solver.getContinuousValue(xIndex)); + ASSERT_NO_THROW(xValue = solver.getContinuousValue("x")); ASSERT_LT(std::abs(xValue - 1), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double yValue = 0; - ASSERT_NO_THROW(yValue = solver.getContinuousValue(yIndex)); + ASSERT_NO_THROW(yValue = solver.getContinuousValue("y")); ASSERT_LT(std::abs(yValue - 6.5), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double zValue = 0; - ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex)); + ASSERT_NO_THROW(zValue = solver.getContinuousValue("z")); ASSERT_LT(std::abs(zValue - 2.75), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double objectiveValue = 0; ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue()); @@ -44,32 +42,29 @@ TEST(GurobiLpSolver, LPOptimizeMax) { TEST(GurobiLpSolver, LPOptimizeMin) { #ifdef STORM_HAVE_GUROBI - storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MINIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::BOUNDED, 1, 5.7, -1)); - - ASSERT_NO_THROW(solver.update()); + storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Minimize); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1)); + ASSERT_NO_THROW(solver.addLowerBoundedIntegerVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("z", 1, 5.7, -1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - ASSERT_NO_THROW(solver.optimize()); ASSERT_TRUE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); double xValue = 0; - ASSERT_NO_THROW(xValue = solver.getContinuousValue(xIndex)); + ASSERT_NO_THROW(xValue = solver.getContinuousValue("x")); ASSERT_LT(std::abs(xValue - 1), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double yValue = 0; - ASSERT_NO_THROW(yValue = solver.getContinuousValue(yIndex)); + ASSERT_NO_THROW(yValue = solver.getContinuousValue("y")); ASSERT_LT(std::abs(yValue - 0), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double zValue = 0; - ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex)); + ASSERT_NO_THROW(zValue = solver.getContinuousValue("z")); ASSERT_LT(std::abs(zValue - 5.7), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double objectiveValue = 0; ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue()); @@ -81,32 +76,29 @@ TEST(GurobiLpSolver, LPOptimizeMin) { TEST(GurobiLpSolver, MILPOptimizeMax) { #ifdef STORM_HAVE_GUROBI - storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.update()); + storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBinaryVariable("x", -1)); + ASSERT_NO_THROW(solver.addLowerBoundedIntegerVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - ASSERT_NO_THROW(solver.optimize()); ASSERT_TRUE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); bool xValue = false; - ASSERT_NO_THROW(xValue = solver.getBinaryValue(xIndex)); + ASSERT_NO_THROW(xValue = solver.getBinaryValue("x")); ASSERT_EQ(true, xValue); int_fast64_t yValue = 0; - ASSERT_NO_THROW(yValue = solver.getIntegerValue(yIndex)); + ASSERT_NO_THROW(yValue = solver.getIntegerValue("y")); ASSERT_EQ(6, yValue); double zValue = 0; - ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex)); + ASSERT_NO_THROW(zValue = solver.getContinuousValue("z")); ASSERT_LT(std::abs(zValue - 3), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double objectiveValue = 0; ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue()); @@ -118,32 +110,29 @@ TEST(GurobiLpSolver, MILPOptimizeMax) { TEST(GurobiLpSolver, MILPOptimizeMin) { #ifdef STORM_HAVE_GUROBI - storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MINIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createIntegerVariable("z", storm::solver::LpSolver::VariableType::BOUNDED, 0, 5.7, -1)); - - ASSERT_NO_THROW(solver.update()); + storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Minimize); + ASSERT_NO_THROW(solver.addBinaryVariable("x", -1)); + ASSERT_NO_THROW(solver.addLowerBoundedIntegerVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("z", 0, 5, -1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - ASSERT_NO_THROW(solver.optimize()); ASSERT_TRUE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); bool xValue = false; - ASSERT_NO_THROW(xValue = solver.getBinaryValue(xIndex)); + ASSERT_NO_THROW(xValue = solver.getBinaryValue("x")); ASSERT_EQ(true, xValue); int_fast64_t yValue = 0; - ASSERT_NO_THROW(yValue = solver.getIntegerValue(yIndex)); + ASSERT_NO_THROW(yValue = solver.getIntegerValue("y")); ASSERT_EQ(0, yValue); double zValue = 0; - ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex)); + ASSERT_NO_THROW(zValue = solver.getContinuousValue("z")); ASSERT_LT(std::abs(zValue - 5), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double objectiveValue = 0; ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue()); @@ -155,33 +144,30 @@ TEST(GurobiLpSolver, MILPOptimizeMin) { TEST(GurobiLpSolver, LPInfeasible) { #ifdef STORM_HAVE_GUROBI - storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.update()); + storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") > storm::expressions::Expression::createDoubleLiteral(7))); + ASSERT_NO_THROW(solver.update()); - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex}, {1}, storm::solver::LpSolver::BoundType::GREATER_EQUAL, 7)); - ASSERT_NO_THROW(solver.optimize()); ASSERT_FALSE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_TRUE(solver.isInfeasible()); double xValue = 0; - ASSERT_THROW(xValue = solver.getContinuousValue(xIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(xValue = solver.getContinuousValue("x"), storm::exceptions::InvalidAccessException); double yValue = 0; - ASSERT_THROW(yValue = solver.getContinuousValue(yIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(yValue = solver.getContinuousValue("y"), storm::exceptions::InvalidAccessException); double zValue = 0; - ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException); double objectiveValue = 0; - ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException); + ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException); #else ASSERT_TRUE(false) << "StoRM built without Gurobi support."; #endif @@ -189,33 +175,30 @@ TEST(GurobiLpSolver, LPInfeasible) { TEST(GurobiLpSolver, MILPInfeasible) { #ifdef STORM_HAVE_GUROBI - storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.update()); + storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBinaryVariable("x", -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") > storm::expressions::Expression::createDoubleLiteral(7))); + ASSERT_NO_THROW(solver.update()); - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex}, {1}, storm::solver::LpSolver::BoundType::GREATER_EQUAL, 7)); - ASSERT_NO_THROW(solver.optimize()); ASSERT_FALSE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_TRUE(solver.isInfeasible()); bool xValue = false; - ASSERT_THROW(xValue = solver.getBinaryValue(xIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(xValue = solver.getBinaryValue("x"), storm::exceptions::InvalidAccessException); int_fast64_t yValue = 0; - ASSERT_THROW(yValue = solver.getIntegerValue(yIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(yValue = solver.getIntegerValue("y"), storm::exceptions::InvalidAccessException); double zValue = 0; - ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException); double objectiveValue = 0; - ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException); + ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException); #else ASSERT_TRUE(false) << "StoRM built without Gurobi support."; #endif @@ -223,31 +206,28 @@ TEST(GurobiLpSolver, MILPInfeasible) { TEST(GurobiLpSolver, LPUnbounded) { #ifdef STORM_HAVE_GUROBI - storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.update()); + storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - ASSERT_NO_THROW(solver.optimize()); ASSERT_FALSE(solver.isOptimal()); ASSERT_TRUE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); double xValue = 0; - ASSERT_THROW(xValue = solver.getContinuousValue(xIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(xValue = solver.getContinuousValue("x"), storm::exceptions::InvalidAccessException); double yValue = 0; - ASSERT_THROW(yValue = solver.getContinuousValue(yIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(yValue = solver.getContinuousValue("y"), storm::exceptions::InvalidAccessException); double zValue = 0; - ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException); double objectiveValue = 0; - ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException); + ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException); #else ASSERT_TRUE(false) << "StoRM built without Gurobi support."; #endif @@ -255,32 +235,28 @@ TEST(GurobiLpSolver, LPUnbounded) { TEST(GurobiLpSolver, MILPUnbounded) { #ifdef STORM_HAVE_GUROBI - storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.update()); + storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBinaryVariable("x", -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); ASSERT_NO_THROW(solver.optimize()); ASSERT_FALSE(solver.isOptimal()); ASSERT_TRUE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); bool xValue = false; - ASSERT_THROW(xValue = solver.getBinaryValue(xIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(xValue = solver.getBinaryValue("x"), storm::exceptions::InvalidAccessException); int_fast64_t yValue = 0; - ASSERT_THROW(yValue = solver.getIntegerValue(yIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(yValue = solver.getIntegerValue("y"), storm::exceptions::InvalidAccessException); double zValue = 0; - ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException); double objectiveValue = 0; - ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException); + ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException); #else ASSERT_TRUE(false) << "StoRM built without Gurobi support."; #endif -} \ No newline at end of file +} diff --git a/test/functional/storage/CuddDdTest.cpp b/test/functional/storage/CuddDdTest.cpp index 91d14f202..ce40c94d1 100644 --- a/test/functional/storage/CuddDdTest.cpp +++ b/test/functional/storage/CuddDdTest.cpp @@ -307,4 +307,26 @@ TEST(CuddDd, ForwardIteratorTest) { ++numberOfValuations; } EXPECT_EQ(9, numberOfValuations); + + dd = manager->getRange("x"); + dd = dd.ite(manager->getOne(), manager->getOne()); + ASSERT_NO_THROW(it = dd.begin()); + ASSERT_NO_THROW(ite = dd.end()); + numberOfValuations = 0; + while (it != ite) { + ASSERT_NO_THROW(valuationValuePair = *it); + ASSERT_NO_THROW(++it); + ++numberOfValuations; + } + EXPECT_EQ(16, numberOfValuations); + + ASSERT_NO_THROW(it = dd.begin(false)); + ASSERT_NO_THROW(ite = dd.end()); + numberOfValuations = 0; + while (it != ite) { + ASSERT_NO_THROW(valuationValuePair = *it); + ASSERT_NO_THROW(++it); + ++numberOfValuations; + } + EXPECT_EQ(1, numberOfValuations); } diff --git a/test/functional/storage/ExpressionTest.cpp b/test/functional/storage/ExpressionTest.cpp index 26822190d..d3a920d1d 100644 --- a/test/functional/storage/ExpressionTest.cpp +++ b/test/functional/storage/ExpressionTest.cpp @@ -3,6 +3,7 @@ #include "gtest/gtest.h" #include "src/storage/expressions/Expression.h" +#include "src/storage/expressions/LinearityCheckVisitor.h" #include "src/storage/expressions/SimpleValuation.h" #include "src/exceptions/InvalidTypeException.h" @@ -332,4 +333,20 @@ TEST(Expression, SimpleEvaluationTest) { ASSERT_THROW(tempExpression.evaluateAsDouble(&valuation), storm::exceptions::InvalidTypeException); ASSERT_THROW(tempExpression.evaluateAsInt(&valuation), storm::exceptions::InvalidTypeException); EXPECT_FALSE(tempExpression.evaluateAsBool(&valuation)); +} + +TEST(Expression, VisitorTest) { + storm::expressions::Expression threeExpression; + storm::expressions::Expression piExpression; + storm::expressions::Expression intVarExpression; + storm::expressions::Expression doubleVarExpression; + + ASSERT_NO_THROW(threeExpression = storm::expressions::Expression::createIntegerLiteral(3)); + ASSERT_NO_THROW(piExpression = storm::expressions::Expression::createDoubleLiteral(3.14)); + ASSERT_NO_THROW(intVarExpression = storm::expressions::Expression::createIntegerVariable("y")); + ASSERT_NO_THROW(doubleVarExpression = storm::expressions::Expression::createDoubleVariable("z")); + + storm::expressions::Expression tempExpression = intVarExpression + doubleVarExpression * threeExpression; + storm::expressions::LinearityCheckVisitor visitor; + EXPECT_TRUE(visitor.check(tempExpression)); } \ No newline at end of file diff --git a/test/functional/storage/SparseMatrixTest.cpp b/test/functional/storage/SparseMatrixTest.cpp index 69517a6d4..de89f4950 100644 --- a/test/functional/storage/SparseMatrixTest.cpp +++ b/test/functional/storage/SparseMatrixTest.cpp @@ -147,7 +147,7 @@ TEST(SparseMatrix, Build) { } TEST(SparseMatrix, CreationWithMovingContents) { - std::vector<std::pair<uint_fast64_t, double>> columnsAndValues; + std::vector<storm::storage::MatrixEntry<double>> columnsAndValues; columnsAndValues.emplace_back(1, 1.0); columnsAndValues.emplace_back(2, 1.2); columnsAndValues.emplace_back(0, 0.5); @@ -540,24 +540,24 @@ TEST(SparseMatrix, Iteration) { ASSERT_NO_THROW(matrix = matrixBuilder.build()); for (auto const& entry : matrix.getRow(4)) { - if (entry.first == 0) { - ASSERT_EQ(0.1, entry.second); - } else if (entry.first == 1) { - ASSERT_EQ(0.2, entry.second); - } else if (entry.first == 3) { - ASSERT_EQ(0.3, entry.second); + if (entry.getColumn() == 0) { + ASSERT_EQ(0.1, entry.getValue()); + } else if (entry.getColumn() == 1) { + ASSERT_EQ(0.2, entry.getValue()); + } else if (entry.getColumn() == 3) { + ASSERT_EQ(0.3, entry.getValue()); } else { ASSERT_TRUE(false); } } for (storm::storage::SparseMatrix<double>::iterator it = matrix.begin(4), ite = matrix.end(4); it != ite; ++it) { - if (it->first == 0) { - ASSERT_EQ(0.1, it->second); - } else if (it->first == 1) { - ASSERT_EQ(0.2, it->second); - } else if (it->first == 3) { - ASSERT_EQ(0.3, it->second); + if (it->getColumn() == 0) { + ASSERT_EQ(0.1, it->getValue()); + } else if (it->getColumn() == 1) { + ASSERT_EQ(0.2, it->getValue()); + } else if (it->getColumn() == 3) { + ASSERT_EQ(0.3, it->getValue()); } else { ASSERT_TRUE(false); } diff --git a/test/performance/modelchecker/GmmxxDtmcPrctModelCheckerTest.cpp b/test/performance/modelchecker/GmmxxDtmcPrctModelCheckerTest.cpp index 809f602fb..2fddf8702 100644 --- a/test/performance/modelchecker/GmmxxDtmcPrctModelCheckerTest.cpp +++ b/test/performance/modelchecker/GmmxxDtmcPrctModelCheckerTest.cpp @@ -17,7 +17,7 @@ TEST(GmmxxDtmcPrctlModelCheckerTest, Crowds) { std::shared_ptr<storm::models::Dtmc<double>> dtmc = abstractModel->as<storm::models::Dtmc<double>>(); ASSERT_EQ(dtmc->getNumberOfStates(), 2036647ull); - ASSERT_EQ(dtmc->getNumberOfTransitions(), 8973900ull); + ASSERT_EQ(dtmc->getNumberOfTransitions(), 7362293ull); storm::modelchecker::prctl::SparseDtmcPrctlModelChecker<double> mc(*dtmc, new storm::solver::GmmxxLinearEquationSolver<double>()); @@ -71,7 +71,7 @@ TEST(GmmxxDtmcPrctlModelCheckerTest, SynchronousLeader) { std::shared_ptr<storm::models::Dtmc<double>> dtmc = abstractModel->as<storm::models::Dtmc<double>>(); ASSERT_EQ(dtmc->getNumberOfStates(), 1312334ull); - ASSERT_EQ(dtmc->getNumberOfTransitions(), 2886810ull); + ASSERT_EQ(dtmc->getNumberOfTransitions(), 1574477ull); storm::modelchecker::prctl::SparseDtmcPrctlModelChecker<double> mc(*dtmc, new storm::solver::GmmxxLinearEquationSolver<double>()); diff --git a/test/performance/storage/SparseMatrixTest.cpp b/test/performance/storage/SparseMatrixTest.cpp index 367bd3401..b59d283af 100644 --- a/test/performance/storage/SparseMatrixTest.cpp +++ b/test/performance/storage/SparseMatrixTest.cpp @@ -15,7 +15,7 @@ TEST(SparseMatrix, Iteration) { for (uint_fast64_t row = 0; row < matrix.getRowCount(); ++row) { for (auto const& entry : matrix.getRow(row)) { // The following can never be true, but prevents the compiler from optimizing away the loop. - if (entry.first > matrix.getColumnCount()) { + if (entry.getColumn() > matrix.getColumnCount()) { ASSERT_TRUE(false); } }