Browse Source
Refactored SCC-Decomposition design as a preparation step for computing maximal end components of Markov automata.
Refactored SCC-Decomposition design as a preparation step for computing maximal end components of Markov automata.
Former-commit-id: 4596ba71ec
tempestpy_adaptions
dehnert
11 years ago
8 changed files with 337 additions and 155 deletions
-
20src/models/AbstractModel.h
-
57src/storage/Decomposition.cpp
-
54src/storage/Decomposition.h
-
156src/storage/StronglyConnectedComponentDecomposition.cpp
-
49src/storage/StronglyConnectedComponentDecomposition.h
-
2src/storage/VectorSet.h
-
137src/utility/graph.h
-
17test/performance/graph/GraphTest.cpp
@ -0,0 +1,57 @@ |
|||
#include "src/storage/Decomposition.h"
|
|||
|
|||
namespace storm { |
|||
namespace storage { |
|||
|
|||
Decomposition::Decomposition() : blocks() { |
|||
// Intentionally left empty.
|
|||
} |
|||
|
|||
Decomposition::Decomposition(Decomposition const& other) : blocks(other.blocks) { |
|||
// Intentionally left empty.
|
|||
} |
|||
|
|||
Decomposition& Decomposition::operator=(Decomposition const& other) { |
|||
this->blocks = other.blocks; |
|||
return *this; |
|||
} |
|||
|
|||
Decomposition::Decomposition(Decomposition&& other) : blocks(std::move(other.blocks)) { |
|||
// Intentionally left empty.
|
|||
} |
|||
|
|||
Decomposition& Decomposition::operator=(Decomposition&& other) { |
|||
this->blocks = std::move(other.blocks); |
|||
return *this; |
|||
} |
|||
|
|||
size_t Decomposition::size() const { |
|||
return blocks.size(); |
|||
} |
|||
|
|||
Decomposition::iterator Decomposition::begin() { |
|||
return blocks.begin(); |
|||
} |
|||
|
|||
Decomposition::iterator Decomposition::end() { |
|||
return blocks.end(); |
|||
} |
|||
|
|||
Decomposition::const_iterator Decomposition::begin() const { |
|||
return blocks.begin(); |
|||
} |
|||
|
|||
Decomposition::const_iterator Decomposition::end() const { |
|||
return blocks.end(); |
|||
} |
|||
|
|||
Decomposition::Block const& Decomposition::getBlock(uint_fast64_t index) const { |
|||
return blocks.at(index); |
|||
} |
|||
|
|||
Decomposition::Block const& Decomposition::operator[](uint_fast64_t index) const { |
|||
return blocks[index]; |
|||
} |
|||
|
|||
} // namespace storage
|
|||
} // namespace storm
|
@ -0,0 +1,54 @@ |
|||
#ifndef STORM_STORAGE_DECOMPOSITION_H_ |
|||
#define STORM_STORAGE_DECOMPOSITION_H_ |
|||
|
|||
#include <vector> |
|||
|
|||
#include "src/storage/VectorSet.h" |
|||
|
|||
namespace storm { |
|||
namespace storage { |
|||
|
|||
/*! |
|||
* This class represents the decomposition of a model into state sets. |
|||
*/ |
|||
class Decomposition { |
|||
public: |
|||
typedef storm::storage::VectorSet<uint_fast64_t> Block; |
|||
typedef std::vector<Block>::iterator iterator; |
|||
typedef std::vector<Block>::const_iterator const_iterator; |
|||
|
|||
/* |
|||
* Creates an empty SCC decomposition. |
|||
*/ |
|||
Decomposition(); |
|||
|
|||
Decomposition(Decomposition const& other); |
|||
|
|||
Decomposition& operator=(Decomposition const& other); |
|||
|
|||
Decomposition(Decomposition&& other); |
|||
|
|||
Decomposition& operator=(Decomposition&& other); |
|||
|
|||
size_t size() const; |
|||
|
|||
iterator begin(); |
|||
|
|||
iterator end(); |
|||
|
|||
const_iterator begin() const; |
|||
|
|||
const_iterator end() const; |
|||
|
|||
Block const& getBlock(uint_fast64_t index) const; |
|||
|
|||
Block const& operator[](uint_fast64_t index) const; |
|||
|
|||
protected: |
|||
// The blocks of the decomposition. |
|||
std::vector<Block> blocks; |
|||
}; |
|||
} |
|||
} |
|||
|
|||
#endif /* STORM_STORAGE_DECOMPOSITION_H_ */ |
@ -0,0 +1,156 @@ |
|||
#include "src/storage/StronglyConnectedComponentDecomposition.h"
|
|||
#include "src/models/AbstractModel.h"
|
|||
|
|||
namespace storm { |
|||
namespace storage { |
|||
template<typename T> |
|||
StronglyConnectedComponentDecomposition<T>::StronglyConnectedComponentDecomposition() : Decomposition() { |
|||
// Intentionally left empty.
|
|||
} |
|||
|
|||
template <typename T> |
|||
StronglyConnectedComponentDecomposition<T>::StronglyConnectedComponentDecomposition(storm::models::AbstractModel<T> const& model) : Decomposition() { |
|||
performSccDecomposition(model); |
|||
} |
|||
|
|||
template <typename T> |
|||
StronglyConnectedComponentDecomposition<T>::StronglyConnectedComponentDecomposition(StronglyConnectedComponentDecomposition const& other) : Decomposition(other) { |
|||
// Intentionally left empty.
|
|||
} |
|||
|
|||
template <typename T> |
|||
StronglyConnectedComponentDecomposition<T>& StronglyConnectedComponentDecomposition<T>::operator=(StronglyConnectedComponentDecomposition const& other) { |
|||
this->blocks = other.blocks; |
|||
return *this; |
|||
} |
|||
|
|||
template <typename T> |
|||
StronglyConnectedComponentDecomposition<T>::StronglyConnectedComponentDecomposition(StronglyConnectedComponentDecomposition&& other) : Decomposition(std::move(other)) { |
|||
// Intentionally left empty.
|
|||
} |
|||
|
|||
template <typename T> |
|||
StronglyConnectedComponentDecomposition<T>& StronglyConnectedComponentDecomposition<T>::operator=(StronglyConnectedComponentDecomposition&& other) { |
|||
this->blocks = std::move(other.blocks); |
|||
return *this; |
|||
} |
|||
|
|||
template <typename T> |
|||
void StronglyConnectedComponentDecomposition<T>::performSccDecomposition(storm::models::AbstractModel<T> const& model) { |
|||
LOG4CPLUS_INFO(logger, "Computing SCC decomposition."); |
|||
|
|||
uint_fast64_t numberOfStates = model.getNumberOfStates(); |
|||
|
|||
// Set up the environment of Tarjan's algorithm.
|
|||
std::vector<uint_fast64_t> tarjanStack; |
|||
tarjanStack.reserve(numberOfStates); |
|||
storm::storage::BitVector tarjanStackStates(numberOfStates); |
|||
std::vector<uint_fast64_t> stateIndices(numberOfStates); |
|||
std::vector<uint_fast64_t> lowlinks(numberOfStates); |
|||
storm::storage::BitVector visitedStates(numberOfStates); |
|||
|
|||
// Start the search for SCCs from every vertex in the graph structure, because there is.
|
|||
uint_fast64_t currentIndex = 0; |
|||
for (uint_fast64_t state = 0; state < numberOfStates; ++state) { |
|||
if (!visitedStates.get(state)) { |
|||
performSccDecompositionHelper(model, state, currentIndex, stateIndices, lowlinks, tarjanStack, tarjanStackStates, visitedStates); |
|||
} |
|||
} |
|||
|
|||
LOG4CPLUS_INFO(logger, "Done computing SCC decomposition."); |
|||
} |
|||
|
|||
template <typename T> |
|||
void StronglyConnectedComponentDecomposition<T>::performSccDecompositionHelper(storm::models::AbstractModel<T> const& model, uint_fast64_t startState, uint_fast64_t& currentIndex, std::vector<uint_fast64_t>& stateIndices, std::vector<uint_fast64_t>& lowlinks, std::vector<uint_fast64_t>& tarjanStack, storm::storage::BitVector& tarjanStackStates, storm::storage::BitVector& visitedStates) { |
|||
// Create the stacks needed for turning the recursive formulation of Tarjan's algorithm
|
|||
// into an iterative version. In particular, we keep one stack for states and one stack
|
|||
// for the iterators. The last one is not strictly needed, but reduces iteration work when
|
|||
// all successors of a particular state are considered.
|
|||
std::vector<uint_fast64_t> recursionStateStack; |
|||
recursionStateStack.reserve(lowlinks.size()); |
|||
std::vector<typename storm::storage::SparseMatrix<T>::ConstIterator> recursionIteratorStack; |
|||
recursionIteratorStack.reserve(lowlinks.size()); |
|||
std::vector<bool> statesInStack(lowlinks.size()); |
|||
|
|||
// Initialize the recursion stacks with the given initial state (and its successor iterator).
|
|||
recursionStateStack.push_back(startState); |
|||
recursionIteratorStack.push_back(model.getRows(startState).begin()); |
|||
|
|||
recursionStepForward: |
|||
while (!recursionStateStack.empty()) { |
|||
uint_fast64_t currentState = recursionStateStack.back(); |
|||
typename storm::storage::SparseMatrix<T>::ConstIterator successorIt = recursionIteratorStack.back(); |
|||
|
|||
// Perform the treatment of newly discovered state as defined by Tarjan's algorithm
|
|||
visitedStates.set(currentState, true); |
|||
stateIndices[currentState] = currentIndex; |
|||
lowlinks[currentState] = currentIndex; |
|||
++currentIndex; |
|||
tarjanStack.push_back(currentState); |
|||
tarjanStackStates.set(currentState, true); |
|||
|
|||
// Now, traverse all successors of the current state.
|
|||
for(; successorIt != model.getRows(currentState).end(); ++successorIt) { |
|||
// If we have not visited the successor already, we need to perform the procedure
|
|||
// recursively on the newly found state.
|
|||
if (!visitedStates.get(successorIt.column())) { |
|||
// 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.column()); |
|||
statesInStack[successorIt.column()] = true; |
|||
|
|||
// Also, put initial value for iterator on corresponding recursion stack.
|
|||
recursionIteratorStack.push_back(model.getRows(successorIt.column()).begin()); |
|||
|
|||
// Perform the actual recursion step in an iterative way.
|
|||
goto recursionStepForward; |
|||
|
|||
recursionStepBackward: |
|||
lowlinks[currentState] = std::min(lowlinks[currentState], lowlinks[successorIt.column()]); |
|||
} else if (tarjanStackStates.get(successorIt.column())) { |
|||
// Update the lowlink of the current state.
|
|||
lowlinks[currentState] = std::min(lowlinks[currentState], stateIndices[successorIt.column()]); |
|||
} |
|||
} |
|||
|
|||
// If the current state is the root of a SCC, we need to pop all states of the SCC from
|
|||
// the algorithm's stack.
|
|||
if (lowlinks[currentState] == stateIndices[currentState]) { |
|||
Block scc; |
|||
|
|||
uint_fast64_t lastState = 0; |
|||
do { |
|||
// Pop topmost state from the algorithm's stack.
|
|||
lastState = tarjanStack.back(); |
|||
tarjanStack.pop_back(); |
|||
tarjanStackStates.set(lastState, false); |
|||
|
|||
// Add the state to the current SCC.
|
|||
scc.insert(lastState); |
|||
} while (lastState != currentState); |
|||
this->blocks.emplace_back(std::move(scc)); |
|||
} |
|||
|
|||
// If we reach this point, we have completed the recursive descent for the current state.
|
|||
// That is, we need to pop it from the recursion stacks.
|
|||
recursionStateStack.pop_back(); |
|||
recursionIteratorStack.pop_back(); |
|||
|
|||
// If there is at least one state under the current one in our recursion stack, we need
|
|||
// to restore the topmost state as the current state and jump to the part after the
|
|||
// original recursive call.
|
|||
if (recursionStateStack.size() > 0) { |
|||
currentState = recursionStateStack.back(); |
|||
successorIt = recursionIteratorStack.back(); |
|||
|
|||
goto recursionStepBackward; |
|||
} |
|||
} |
|||
} |
|||
|
|||
template class StronglyConnectedComponentDecomposition<double>; |
|||
} // namespace storage
|
|||
} // namespace storm
|
@ -0,0 +1,49 @@ |
|||
#ifndef STORM_STORAGE_STRONGLYCONNECTEDCOMPONENTDECOMPOSITION_H_ |
|||
#define STORM_STORAGE_STRONGLYCONNECTEDCOMPONENTDECOMPOSITION_H_ |
|||
|
|||
#include "src/storage/Decomposition.h" |
|||
#include "src/storage/VectorSet.h" |
|||
#include "src/storage/BitVector.h" |
|||
#include "src/storage/SparseMatrix.h" |
|||
|
|||
namespace storm { |
|||
namespace models { |
|||
// Forward declare the abstract model class. |
|||
template <typename T> class AbstractModel; |
|||
} |
|||
|
|||
namespace storage { |
|||
|
|||
/*! |
|||
* This class represents the decomposition of a graph-like structure into its strongly connected components. |
|||
*/ |
|||
template <typename T> |
|||
class StronglyConnectedComponentDecomposition : public Decomposition { |
|||
public: |
|||
/* |
|||
* Creates an empty SCC decomposition. |
|||
*/ |
|||
StronglyConnectedComponentDecomposition(); |
|||
|
|||
/* |
|||
* Creates an SCC decomposition of the given model. |
|||
*/ |
|||
StronglyConnectedComponentDecomposition(storm::models::AbstractModel<T> const& model); |
|||
|
|||
StronglyConnectedComponentDecomposition(StronglyConnectedComponentDecomposition const& other); |
|||
|
|||
StronglyConnectedComponentDecomposition& operator=(StronglyConnectedComponentDecomposition const& other); |
|||
|
|||
StronglyConnectedComponentDecomposition(StronglyConnectedComponentDecomposition&& other); |
|||
|
|||
StronglyConnectedComponentDecomposition& operator=(StronglyConnectedComponentDecomposition&& other); |
|||
|
|||
private: |
|||
void performSccDecomposition(storm::models::AbstractModel<T> const& model); |
|||
|
|||
void performSccDecompositionHelper(storm::models::AbstractModel<T> const& model, uint_fast64_t startState, uint_fast64_t& currentIndex, std::vector<uint_fast64_t>& stateIndices, std::vector<uint_fast64_t>& lowlinks, std::vector<uint_fast64_t>& tarjanStack, storm::storage::BitVector& tarjanStackStates, storm::storage::BitVector& visitedStates); |
|||
}; |
|||
} |
|||
} |
|||
|
|||
#endif /* STORM_STORAGE_STRONGLYCONNECTEDCOMPONENTDECOMPOSITION_H_ */ |
Write
Preview
Loading…
Cancel
Save
Reference in new issue