Browse Source

EC elimination for Pmax for hybrid MDP model checker

main
dehnert 7 years ago
parent
commit
694e6ba240
  1. 131
      src/storm/modelchecker/prctl/helper/HybridMdpPrctlHelper.cpp
  2. 3
      src/storm/modelchecker/prctl/helper/SparseMdpEndComponentInformation.cpp
  3. 4
      src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp
  4. 12
      src/storm/storage/SparseMatrix.cpp

131
src/storm/modelchecker/prctl/helper/HybridMdpPrctlHelper.cpp

@ -65,6 +65,49 @@ namespace storm {
return result; return result;
} }
template <typename ValueType>
void eliminateExtendedStatesFromExplicitRepresentation(std::pair<storm::storage::SparseMatrix<ValueType>, std::vector<ValueType>>& explicitRepresentation, std::vector<uint64_t>& scheduler, storm::storage::BitVector const& properMaybeStates) {
// Eliminate superfluous entries from the scheduler.
uint64_t position = 0;
for (auto state : properMaybeStates) {
scheduler[position] = scheduler[state];
position++;
}
scheduler.resize(properMaybeStates.getNumberOfSetBits());
scheduler.shrink_to_fit();
// Treat the matrix.
explicitRepresentation.first = explicitRepresentation.first.getSubmatrix(true, properMaybeStates, properMaybeStates);
}
template<typename ValueType>
void eliminateEndComponentsAndExtendedStatesUntilProbabilities(std::pair<storm::storage::SparseMatrix<ValueType>, std::vector<ValueType>>& explicitRepresentation, SolverRequirementsData<ValueType>& solverRequirementsData, storm::storage::BitVector const& targetStates) {
// Get easier handles to the data.
auto& transitionMatrix = explicitRepresentation.first;
auto& oneStepProbabilities = explicitRepresentation.second;
bool doDecomposition = !solverRequirementsData.properMaybeStates.empty();
storm::storage::MaximalEndComponentDecomposition<ValueType> endComponentDecomposition;
if (doDecomposition) {
// Then compute the states that are in MECs with zero reward.
endComponentDecomposition = storm::storage::MaximalEndComponentDecomposition<ValueType>(transitionMatrix, transitionMatrix.transpose(), solverRequirementsData.properMaybeStates);
}
// Only do more work if there are actually end-components.
if (doDecomposition && !endComponentDecomposition.empty()) {
STORM_LOG_DEBUG("Eliminating " << endComponentDecomposition.size() << " EC(s).");
std::vector<ValueType> subvector;
SparseMdpEndComponentInformation<ValueType>::eliminateEndComponents(endComponentDecomposition, transitionMatrix, solverRequirementsData.properMaybeStates, &targetStates, nullptr, nullptr, explicitRepresentation.first, &subvector, nullptr);
oneStepProbabilities = std::move(subvector);
} else {
STORM_LOG_DEBUG("Not eliminating ECs as there are none.");
eliminateExtendedStatesFromExplicitRepresentation(explicitRepresentation, solverRequirementsData.initialScheduler.get(), solverRequirementsData.properMaybeStates);
oneStepProbabilities = explicitRepresentation.first.getConstrainedRowGroupSumVector(solverRequirementsData.properMaybeStates, targetStates);
}
}
template<storm::dd::DdType DdType, typename ValueType> template<storm::dd::DdType DdType, typename ValueType>
std::unique_ptr<CheckResult> HybridMdpPrctlHelper<DdType, ValueType>::computeUntilProbabilities(OptimizationDirection dir, storm::models::symbolic::NondeterministicModel<DdType, ValueType> const& model, storm::dd::Add<DdType, ValueType> const& transitionMatrix, storm::dd::Bdd<DdType> const& phiStates, storm::dd::Bdd<DdType> const& psiStates, bool qualitative, storm::solver::MinMaxLinearEquationSolverFactory<ValueType> const& linearEquationSolverFactory) { std::unique_ptr<CheckResult> HybridMdpPrctlHelper<DdType, ValueType>::computeUntilProbabilities(OptimizationDirection dir, storm::models::symbolic::NondeterministicModel<DdType, ValueType> const& model, storm::dd::Add<DdType, ValueType> const& transitionMatrix, storm::dd::Bdd<DdType> const& phiStates, storm::dd::Bdd<DdType> const& psiStates, bool qualitative, storm::solver::MinMaxLinearEquationSolverFactory<ValueType> const& linearEquationSolverFactory) {
// We need to identify the states which have to be taken out of the matrix, i.e. all states that have // We need to identify the states which have to be taken out of the matrix, i.e. all states that have
@ -91,7 +134,8 @@ namespace storm {
storm::solver::MinMaxLinearEquationSolverRequirements requirements = linearEquationSolverFactory.getRequirements(storm::solver::EquationSystemType::UntilProbabilities, dir); storm::solver::MinMaxLinearEquationSolverRequirements requirements = linearEquationSolverFactory.getRequirements(storm::solver::EquationSystemType::UntilProbabilities, dir);
storm::solver::MinMaxLinearEquationSolverRequirements clearedRequirements = requirements; storm::solver::MinMaxLinearEquationSolverRequirements clearedRequirements = requirements;
SolverRequirementsData<ValueType> solverRequirementsData; SolverRequirementsData<ValueType> solverRequirementsData;
bool extendMaybeStates = true;
bool extendMaybeStates = false;
if (!clearedRequirements.empty()) { if (!clearedRequirements.empty()) {
if (clearedRequirements.requiresNoEndComponents()) { if (clearedRequirements.requiresNoEndComponents()) {
STORM_LOG_DEBUG("Scheduling EC elimination, because the solver requires it."); STORM_LOG_DEBUG("Scheduling EC elimination, because the solver requires it.");
@ -116,13 +160,31 @@ namespace storm {
// Create the ODD for the translation between symbolic and explicit storage. // Create the ODD for the translation between symbolic and explicit storage.
storm::dd::Odd odd = extendedMaybeStates.createOdd(); storm::dd::Odd odd = extendedMaybeStates.createOdd();
// Create the matrix and the vector for the equation system.
storm::dd::Add<DdType, ValueType> extendedMaybeStatesAdd = extendedMaybeStates.template toAdd<ValueType>();
// Convert the maybe states BDD to an ADD.
storm::dd::Add<DdType, ValueType> maybeStatesAdd = maybeStates.template toAdd<ValueType>();
// Start by cutting away all rows that do not belong to maybe states. Note that this leaves columns targeting // Start by cutting away all rows that do not belong to maybe states. Note that this leaves columns targeting
// non-maybe states in the matrix. // non-maybe states in the matrix.
storm::dd::Add<DdType, ValueType> submatrix = transitionMatrix * extendedMaybeStatesAdd;
storm::dd::Add<DdType, ValueType> submatrix = transitionMatrix * maybeStatesAdd;
// If the maybe states were extended, we generate the explicit representation slightly differently.
std::pair<storm::storage::SparseMatrix<ValueType>, std::vector<ValueType>> explicitRepresentation;
if (extendMaybeStates) {
// Eliminate all transitions to non-extended-maybe states.
submatrix *= extendedMaybeStates.template toAdd<ValueType>().swapVariables(model.getRowColumnMetaVariablePairs());
// Only translate the matrix for now.
explicitRepresentation.first = submatrix.toMatrix(model.getNondeterminismVariables(), odd, odd);
// Get all original maybe states in the extended matrix.
solverRequirementsData.properMaybeStates = maybeStates.toVector(odd);
// Compute the target states within the set of extended maybe states.
storm::storage::BitVector targetStates = (extendedMaybeStates && statesWithProbability01.second).toVector(odd);
// Eliminate the end components and remove the states that are not interesting (target or non-filter).
eliminateEndComponentsAndExtendedStatesUntilProbabilities(explicitRepresentation, solverRequirementsData, targetStates);
} else {
// Then compute the vector that contains the one-step probabilities to a state with probability 1 for all // Then compute the vector that contains the one-step probabilities to a state with probability 1 for all
// maybe states. // maybe states.
storm::dd::Add<DdType, ValueType> prob1StatesAsColumn = statesWithProbability01.second.template toAdd<ValueType>(); storm::dd::Add<DdType, ValueType> prob1StatesAsColumn = statesWithProbability01.second.template toAdd<ValueType>();
@ -137,11 +199,12 @@ namespace storm {
submatrix *= maybeStatesAdd.swapVariables(model.getRowColumnMetaVariablePairs()); submatrix *= maybeStatesAdd.swapVariables(model.getRowColumnMetaVariablePairs());
// Translate the symbolic matrix/vector to their explicit representations and solve the equation system. // Translate the symbolic matrix/vector to their explicit representations and solve the equation system.
std::pair<storm::storage::SparseMatrix<ValueType>, std::vector<ValueType>> explicitRepresentation = submatrix.toMatrixVector(subvector, std::move(rowGroupSizes), model.getNondeterminismVariables(), odd, odd);
explicitRepresentation = submatrix.toMatrixVector(subvector, std::move(rowGroupSizes), model.getNondeterminismVariables(), odd, odd);
if (requirements.requiresValidInitialScheduler()) { if (requirements.requiresValidInitialScheduler()) {
solverRequirementsData.initialScheduler = computeValidInitialSchedulerForUntilProbabilities<ValueType>(explicitRepresentation.first, explicitRepresentation.second); solverRequirementsData.initialScheduler = computeValidInitialSchedulerForUntilProbabilities<ValueType>(explicitRepresentation.first, explicitRepresentation.second);
} }
}
// Create the solution vector. // Create the solution vector.
std::vector<ValueType> x(explicitRepresentation.first.getRowGroupCount(), storm::utility::zero<ValueType>()); std::vector<ValueType> x(explicitRepresentation.first.getRowGroupCount(), storm::utility::zero<ValueType>());
@ -154,6 +217,18 @@ namespace storm {
solver->setRequirementsChecked(); solver->setRequirementsChecked();
solver->solveEquations(dir, x, explicitRepresentation.second); solver->solveEquations(dir, x, explicitRepresentation.second);
// If we included some target and non-filter states in the ODD, we need to expand the result from the solver.
if (requirements.requiresNoEndComponents() && solverRequirementsData.ecInformation) {
std::vector<ValueType> extendedVector(solverRequirementsData.properMaybeStates.getNumberOfSetBits());
solverRequirementsData.ecInformation.get().setValues(extendedVector, solverRequirementsData.properMaybeStates, x);
x = std::move(extendedVector);
}
// If we extended the maybe states, we create a new ODD containing only the propery maybe states.
if (extendMaybeStates) {
odd = maybeStates.createOdd();
}
// Return a hybrid check result that stores the numerical values explicitly. // Return a hybrid check result that stores the numerical values explicitly.
return std::unique_ptr<CheckResult>(new storm::modelchecker::HybridQuantitativeCheckResult<DdType, ValueType>(model.getReachableStates(), model.getReachableStates() && !maybeStates, statesWithProbability01.second.template toAdd<ValueType>(), maybeStates, odd, x)); return std::unique_ptr<CheckResult>(new storm::modelchecker::HybridQuantitativeCheckResult<DdType, ValueType>(model.getReachableStates(), model.getReachableStates() && !maybeStates, statesWithProbability01.second.template toAdd<ValueType>(), maybeStates, odd, x));
} else { } else {
@ -310,34 +385,6 @@ namespace storm {
return result; return result;
} }
template <typename ValueType>
void eliminateTargetStatesFromExplicitRepresentation(std::pair<storm::storage::SparseMatrix<ValueType>, std::vector<ValueType>>& explicitRepresentation, std::vector<uint64_t>& scheduler, storm::storage::BitVector const& properMaybeStates) {
// Eliminate superfluous entries from the scheduler.
uint64_t position = 0;
for (auto state : properMaybeStates) {
scheduler[position] = scheduler[state];
position++;
}
scheduler.resize(properMaybeStates.getNumberOfSetBits());
scheduler.shrink_to_fit();
// Treat the matrix.
explicitRepresentation.first = explicitRepresentation.first.getSubmatrix(true, properMaybeStates, properMaybeStates);
}
template <typename ValueType>
std::vector<ValueType> insertTargetStateValuesIntoExplicitRepresentation(std::vector<ValueType> const& x, storm::storage::BitVector const& properMaybeStates) {
std::vector<ValueType> expandedResult(properMaybeStates.size(), storm::utility::zero<ValueType>());
uint64_t position = 0;
for (auto state : properMaybeStates) {
expandedResult[state] = x[position];
position++;
}
return expandedResult;
}
template<typename ValueType> template<typename ValueType>
void eliminateEndComponentsAndTargetStatesReachabilityRewards(std::pair<storm::storage::SparseMatrix<ValueType>, std::vector<ValueType>>& explicitRepresentation, SolverRequirementsData<ValueType>& solverRequirementsData) { void eliminateEndComponentsAndTargetStatesReachabilityRewards(std::pair<storm::storage::SparseMatrix<ValueType>, std::vector<ValueType>>& explicitRepresentation, SolverRequirementsData<ValueType>& solverRequirementsData) {
@ -389,6 +436,7 @@ namespace storm {
rewardVector = std::move(subvector); rewardVector = std::move(subvector);
} else { } else {
STORM_LOG_DEBUG("Not eliminating ECs as there are none."); STORM_LOG_DEBUG("Not eliminating ECs as there are none.");
eliminateExtendedStatesFromExplicitRepresentation(explicitRepresentation, solverRequirementsData.initialScheduler.get(), solverRequirementsData.properMaybeStates);
} }
} }
@ -487,7 +535,7 @@ namespace storm {
// Since we needed the transitions to target states to be translated as well for the computation // Since we needed the transitions to target states to be translated as well for the computation
// of the scheduler, we have to get rid of them now. // of the scheduler, we have to get rid of them now.
eliminateTargetStatesFromExplicitRepresentation(explicitRepresentation, solverRequirementsData.initialScheduler.get(), solverRequirementsData.properMaybeStates);
eliminateExtendedStatesFromExplicitRepresentation(explicitRepresentation, solverRequirementsData.initialScheduler.get(), solverRequirementsData.properMaybeStates);
} }
} }
@ -506,17 +554,16 @@ namespace storm {
solver->setRequirementsChecked(); solver->setRequirementsChecked();
solver->solveEquations(dir, x, explicitRepresentation.second); solver->solveEquations(dir, x, explicitRepresentation.second);
// If we included the target states in the ODD, we need to expand the result from the solver.
if (extendMaybeStates) {
if (requirements.requiresNoEndComponents()) {
if (solverRequirementsData.ecInformation) {
std::vector<ValueType> extendedVector(solverRequirementsData.properMaybeStates.size());
// If we eliminated end components, we need to extend the solution vector.
if (requirements.requiresNoEndComponents() && solverRequirementsData.ecInformation) {
std::vector<ValueType> extendedVector(solverRequirementsData.properMaybeStates.getNumberOfSetBits());
solverRequirementsData.ecInformation.get().setValues(extendedVector, solverRequirementsData.properMaybeStates, x); solverRequirementsData.ecInformation.get().setValues(extendedVector, solverRequirementsData.properMaybeStates, x);
x = std::move(extendedVector); x = std::move(extendedVector);
} }
} else {
x = insertTargetStateValuesIntoExplicitRepresentation(x, solverRequirementsData.properMaybeStates);
}
// If we extended the maybe states, we create a new ODD that only contains proper maybe states.
if (extendMaybeStates) {
odd = maybeStates.createOdd();
} }
// Return a hybrid check result that stores the numerical values explicitly. // Return a hybrid check result that stores the numerical values explicitly.

3
src/storm/modelchecker/prctl/helper/SparseMdpEndComponentInformation.cpp

@ -161,6 +161,7 @@ namespace storm {
if (stateActions.second.find(row) != stateActions.second.end()) { if (stateActions.second.find(row) != stateActions.second.end()) {
continue; continue;
} }
// If the choices is not in the selected ones, drop it. // If the choices is not in the selected ones, drop it.
if (selectedChoices && !selectedChoices->get(row)) { if (selectedChoices && !selectedChoices->get(row)) {
continue; continue;
@ -198,7 +199,7 @@ namespace storm {
} }
} }
submatrix = builder.build();
submatrix = builder.build(currentRow);
return result; return result;
} }

4
src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp

@ -489,6 +489,7 @@ namespace storm {
this->createLowerBoundsVector(*lowerX); this->createLowerBoundsVector(*lowerX);
this->createUpperBoundsVector(this->auxiliaryRowGroupVector, this->A->getRowGroupCount()); this->createUpperBoundsVector(this->auxiliaryRowGroupVector, this->A->getRowGroupCount());
std::vector<ValueType>* upperX = this->auxiliaryRowGroupVector.get(); std::vector<ValueType>* upperX = this->auxiliaryRowGroupVector.get();
std::vector<ValueType>* tmp = nullptr; std::vector<ValueType>* tmp = nullptr;
if (!useGaussSeidelMultiplication) { if (!useGaussSeidelMultiplication) {
auxiliaryRowGroupVector2 = std::make_unique<std::vector<ValueType>>(lowerX->size()); auxiliaryRowGroupVector2 = std::make_unique<std::vector<ValueType>>(lowerX->size());
@ -618,7 +619,8 @@ namespace storm {
reportStatus(status, iterations); reportStatus(status, iterations);
// We take the means of the lower and upper bound so we guarantee the desired precision. // We take the means of the lower and upper bound so we guarantee the desired precision.
storm::utility::vector::applyPointwise(*lowerX, *upperX, *lowerX, [] (ValueType const& a, ValueType const& b) { return (a + b) / storm::utility::convertNumber<ValueType>(2.0); });
ValueType two = storm::utility::convertNumber<ValueType>(2.0);
storm::utility::vector::applyPointwise<ValueType, ValueType, ValueType>(*lowerX, *upperX, *lowerX, [&two] (ValueType const& a, ValueType const& b) -> ValueType { return (a + b) / two; });
// Since we shuffled the pointer around, we need to write the actual results to the input/output vector x. // Since we shuffled the pointer around, we need to write the actual results to the input/output vector x.
if (&x == tmp) { if (&x == tmp) {

12
src/storm/storage/SparseMatrix.cpp

@ -231,16 +231,13 @@ namespace storm {
rowIndications.push_back(currentEntryCount); rowIndications.push_back(currentEntryCount);
} }
// If there are no rows, we need to erase the start index of the current (non-existing) row.
if (rowCount == 0) {
rowIndications.pop_back();
}
// We put a sentinel element at the last position of the row indices array. This eases iteration work, // We put a sentinel element at the last position of the row indices array. This eases iteration work,
// as now the indices of row i are always between rowIndications[i] and rowIndications[i + 1], also for // as now the indices of row i are always between rowIndications[i] and rowIndications[i + 1], also for
// the first and last row. // the first and last row.
if (rowCount > 0) {
rowIndications.push_back(currentEntryCount); rowIndications.push_back(currentEntryCount);
STORM_LOG_ASSERT(rowCount == rowIndications.size() - 1, "Wrong sizes of vectors.");
}
STORM_LOG_ASSERT(rowCount == rowIndications.size() - 1, "Wrong sizes of vectors: " << rowCount << " != " << (rowIndications.size() - 1) << ".");
uint_fast64_t columnCount = hasEntries ? highestColumn + 1 : 0; uint_fast64_t columnCount = hasEntries ? highestColumn + 1 : 0;
if (initialColumnCountSet && forceInitialDimensions) { if (initialColumnCountSet && forceInitialDimensions) {
STORM_LOG_THROW(columnCount <= initialColumnCount, storm::exceptions::InvalidStateException, "Expected not more than " << initialColumnCount << " columns, but got " << columnCount << "."); STORM_LOG_THROW(columnCount <= initialColumnCount, storm::exceptions::InvalidStateException, "Expected not more than " << initialColumnCount << " columns, but got " << columnCount << ".");
@ -1478,7 +1475,8 @@ namespace storm {
template<typename ValueType> template<typename ValueType>
ValueType SparseMatrix<ValueType>::multiplyRowWithVector(index_type row, std::vector<ValueType> const& vector) const { ValueType SparseMatrix<ValueType>::multiplyRowWithVector(index_type row, std::vector<ValueType> const& vector) const {
ValueType result = storm::utility::zero<ValueType>(); ValueType result = storm::utility::zero<ValueType>();
for(auto const& entry : this->getRow(row)){
for (auto const& entry : this->getRow(row)){
result += entry.getValue() * vector[entry.getColumn()]; result += entry.getValue() * vector[entry.getColumn()];
} }
return result; return result;

Loading…
Cancel
Save