You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
421 lines
19 KiB
421 lines
19 KiB
/*
|
|
* GraphAnalyzer.h
|
|
*
|
|
* Created on: 28.11.2012
|
|
* Author: Christian Dehnert
|
|
*/
|
|
|
|
#ifndef STORM_UTILITY_GRAPHANALYZER_H_
|
|
#define STORM_UTILITY_GRAPHANALYZER_H_
|
|
|
|
#include "src/models/AbstractDeterministicModel.h"
|
|
#include "src/models/AbstractNondeterministicModel.h"
|
|
#include "src/exceptions/InvalidArgumentException.h"
|
|
|
|
#include "log4cplus/logger.h"
|
|
#include "log4cplus/loggingmacros.h"
|
|
|
|
extern log4cplus::Logger logger;
|
|
|
|
namespace storm {
|
|
|
|
namespace utility {
|
|
|
|
class GraphAnalyzer {
|
|
public:
|
|
/*!
|
|
* Computes the sets of states that have probability 0 or 1, respectively, of satisfying phi until psi in a
|
|
* deterministic model.
|
|
* @param model The model whose graph structure to search.
|
|
* @param phiStates The set of all states satisfying phi.
|
|
* @param psiStates The set of all states satisfying psi.
|
|
* @param statesWithProbability0 A pointer to a bit vector that is initially empty and will contain all states with
|
|
* probability 0 after the invocation of the function.
|
|
* @param statesWithProbability1 A pointer to a bit vector that is initially empty and will contain all states with
|
|
* probability 1 after the invocation of the function.
|
|
*/
|
|
template <class T>
|
|
static void performProb01(storm::models::AbstractDeterministicModel<T>& model, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, storm::storage::BitVector* statesWithProbability0, storm::storage::BitVector* statesWithProbability1) {
|
|
// Check for valid parameters.
|
|
if (statesWithProbability0 == nullptr) {
|
|
LOG4CPLUS_ERROR(logger, "Parameter 'statesWithProbability0' must not be null.");
|
|
throw storm::exceptions::InvalidArgumentException("Parameter 'statesWithProbability0' must not be null.");
|
|
}
|
|
if (statesWithProbability1 == nullptr) {
|
|
LOG4CPLUS_ERROR(logger, "Parameter 'statesWithProbability1' must not be null.");
|
|
throw storm::exceptions::InvalidArgumentException("Parameter 'statesWithProbability1' must not be null.");
|
|
}
|
|
|
|
// Perform the actual search.
|
|
GraphAnalyzer::performProbGreater0(model, phiStates, psiStates, statesWithProbability0);
|
|
GraphAnalyzer::performProb1(model, phiStates, psiStates, *statesWithProbability0, statesWithProbability1);
|
|
statesWithProbability0->complement();
|
|
}
|
|
|
|
/*!
|
|
* Performs a backwards depth-first search trough the underlying graph structure
|
|
* of the given model to determine which states of the model have a positive probability
|
|
* of satisfying phi until psi. The resulting states are written to the given bit vector.
|
|
* @param model The model whose graph structure to search.
|
|
* @param phiStates A bit vector of all states satisfying phi.
|
|
* @param psiStates A bit vector of all states satisfying psi.
|
|
* @param statesWithProbabilityGreater0 A pointer to the result of the search for states that possess
|
|
* a positive probability of satisfying phi until psi.
|
|
*/
|
|
template <class T>
|
|
static void performProbGreater0(storm::models::AbstractDeterministicModel<T>& model, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, storm::storage::BitVector* statesWithProbabilityGreater0) {
|
|
// Check for valid parameter.
|
|
if (statesWithProbabilityGreater0 == nullptr) {
|
|
LOG4CPLUS_ERROR(logger, "Parameter 'statesWithProbabilityGreater0' must not be null.");
|
|
throw storm::exceptions::InvalidArgumentException("Parameter 'statesWithProbabilityGreater0' must not be null.");
|
|
}
|
|
|
|
// Get the backwards transition relation from the model to ease the search.
|
|
storm::models::GraphTransitions<T> backwardTransitions(model.getTransitionMatrix(), false);
|
|
|
|
// Add all psi states as the already satisfy the condition.
|
|
*statesWithProbabilityGreater0 |= psiStates;
|
|
|
|
// Initialize the stack used for the DFS with the states
|
|
std::vector<uint_fast64_t> stack;
|
|
stack.reserve(model.getNumberOfStates());
|
|
psiStates.addSetIndicesToList(stack);
|
|
|
|
// Perform the actual DFS.
|
|
while(!stack.empty()) {
|
|
uint_fast64_t currentState = stack.back();
|
|
stack.pop_back();
|
|
|
|
for(auto it = backwardTransitions.beginStateSuccessorsIterator(currentState); it != backwardTransitions.endStateSuccessorsIterator(currentState); ++it) {
|
|
if (phiStates.get(*it) && !statesWithProbabilityGreater0->get(*it)) {
|
|
statesWithProbabilityGreater0->set(*it, true);
|
|
stack.push_back(*it);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Computes the set of states of the given model for which all paths lead to
|
|
* the given set of target states and only visit states from the filter set
|
|
* before. In order to do this, it uses the given set of states that
|
|
* characterizes the states that possess at least one path to a target state.
|
|
* The results are written to the given bit vector.
|
|
* @param model The model whose graph structure to search.
|
|
* @param phiStates A bit vector of all states satisfying phi.
|
|
* @param psiStates A bit vector of all states satisfying psi.
|
|
* @param statesWithProbabilityGreater0 A reference to a bit vector of states that possess a positive
|
|
* probability mass of satisfying phi until psi.
|
|
* @param alwaysPhiUntilPsiStates A pointer to the result of the search for states that only
|
|
* have paths satisfying phi until psi.
|
|
*/
|
|
template <class T>
|
|
static void performProb1(storm::models::AbstractDeterministicModel<T>& model, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, storm::storage::BitVector const& statesWithProbabilityGreater0, storm::storage::BitVector* statesWithProbability1) {
|
|
// Check for valid parameter.
|
|
if (statesWithProbability1 == nullptr) {
|
|
LOG4CPLUS_ERROR(logger, "Parameter 'statesWithProbability1' must not be null.");
|
|
throw storm::exceptions::InvalidArgumentException("Parameter 'statesWithProbability1' must not be null.");
|
|
}
|
|
|
|
GraphAnalyzer::performProbGreater0(model, ~psiStates, ~statesWithProbabilityGreater0, statesWithProbability1);
|
|
statesWithProbability1->complement();
|
|
}
|
|
|
|
/*!
|
|
* Computes the set of states of the given model for which all paths lead to
|
|
* the given set of target states and only visit states from the filter set
|
|
* before. In order to do this, it uses the given set of states that
|
|
* characterizes the states that possess at least one path to a target state.
|
|
* The results are written to the given bit vector.
|
|
* @param model The model whose graph structure to search.
|
|
* @param phiStates A bit vector of all states satisfying phi.
|
|
* @param psiStates A bit vector of all states satisfying psi.
|
|
* @param alwaysPhiUntilPsiStates A pointer to the result of the search for states that only
|
|
* have paths satisfying phi until psi.
|
|
*/
|
|
template <class T>
|
|
static void performProb1(storm::models::AbstractDeterministicModel<T>& model, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, storm::storage::BitVector* statesWithProbability1) {
|
|
// Check for valid parameter.
|
|
if (statesWithProbability1 == nullptr) {
|
|
LOG4CPLUS_ERROR(logger, "Parameter 'statesWithProbability1' must not be null.");
|
|
throw storm::exceptions::InvalidArgumentException("Parameter 'statesWithProbability1' must not be null.");
|
|
}
|
|
|
|
storm::storage::BitVector* statesWithProbabilityGreater0 = new storm::storage::BitVector(model.getNumberOfStates());
|
|
GraphAnalyzer::performProbGreater0(model, phiStates, psiStates, statesWithProbabilityGreater0);
|
|
GraphAnalyzer::performProbGreater0(model, ~psiStates, ~(*statesWithProbabilityGreater0), statesWithProbability1);
|
|
delete statesWithProbabilityGreater0;
|
|
statesWithProbability1->complement();
|
|
}
|
|
|
|
template <class T>
|
|
static void performProb01Max(storm::models::AbstractNondeterministicModel<T>& model, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, storm::storage::BitVector* statesWithProbability0, storm::storage::BitVector* statesWithProbability1) {
|
|
// Check for valid parameters.
|
|
if (statesWithProbability0 == nullptr) {
|
|
LOG4CPLUS_ERROR(logger, "Parameter 'statesWithProbability0' must not be null.");
|
|
throw storm::exceptions::InvalidArgumentException("Parameter 'statesWithProbability0' must not be null.");
|
|
}
|
|
if (statesWithProbability1 == nullptr) {
|
|
LOG4CPLUS_ERROR(logger, "Parameter 'statesWithProbability1' must not be null.");
|
|
throw storm::exceptions::InvalidArgumentException("Parameter 'statesWithProbability1' must not be null.");
|
|
}
|
|
|
|
// Perform the actual search.
|
|
GraphAnalyzer::performProb0A(model, phiStates, psiStates, statesWithProbability0);
|
|
GraphAnalyzer::performProb1E(model, phiStates, psiStates, statesWithProbability1);
|
|
}
|
|
|
|
template <class T>
|
|
static void performProb0A(storm::models::AbstractNondeterministicModel<T>& model, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, storm::storage::BitVector* statesWithProbability0) {
|
|
// Check for valid parameter.
|
|
if (statesWithProbability0 == nullptr) {
|
|
LOG4CPLUS_ERROR(logger, "Parameter 'statesWithProbability0' must not be null.");
|
|
throw storm::exceptions::InvalidArgumentException("Parameter 'statesWithProbability0' must not be null.");
|
|
}
|
|
|
|
// Get the backwards transition relation from the model to ease the search.
|
|
storm::models::GraphTransitions<T> backwardTransitions(model.getTransitionMatrix(), model.getNondeterministicChoiceIndices(), false);
|
|
|
|
// Add all psi states as the already satisfy the condition.
|
|
*statesWithProbability0 |= psiStates;
|
|
|
|
// Initialize the stack used for the DFS with the states
|
|
std::vector<uint_fast64_t> stack;
|
|
stack.reserve(model.getNumberOfStates());
|
|
psiStates.addSetIndicesToList(stack);
|
|
|
|
// Perform the actual DFS.
|
|
while(!stack.empty()) {
|
|
uint_fast64_t currentState = stack.back();
|
|
stack.pop_back();
|
|
|
|
for(auto it = backwardTransitions.beginStateSuccessorsIterator(currentState); it != backwardTransitions.endStateSuccessorsIterator(currentState); ++it) {
|
|
if (phiStates.get(*it) && !statesWithProbability0->get(*it)) {
|
|
statesWithProbability0->set(*it, true);
|
|
stack.push_back(*it);
|
|
}
|
|
}
|
|
}
|
|
|
|
statesWithProbability0->complement();
|
|
}
|
|
|
|
template <class T>
|
|
static void performProb1E(storm::models::AbstractNondeterministicModel<T>& model, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, storm::storage::BitVector* statesWithProbability1) {
|
|
// Check for valid parameters.
|
|
if (statesWithProbability1 == nullptr) {
|
|
LOG4CPLUS_ERROR(logger, "Parameter 'statesWithProbability1' must not be null.");
|
|
throw storm::exceptions::InvalidArgumentException("Parameter 'statesWithProbability1' must not be null.");
|
|
}
|
|
|
|
// Get some temporaries for convenience.
|
|
std::shared_ptr<storm::storage::SparseMatrix<T>> transitionMatrix = model.getTransitionMatrix();
|
|
std::shared_ptr<std::vector<uint_fast64_t>> nondeterministicChoiceIndices = model.getNondeterministicChoiceIndices();
|
|
|
|
// Get the backwards transition relation from the model to ease the search.
|
|
storm::models::GraphTransitions<T> backwardTransitions(model.getTransitionMatrix(), model.getNondeterministicChoiceIndices(), false);
|
|
|
|
storm::storage::BitVector* currentStates = new storm::storage::BitVector(model.getNumberOfStates(), true);
|
|
|
|
std::vector<uint_fast64_t> stack;
|
|
stack.reserve(model.getNumberOfStates());
|
|
|
|
bool done = false;
|
|
while (!done) {
|
|
stack.clear();
|
|
storm::storage::BitVector* nextStates = new storm::storage::BitVector(psiStates);
|
|
psiStates.addSetIndicesToList(stack);
|
|
|
|
while (!stack.empty()) {
|
|
uint_fast64_t currentState = stack.back();
|
|
stack.pop_back();
|
|
|
|
for(auto it = backwardTransitions.beginStateSuccessorsIterator(currentState); it != backwardTransitions.endStateSuccessorsIterator(currentState); ++it) {
|
|
if (phiStates.get(*it) && !nextStates->get(*it)) {
|
|
// Check whether the predecessor has only successors in the current state set for one of the
|
|
// nondeterminstic choices.
|
|
for (uint_fast64_t row = (*nondeterministicChoiceIndices)[*it]; row < (*nondeterministicChoiceIndices)[*it + 1]; ++row) {
|
|
bool allSuccessorsInCurrentStates = true;
|
|
for (auto colIt = transitionMatrix->beginConstColumnIterator(row); colIt != transitionMatrix->endConstColumnIterator(row); ++colIt) {
|
|
if (!currentStates->get(*colIt)) {
|
|
allSuccessorsInCurrentStates = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If all successors for a given nondeterministic choice are in the current state set, we
|
|
// add it to the set of states for the next iteration and perform a backward search from
|
|
// that state.
|
|
if (allSuccessorsInCurrentStates) {
|
|
nextStates->set(*it, true);
|
|
stack.push_back(*it);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check whether we need to perform an additional iteration.
|
|
if (*currentStates == *nextStates) {
|
|
done = true;
|
|
} else {
|
|
*currentStates = *nextStates;
|
|
}
|
|
}
|
|
|
|
*statesWithProbability1 = *currentStates;
|
|
delete currentStates;
|
|
}
|
|
|
|
template <class T>
|
|
static void performProb01Min(storm::models::AbstractNondeterministicModel<T>& model, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, storm::storage::BitVector* statesWithProbability0, storm::storage::BitVector* statesWithProbability1) {
|
|
// Check for valid parameters.
|
|
if (statesWithProbability0 == nullptr) {
|
|
LOG4CPLUS_ERROR(logger, "Parameter 'statesWithProbability0' must not be null.");
|
|
throw storm::exceptions::InvalidArgumentException("Parameter 'statesWithProbability0' must not be null.");
|
|
}
|
|
if (statesWithProbability1 == nullptr) {
|
|
LOG4CPLUS_ERROR(logger, "Parameter 'statesWithProbability1' must not be null.");
|
|
throw storm::exceptions::InvalidArgumentException("Parameter 'statesWithProbability1' must not be null.");
|
|
}
|
|
|
|
// Perform the actual search.
|
|
GraphAnalyzer::performProb0E(model, phiStates, psiStates, statesWithProbability0);
|
|
GraphAnalyzer::performProb1A(model, phiStates, psiStates, statesWithProbability1);
|
|
}
|
|
|
|
template <class T>
|
|
static void performProb0E(storm::models::AbstractNondeterministicModel<T>& model, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, storm::storage::BitVector* statesWithProbability0) {
|
|
// Check for valid parameter.
|
|
if (statesWithProbability0 == nullptr) {
|
|
LOG4CPLUS_ERROR(logger, "Parameter 'statesWithProbability0' must not be null.");
|
|
throw storm::exceptions::InvalidArgumentException("Parameter 'statesWithProbability0' must not be null.");
|
|
}
|
|
|
|
// Get some temporaries for convenience.
|
|
std::shared_ptr<storm::storage::SparseMatrix<T>> transitionMatrix = model.getTransitionMatrix();
|
|
std::shared_ptr<std::vector<uint_fast64_t>> nondeterministicChoiceIndices = model.getNondeterministicChoiceIndices();
|
|
|
|
// Get the backwards transition relation from the model to ease the search.
|
|
storm::models::GraphTransitions<T> backwardTransitions(transitionMatrix, nondeterministicChoiceIndices, false);
|
|
|
|
// Add all psi states as the already satisfy the condition.
|
|
*statesWithProbability0 |= psiStates;
|
|
|
|
// Initialize the stack used for the DFS with the states
|
|
std::vector<uint_fast64_t> stack;
|
|
stack.reserve(model.getNumberOfStates());
|
|
psiStates.addSetIndicesToList(stack);
|
|
|
|
// Perform the actual DFS.
|
|
while(!stack.empty()) {
|
|
uint_fast64_t currentState = stack.back();
|
|
stack.pop_back();
|
|
|
|
for(auto it = backwardTransitions.beginStateSuccessorsIterator(currentState); it != backwardTransitions.endStateSuccessorsIterator(currentState); ++it) {
|
|
if (phiStates.get(*it) && !statesWithProbability0->get(*it)) {
|
|
// Check whether the predecessor has at least one successor in the current state
|
|
// set for every nondeterministic choice.
|
|
bool addToStatesWithProbability0 = true;
|
|
for (auto rowIt = nondeterministicChoiceIndices->begin() + *it; rowIt != nondeterministicChoiceIndices->begin() + *it + 1; ++rowIt) {
|
|
bool hasAtLeastOneSuccessorWithProbabilityGreater0 = false;
|
|
for (auto colIt = transitionMatrix->beginConstColumnIterator(*rowIt); colIt != transitionMatrix->endConstColumnIterator(*rowIt); ++colIt) {
|
|
if (statesWithProbability0->get(*colIt)) {
|
|
hasAtLeastOneSuccessorWithProbabilityGreater0 = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!hasAtLeastOneSuccessorWithProbabilityGreater0) {
|
|
addToStatesWithProbability0 = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we need to add the state, then actually add it and perform further search
|
|
// from the state.
|
|
if (addToStatesWithProbability0) {
|
|
statesWithProbability0->set(*it, true);
|
|
stack.push_back(*it);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
statesWithProbability0->complement();
|
|
}
|
|
|
|
template <class T>
|
|
static void performProb1A(storm::models::AbstractNondeterministicModel<T>& model, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, storm::storage::BitVector* statesWithProbability1) {
|
|
// Check for valid parameters.
|
|
if (statesWithProbability1 == nullptr) {
|
|
LOG4CPLUS_ERROR(logger, "Parameter 'statesWithProbability1' must not be null.");
|
|
throw storm::exceptions::InvalidArgumentException("Parameter 'statesWithProbability1' must not be null.");
|
|
}
|
|
|
|
// Get some temporaries for convenience.
|
|
std::shared_ptr<storm::storage::SparseMatrix<T>> transitionMatrix = model.getTransitionMatrix();
|
|
std::shared_ptr<std::vector<uint_fast64_t>> nondeterministicChoiceIndices = model.getNondeterministicChoiceIndices();
|
|
|
|
// Get the backwards transition relation from the model to ease the search.
|
|
storm::models::GraphTransitions<T> backwardTransitions(model.getTransitionMatrix(), model.getNondeterministicChoiceIndices(), false);
|
|
|
|
storm::storage::BitVector* currentStates = new storm::storage::BitVector(model.getNumberOfStates(), true);
|
|
|
|
std::vector<uint_fast64_t> stack;
|
|
stack.reserve(model.getNumberOfStates());
|
|
|
|
bool done = false;
|
|
while (!done) {
|
|
stack.clear();
|
|
storm::storage::BitVector* nextStates = new storm::storage::BitVector(psiStates);
|
|
psiStates.addSetIndicesToList(stack);
|
|
|
|
while (!stack.empty()) {
|
|
uint_fast64_t currentState = stack.back();
|
|
stack.pop_back();
|
|
|
|
for(auto it = backwardTransitions.beginStateSuccessorsIterator(currentState); it != backwardTransitions.endStateSuccessorsIterator(currentState); ++it) {
|
|
if (phiStates.get(*it) && !nextStates->get(*it)) {
|
|
// Check whether the predecessor has only successors in the current state set for all of the
|
|
// nondeterminstic choices.
|
|
bool allSuccessorsInCurrentStatesForAllChoices = true;
|
|
for (uint_fast64_t row = (*nondeterministicChoiceIndices)[*it]; row < (*nondeterministicChoiceIndices)[*it + 1]; ++row) {
|
|
for (auto colIt = transitionMatrix->beginConstColumnIterator(row); colIt != transitionMatrix->endConstColumnIterator(row); ++colIt) {
|
|
if (!currentStates->get(*colIt)) {
|
|
allSuccessorsInCurrentStatesForAllChoices = false;
|
|
goto afterCheckLoop;
|
|
}
|
|
}
|
|
}
|
|
|
|
afterCheckLoop:
|
|
// If all successors for all nondeterministic choices are in the current state set, we
|
|
// add it to the set of states for the next iteration and perform a backward search from
|
|
// that state.
|
|
if (allSuccessorsInCurrentStatesForAllChoices) {
|
|
nextStates->set(*it, true);
|
|
stack.push_back(*it);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check whether we need to perform an additional iteration.
|
|
if (*currentStates == *nextStates) {
|
|
done = true;
|
|
} else {
|
|
*currentStates = *nextStates;
|
|
}
|
|
}
|
|
|
|
*statesWithProbability1 = *currentStates;
|
|
delete currentStates;
|
|
}
|
|
};
|
|
|
|
} // namespace utility
|
|
|
|
} // namespace storm
|
|
|
|
#endif /* STORM_UTILITY_GRAPHANALYZER_H_ */
|