|
|
@ -8,13 +8,24 @@ namespace storm { |
|
|
|
namespace storage { |
|
|
|
|
|
|
|
template<typename ValueType> |
|
|
|
BisimulationDecomposition2<ValueType>::Block::Block(storm::storage::sparse::state_type begin, storm::storage::sparse::state_type end, Block* prev, Block* next) : begin(begin), end(end), prev(prev), next(next), numberOfStates(end - begin) { |
|
|
|
BisimulationDecomposition2<ValueType>::Block::Block(storm::storage::sparse::state_type begin, storm::storage::sparse::state_type end, Block* prev, Block* next) : begin(begin), end(end), prev(prev), next(next), numberOfStates(end - begin), isMarked(false) { |
|
|
|
// Intentionally left empty.
|
|
|
|
} |
|
|
|
|
|
|
|
template<typename ValueType> |
|
|
|
void BisimulationDecomposition2<ValueType>::Block::print(Partition const& partition) const { |
|
|
|
std::cout << "this " << this << std::endl; |
|
|
|
std::cout << "begin: " << this->begin << " and end: " << this->end << " (number of states: " << this->numberOfStates << ")" << std::endl; |
|
|
|
std::cout << "next: " << this->next << " and prev " << this->prev << std::endl; |
|
|
|
std::cout << "states:" << std::endl; |
|
|
|
for (storm::storage::sparse::state_type index = this->begin; index < this->end; ++index) { |
|
|
|
std::cout << partition.states[index] << " " << std::endl; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
template<typename ValueType> |
|
|
|
BisimulationDecomposition2<ValueType>::Partition::Partition(std::size_t numberOfStates) : stateToBlockMapping(numberOfStates), states(numberOfStates), positions(numberOfStates), values(numberOfStates) { |
|
|
|
this->blocks.back().itToSelf = blocks.emplace(this->blocks.end(), 0, numberOfStates, nullptr); |
|
|
|
this->blocks.back().itToSelf = blocks.emplace(this->blocks.end(), 0, numberOfStates, nullptr, nullptr); |
|
|
|
for (storm::storage::sparse::state_type state = 0; state < numberOfStates; ++state) { |
|
|
|
states[state] = state; |
|
|
|
positions[state] = state; |
|
|
@ -44,12 +55,18 @@ namespace storm { |
|
|
|
auto cutPoint = std::distance(this->states.begin(), it); |
|
|
|
|
|
|
|
++blockIterator; |
|
|
|
auto newBlockIterator = this->blocks.emplace(blockIterator, cutPoint, block.end, &(*blockIterator), block.next); |
|
|
|
auto newBlockIterator = this->blocks.emplace(blockIterator, cutPoint, block.end, &block, block.next); |
|
|
|
newBlockIterator->itToSelf = newBlockIterator; |
|
|
|
|
|
|
|
// Make the old block end at the cut position and insert a new block after it.
|
|
|
|
block.end = cutPoint; |
|
|
|
block.next = &(*newBlockIterator); |
|
|
|
block.numberOfStates = block.end - block.begin; |
|
|
|
|
|
|
|
// Update the block mapping for all states that we just removed from the block.
|
|
|
|
for (auto it = this->states.begin() + newBlockIterator->begin, ite = this->states.begin() + newBlockIterator->end; it != ite; ++it) { |
|
|
|
stateToBlockMapping[*it] = &(*newBlockIterator); |
|
|
|
} |
|
|
|
} else { |
|
|
|
// Otherwise, we simply proceed to the next block.
|
|
|
|
++blockIterator; |
|
|
@ -60,12 +77,21 @@ namespace storm { |
|
|
|
template<typename ValueType> |
|
|
|
void BisimulationDecomposition2<ValueType>::Partition::print() const { |
|
|
|
for (auto const& block : this->blocks) { |
|
|
|
std::cout << "begin: " << block.begin << " and end: " << block.end << " (number of states: " << block.numberOfStates << ")" << std::endl; |
|
|
|
std::cout << "states:" << std::endl; |
|
|
|
for (storm::storage::sparse::state_type index = block.begin; index < block.end; ++index) { |
|
|
|
std::cout << this->states[index] << " " << std::endl; |
|
|
|
} |
|
|
|
block.print(*this); |
|
|
|
} |
|
|
|
std::cout << "states" << std::endl; |
|
|
|
for (auto const& state : states) { |
|
|
|
std::cout << state << " "; |
|
|
|
} |
|
|
|
std::cout << std::endl << "positions: " << std::endl; |
|
|
|
for (auto const& index : positions) { |
|
|
|
std::cout << index << " "; |
|
|
|
} |
|
|
|
std::cout << std::endl << "state to block mapping: " << std::endl; |
|
|
|
for (auto const& block : stateToBlockMapping) { |
|
|
|
std::cout << block << " "; |
|
|
|
} |
|
|
|
std::cout << std::endl; |
|
|
|
} |
|
|
|
|
|
|
|
template<typename ValueType> |
|
|
@ -105,6 +131,10 @@ namespace storm { |
|
|
|
while (!splitterQueue.empty()) { |
|
|
|
splitPartition(backwardTransitions, *splitterQueue.front(), partition, splitterQueue); |
|
|
|
splitterQueue.pop_front(); |
|
|
|
|
|
|
|
std::cout << "####### updated partition ##############" << std::endl; |
|
|
|
partition.print(); |
|
|
|
std::cout << "####### end of updated partition #######" << std::endl; |
|
|
|
} |
|
|
|
|
|
|
|
std::chrono::high_resolution_clock::duration totalTime = std::chrono::high_resolution_clock::now() - totalStart; |
|
|
@ -112,8 +142,63 @@ namespace storm { |
|
|
|
} |
|
|
|
|
|
|
|
template<typename ValueType> |
|
|
|
std::size_t BisimulationDecomposition2<ValueType>::splitPartition(storm::storage::SparseMatrix<ValueType> const& backwardTransitions, Block const& splitter, Partition& partition, std::deque<Block*>& splitterQueue) { |
|
|
|
std::size_t BisimulationDecomposition2<ValueType>::splitBlockProbabilities(Block* block, Partition& partition, std::deque<Block*>& splitterQueue) { |
|
|
|
Block& currentBlock = *block; |
|
|
|
|
|
|
|
// Sort the states in the block based on their probabilities.
|
|
|
|
std::sort(partition.states.begin() + currentBlock.begin, partition.states.begin() + currentBlock.end, [&partition] (storm::storage::sparse::state_type const& a, storm::storage::sparse::state_type const& b) { return partition.values[a] < partition.values[b]; } ); |
|
|
|
|
|
|
|
// FIXME: This can probably be done more efficiently.
|
|
|
|
std::sort(partition.values.begin() + currentBlock.begin, partition.values.begin() + currentBlock.end); |
|
|
|
|
|
|
|
// Update the positions vector.
|
|
|
|
storm::storage::sparse::state_type position = currentBlock.begin; |
|
|
|
for (auto stateIt = partition.states.begin() + currentBlock.begin, stateIte = partition.states.begin() + currentBlock.end; stateIt != stateIte; ++stateIt, ++position) { |
|
|
|
partition.positions[*stateIt] = position; |
|
|
|
} |
|
|
|
|
|
|
|
// Finally, we need to scan the ranges of states that agree on the probability.
|
|
|
|
storm::storage::sparse::state_type beginIndex = currentBlock.begin; |
|
|
|
storm::storage::sparse::state_type currentIndex = beginIndex; |
|
|
|
storm::storage::sparse::state_type endIndex = currentBlock.end; |
|
|
|
|
|
|
|
Block* prevBlock = block->prev; |
|
|
|
|
|
|
|
std::list<Block*> createdBlocks; |
|
|
|
std::cout << currentIndex << " < " << endIndex << std::endl; |
|
|
|
while (currentIndex < endIndex) { |
|
|
|
ValueType& currentValue = *(partition.values.begin() + currentIndex); |
|
|
|
|
|
|
|
++currentIndex; |
|
|
|
ValueType* nextValuePtr = ¤tValue; |
|
|
|
while (currentIndex < endIndex && std::abs(currentValue - *nextValuePtr) < 1e-6) { |
|
|
|
++currentIndex; |
|
|
|
++nextValuePtr; |
|
|
|
} |
|
|
|
|
|
|
|
// Create a new block from the states that agree on the values.
|
|
|
|
typename std::list<Block>::iterator newBlockIterator = partition.blocks.emplace(currentBlock.itToSelf, beginIndex, endIndex, prevBlock, currentBlock.next); |
|
|
|
newBlockIterator->itToSelf = newBlockIterator; |
|
|
|
if (prevBlock != nullptr) { |
|
|
|
prevBlock->next = &(*newBlockIterator); |
|
|
|
} |
|
|
|
prevBlock = &(*newBlockIterator); |
|
|
|
if (prevBlock->numberOfStates > 1) { |
|
|
|
createdBlocks.emplace_back(prevBlock); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
for (auto block : createdBlocks) { |
|
|
|
splitterQueue.push_back(block); |
|
|
|
} |
|
|
|
|
|
|
|
return createdBlocks.size(); |
|
|
|
} |
|
|
|
|
|
|
|
template<typename ValueType> |
|
|
|
std::size_t BisimulationDecomposition2<ValueType>::splitPartition(storm::storage::SparseMatrix<ValueType> const& backwardTransitions, Block const& splitter, Partition& partition, std::deque<Block*>& splitterQueue) { |
|
|
|
std::cout << "getting block " << &splitter << " as splitter" << std::endl; |
|
|
|
splitter.print(partition); |
|
|
|
std::list<Block*> predecessorBlocks; |
|
|
|
|
|
|
|
// Iterate over all states of the splitter and check its predecessors.
|
|
|
@ -122,17 +207,36 @@ namespace storm { |
|
|
|
|
|
|
|
for (auto const& predecessorEntry : backwardTransitions.getRow(state)) { |
|
|
|
storm::storage::sparse::state_type predecessor = predecessorEntry.getColumn(); |
|
|
|
std::cout << "found pred " << predecessor << std::endl; |
|
|
|
Block* predecessorBlock = partition.stateToBlockMapping[predecessor]; |
|
|
|
std::cout << "predecessor block " << std::endl; |
|
|
|
predecessorBlock->print(partition); |
|
|
|
|
|
|
|
// If the predecessor block has just one state, there is no point in splitting it.
|
|
|
|
if (predecessorBlock->numberOfStates <= 1) { |
|
|
|
std::cout << "continuing" << std::endl; |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
storm::storage::sparse::state_type predecessorPosition = partition.positions[predecessor]; |
|
|
|
|
|
|
|
// If we have not seen this predecessor before, we move it to a part near the beginning of the block.
|
|
|
|
if (predecessorPosition < predecessorBlock->begin) { |
|
|
|
std::cout << "predecessor position: " << predecessorPosition << " and begin " << predecessorBlock->begin << std::endl; |
|
|
|
if (predecessorPosition >= predecessorBlock->begin) { |
|
|
|
std::swap(partition.states[predecessorPosition], partition.states[predecessorBlock->begin]); |
|
|
|
std::swap(partition.positions[predecessor], partition.positions[predecessorBlock->begin]); |
|
|
|
std::cout << "swapping positions of " << predecessor << " and " << partition.states[predecessorPosition] << std::endl; |
|
|
|
storm::storage::sparse::state_type tmp = partition.positions[partition.states[predecessorPosition]]; |
|
|
|
partition.positions[partition.states[predecessorPosition]] = partition.positions[predecessor]; |
|
|
|
partition.positions[predecessor] = tmp; |
|
|
|
|
|
|
|
// std::swap(partition.positions[predecessor], partition.positions[predecessorBlock->begin]);
|
|
|
|
|
|
|
|
++predecessorBlock->begin; |
|
|
|
std::cout << "incrementing begin... " << std::endl; |
|
|
|
partition.values[predecessor] = predecessorEntry.getValue(); |
|
|
|
} else { |
|
|
|
// Otherwise, we just need to update the probability for this predecessor.
|
|
|
|
std::cout << "updating probability" << std::endl; |
|
|
|
partition.values[predecessor] += predecessorEntry.getValue(); |
|
|
|
} |
|
|
|
|
|
|
@ -143,20 +247,60 @@ namespace storm { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
std::list<Block*> blocksToSplit; |
|
|
|
|
|
|
|
// Now, we can iterate over the predecessor blocks and see whether we have to create a new block for
|
|
|
|
// predecessors of the splitter.
|
|
|
|
for (auto block : predecessorBlocks) { |
|
|
|
block->isMarked = false; |
|
|
|
|
|
|
|
// If we have moved the begin of the block to somewhere in the middle of the block, we need to split it.
|
|
|
|
if (block->begin != block->end) { |
|
|
|
|
|
|
|
|
|
|
|
std::cout << "moved begin to " << block->begin << " and end to " << block->end << std::endl; |
|
|
|
storm::storage::sparse::state_type tmpBegin = block->begin; |
|
|
|
storm::storage::sparse::state_type tmpEnd = block->end; |
|
|
|
|
|
|
|
block->begin = block->prev != nullptr ? block->prev->end : 0; |
|
|
|
std::cout << "begin: " << block->begin << " and not-null? " << (block->prev != nullptr) << ": " << block->prev << std::endl; |
|
|
|
block->end = tmpBegin; |
|
|
|
block->numberOfStates = block->end - block->begin; |
|
|
|
|
|
|
|
// Create a new block that holds all states that do not have a successor in the current splitter.
|
|
|
|
typename std::list<Block>::iterator it = partition.blocks.emplace(block->next != nullptr ? block->next->itToSelf : partition.blocks.end(), tmpBegin, tmpEnd, block, block->next); |
|
|
|
Block* newBlock = &(*it); |
|
|
|
newBlock->itToSelf = it; |
|
|
|
if (block->next != nullptr) { |
|
|
|
block->next->prev = newBlock; |
|
|
|
} |
|
|
|
block->next = newBlock; |
|
|
|
|
|
|
|
std::cout << "created new block " << std::endl; |
|
|
|
newBlock->print(partition); |
|
|
|
|
|
|
|
// Update the block mapping in the partition.
|
|
|
|
for (auto it = partition.states.begin() + newBlock->begin, ite = partition.states.begin() + newBlock->end; it != ite; ++it) { |
|
|
|
partition.stateToBlockMapping[*it] = newBlock; |
|
|
|
} |
|
|
|
|
|
|
|
// Mark the half of the block that can be further refined using the probability information.
|
|
|
|
blocksToSplit.emplace_back(block); |
|
|
|
block->print(partition); |
|
|
|
|
|
|
|
splitterQueue.push_back(newBlock); |
|
|
|
} else { |
|
|
|
std::cout << "found block to split" << std::endl; |
|
|
|
blocksToSplit.emplace_back(block); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Finally, we walk through the blocks that have a transition to the splitter and split them using
|
|
|
|
// probabilistic information.
|
|
|
|
for (auto block : blocksToSplit) { |
|
|
|
if (block->numberOfStates <= 1) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
splitBlockProbabilities(block, partition, splitterQueue); |
|
|
|
} |
|
|
|
|
|
|
|
return 0; |
|
|
|