|
|
@ -4,6 +4,7 @@ |
|
|
|
#include "storm/models/sparse/StandardRewardModel.h"
|
|
|
|
#include "storm/adapters/RationalFunctionAdapter.h"
|
|
|
|
#include "storm/utility/macros.h"
|
|
|
|
#include "storm/utility/Stopwatch.h"
|
|
|
|
|
|
|
|
#include "storm/exceptions/UnexpectedException.h"
|
|
|
|
|
|
|
@ -54,6 +55,7 @@ namespace storm { |
|
|
|
* @param currentIndex The next free index that can be assigned to states. |
|
|
|
* @param hasPreorderNumber A bit that is used to keep track of the states that already have a preorder number. |
|
|
|
* @param preorderNumbers A vector storing the preorder number for each state. |
|
|
|
* @param recursionStateStack memory used for the recursion stack. |
|
|
|
* @param s The stack S used by the algorithm. |
|
|
|
* @param p The stack S used by the algorithm. |
|
|
|
* @param stateHasScc A bit vector containing all states that have already been assigned to an SCC. |
|
|
@ -63,12 +65,11 @@ namespace storm { |
|
|
|
* is increased. |
|
|
|
*/ |
|
|
|
template <typename ValueType> |
|
|
|
void performSccDecompositionGCM(storm::storage::SparseMatrix<ValueType> const& transitionMatrix, uint_fast64_t startState, storm::storage::BitVector& nonTrivialStates, storm::storage::BitVector const* subsystem, storm::storage::BitVector const* choices, uint_fast64_t& currentIndex, storm::storage::BitVector& hasPreorderNumber, std::vector<uint_fast64_t>& preorderNumbers, std::vector<uint_fast64_t>& s, std::vector<uint_fast64_t>& p, storm::storage::BitVector& stateHasScc, std::vector<uint_fast64_t>& stateToSccMapping, uint_fast64_t& sccCount, bool /*forceTopologicalSort*/, std::vector<uint_fast64_t>* sccDepths) { |
|
|
|
void performSccDecompositionGCM(storm::storage::SparseMatrix<ValueType> const& transitionMatrix, uint_fast64_t startState, storm::storage::BitVector& nonTrivialStates, storm::storage::BitVector const* subsystem, storm::storage::BitVector const* choices, uint_fast64_t& currentIndex, storm::storage::BitVector& hasPreorderNumber, std::vector<uint_fast64_t>& preorderNumbers, std::vector<uint_fast64_t>& recursionStateStack, std::vector<uint_fast64_t>& s, std::vector<uint_fast64_t>& p, storm::storage::BitVector& stateHasScc, std::vector<uint_fast64_t>& stateToSccMapping, uint_fast64_t& sccCount, bool /*forceTopologicalSort*/, std::vector<uint_fast64_t>* sccDepths) { |
|
|
|
// The forceTopologicalSort flag can be ignored as this method always generates a topological sort.
|
|
|
|
|
|
|
|
// Prepare the stack used for turning the recursive procedure into an iterative one.
|
|
|
|
std::vector<uint_fast64_t> recursionStateStack; |
|
|
|
recursionStateStack.reserve(transitionMatrix.getRowGroupCount()); |
|
|
|
STORM_LOG_ASSERT(recursionStateStack.empty(), "Expected an empty recursion stack.") |
|
|
|
recursionStateStack.push_back(startState); |
|
|
|
|
|
|
|
while (!recursionStateStack.empty()) { |
|
|
@ -157,48 +158,55 @@ namespace storm { |
|
|
|
STORM_LOG_ASSERT(!options.choicesPtr || options.subsystemPtr, "Expecting subsystem if choices are given."); |
|
|
|
|
|
|
|
uint_fast64_t numberOfStates = transitionMatrix.getRowGroupCount(); |
|
|
|
|
|
|
|
// Set up the environment of the algorithm.
|
|
|
|
// Start with the two stacks it maintains.
|
|
|
|
std::vector<uint_fast64_t> s; |
|
|
|
s.reserve(numberOfStates); |
|
|
|
std::vector<uint_fast64_t> p; |
|
|
|
p.reserve(numberOfStates); |
|
|
|
|
|
|
|
// We also need to store the preorder numbers of states and which states have been assigned to which SCC.
|
|
|
|
std::vector<uint_fast64_t> preorderNumbers(numberOfStates); |
|
|
|
storm::storage::BitVector hasPreorderNumber(numberOfStates); |
|
|
|
storm::storage::BitVector stateHasScc(numberOfStates); |
|
|
|
std::vector<uint_fast64_t> stateToSccMapping(numberOfStates); |
|
|
|
uint_fast64_t sccCount = 0; |
|
|
|
|
|
|
|
// Store scc depths if requested
|
|
|
|
std::vector<uint_fast64_t>* sccDepthsPtr = nullptr; |
|
|
|
sccDepths = boost::none; |
|
|
|
if (options.isComputeSccDepthsSet || options.areOnlyBottomSccsConsidered) { |
|
|
|
sccDepths = std::vector<uint_fast64_t>(); |
|
|
|
sccDepthsPtr = &sccDepths.get(); |
|
|
|
} |
|
|
|
|
|
|
|
// Finally, we need to keep of trivial states (singleton SCCs without selfloop).
|
|
|
|
|
|
|
|
// We need to keep of trivial states (singleton SCCs without selfloop).
|
|
|
|
storm::storage::BitVector nonTrivialStates(numberOfStates, false); |
|
|
|
|
|
|
|
// Start the search for SCCs from every state in the block.
|
|
|
|
uint_fast64_t currentIndex = 0; |
|
|
|
if (options.subsystemPtr) { |
|
|
|
for (auto state : *options.subsystemPtr) { |
|
|
|
if (!hasPreorderNumber.get(state)) { |
|
|
|
performSccDecompositionGCM(transitionMatrix, state, nonTrivialStates, options.subsystemPtr, options.choicesPtr, currentIndex, hasPreorderNumber, preorderNumbers, s, p, stateHasScc, stateToSccMapping, sccCount, options.isTopologicalSortForced, sccDepthsPtr); |
|
|
|
} |
|
|
|
// Obtain a mapping from states to the SCC it belongs to
|
|
|
|
std::vector<uint_fast64_t> stateToSccMapping(numberOfStates); |
|
|
|
{ |
|
|
|
|
|
|
|
// Set up the environment of the algorithm.
|
|
|
|
// Start with the two stacks it maintains.
|
|
|
|
// This is to reduce memory (re-)allocations
|
|
|
|
std::vector<uint_fast64_t> s; |
|
|
|
s.reserve(numberOfStates); |
|
|
|
std::vector<uint_fast64_t> p; |
|
|
|
p.reserve(numberOfStates); |
|
|
|
std::vector<uint_fast64_t> recursionStateStack; |
|
|
|
recursionStateStack.reserve(numberOfStates); |
|
|
|
|
|
|
|
// We also need to store the preorder numbers of states and which states have been assigned to which SCC.
|
|
|
|
std::vector<uint_fast64_t> preorderNumbers(numberOfStates); |
|
|
|
storm::storage::BitVector hasPreorderNumber(numberOfStates); |
|
|
|
storm::storage::BitVector stateHasScc(numberOfStates); |
|
|
|
|
|
|
|
// Store scc depths if requested
|
|
|
|
std::vector<uint_fast64_t>* sccDepthsPtr = nullptr; |
|
|
|
sccDepths = boost::none; |
|
|
|
if (options.isComputeSccDepthsSet || options.areOnlyBottomSccsConsidered) { |
|
|
|
sccDepths = std::vector<uint_fast64_t>(); |
|
|
|
sccDepthsPtr = &sccDepths.get(); |
|
|
|
} |
|
|
|
} else { |
|
|
|
for (uint64_t state = 0; state < transitionMatrix.getRowGroupCount(); ++state) { |
|
|
|
if (!hasPreorderNumber.get(state)) { |
|
|
|
performSccDecompositionGCM(transitionMatrix, state, nonTrivialStates, options.subsystemPtr, options.choicesPtr, currentIndex, hasPreorderNumber, preorderNumbers, s, p, stateHasScc, stateToSccMapping, sccCount, options.isTopologicalSortForced, sccDepthsPtr); |
|
|
|
|
|
|
|
|
|
|
|
// Start the search for SCCs from every state in the block.
|
|
|
|
uint_fast64_t currentIndex = 0; |
|
|
|
if (options.subsystemPtr) { |
|
|
|
for (auto state : *options.subsystemPtr) { |
|
|
|
if (!hasPreorderNumber.get(state)) { |
|
|
|
performSccDecompositionGCM(transitionMatrix, state, nonTrivialStates, options.subsystemPtr, options.choicesPtr, currentIndex, hasPreorderNumber, preorderNumbers, recursionStateStack, s, p, stateHasScc, stateToSccMapping, sccCount, options.isTopologicalSortForced, sccDepthsPtr); |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
for (uint64_t state = 0; state < transitionMatrix.getRowGroupCount(); ++state) { |
|
|
|
if (!hasPreorderNumber.get(state)) { |
|
|
|
performSccDecompositionGCM(transitionMatrix, state, nonTrivialStates, options.subsystemPtr, options.choicesPtr, currentIndex, hasPreorderNumber, preorderNumbers, recursionStateStack, s, p, stateHasScc, stateToSccMapping, sccCount, options.isTopologicalSortForced, sccDepthsPtr); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// After we obtained the state-to-SCC mapping, we build the actual blocks.
|
|
|
|
this->blocks.resize(sccCount); |
|
|
|
for (uint64_t state = 0; state < transitionMatrix.getRowGroupCount(); ++state) { |
|
|
|