Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
Partition.cpp
Go to the documentation of this file.
2
4
5namespace storm::bisimulation {
6
7namespace {
8
12[[maybe_unused]] std::size_t countBlocks(Partition const& partition) {
13 std::size_t result = 0;
14 partition.forEachBlock([&result](Partition::Block const&) { ++result; });
15 return result;
16}
17
18} // namespace
19
20Partition::NonSuperBlockSet::NonSuperBlockSet(Partition const& partition) : partition(partition), blockIndices(partition.getNumberOfElements(), false) {}
21
23 return containedBlockIndices.size();
24}
25
27 return containedBlockIndices.empty();
28}
29
31 return blockIndices.get(partition.getBlockIndex(block));
32}
33
35 STORM_LOG_ASSERT(!block.empty(), "Cannot insert empty block.");
36 STORM_LOG_ASSERT(!partition.isProperSuperBlock(block), "Cannot insert block that is a proper superblock.");
37 if (BlockIndex i = partition.getBlockIndex(block); !blockIndices.get(i)) {
38 blockIndices.set(i, true);
39 containedBlockIndices.push_back(i);
40 }
41}
42
44 STORM_LOG_ASSERT(!empty(), "Cannot pop from empty blockset.");
45 BlockIndex const i = containedBlockIndices.back();
46 blockIndices.set(i, false);
47 containedBlockIndices.pop_back();
48 return partition.getBlockFromIndex(i);
49}
50
51Partition::Partition(ElementIndex numElements) : numBlocks(1) {
52 STORM_LOG_THROW(numElements > 0, storm::exceptions::InvalidArgumentException, "Cannot create a partition without elements.");
53 auto indexRange = std::ranges::iota_view<ElementIndex, ElementIndex>(0, numElements);
54 blockContents.assign(indexRange.begin(), indexRange.end());
55 blockContentsInverse = blockContents;
56 elementToBlockIndex.assign(numElements, 0);
57 blockSizes.assign(numElements, 0);
58 blockSizes[0] = blockContents.size();
59}
60
61std::size_t Partition::getNumberOfBlocks() const {
62 STORM_LOG_ASSERT(countBlocks(*this) == numBlocks, "The cached number of blocks is inconsistent with the actual number of blocks");
63 return numBlocks;
64}
65
66std::size_t Partition::getNumberOfElements() const {
67 return elementToBlockIndex.size();
68}
69
71 return Block(blockContents.begin(), blockContents.end());
72}
73
75 STORM_LOG_ASSERT(element < elementToBlockIndex.size(), "Element index out of bounds");
76 return getBlockFromIndex(elementToBlockIndex[element]);
77}
78
79bool Partition::contains(Block const& block, ElementIndex element) const {
80 // We use the fact that each pair of blocks is either disjoint or one is a subset of the other.
81 return isSubBlockOf(getBlockOfElement(element), block);
82}
83
84bool Partition::isSubBlockOf(Block const& subblock, Block const& superblock) const {
85 STORM_LOG_ASSERT(checkBlockValidity(subblock), "Invalid subblock.");
86 STORM_LOG_ASSERT(checkBlockValidity(superblock), "Invalid superblock.");
87 // Check if subblock is a subspan of superblock
88 return subblock.data() >= superblock.data() && (subblock.data() + subblock.size()) <= (superblock.data() + superblock.size());
89}
90
91bool Partition::isEqualBlock(Block const& lhs, Block const& rhs) const {
92 STORM_LOG_ASSERT(checkBlockValidity(lhs), "Invalid block.");
93 STORM_LOG_ASSERT(checkBlockValidity(rhs), "Invalid block.");
94 return lhs.data() == rhs.data() && lhs.size() == rhs.size();
95}
96
97bool Partition::isSameBlock(ElementIndex const& element1, ElementIndex const& element2) const {
98 STORM_LOG_ASSERT(element1 < elementToBlockIndex.size() && element2 < elementToBlockIndex.size(), "Element index out of bounds");
99 // Each block is identified by the index at which it starts, so the elements are in the same block iff their (finest) blocks start at the same index.
100 return elementToBlockIndex[element1] == elementToBlockIndex[element2];
101}
102
103bool Partition::isBlockOfElement(Block const& block, ElementIndex const& element) const {
104 Block const eBlock = getBlockOfElement(element);
105 return eBlock.data() == block.data() && eBlock.size() == block.size();
106}
107
108Partition::BlockIndex Partition::getBlockIndex(Block const& block) const {
109 STORM_LOG_ASSERT(checkBlockValidity(block), "Tried to get the index of an invalid block.");
110 return std::distance(blockContents.data(), block.data());
111}
112
113Partition::Block Partition::getBlockFromIndex(BlockIndex blockIndex) const {
114 STORM_LOG_ASSERT(blockIndex < blockContents.size(), "Block index out of bounds");
115 STORM_LOG_ASSERT(blockSizes[blockIndex] > 0, "Block index points to a non-existent block");
116 return Block(blockContents.data() + blockIndex, blockSizes[blockIndex]);
117}
118
119Partition::Block Partition::registerNewBlock(BlockIndex const start, BlockIndex const end) {
120 STORM_LOG_ASSERT(start < end && end <= blockContents.size(), "Invalid block range");
121 Block newBlock(blockContents.data() + start, end - start);
122 // Shrinking an existing block doesn't require iterating over all block contents
123 if (blockSizes[start] == 0) {
124 for (ElementIndex const e : newBlock) {
125 elementToBlockIndex[e] = start;
126 }
127 } else {
128 STORM_LOG_ASSERT(blockSizes[start] >= newBlock.size(), "Shrinking a block should not increase its size");
129 }
130 blockSizes[start] = newBlock.size();
131 return newBlock;
132}
133
134bool Partition::checkBlockValidity(Block const& block) const {
135 // block must not be empty
136 if (block.empty()) {
137 return false;
138 }
139 // the block refers to this partition
140 if (block.data() < blockContents.data() || block.data() + block.size() > blockContents.data() + blockContents.size()) {
141 return false;
142 }
143
144 BlockIndex const blockIndex = std::distance(blockContents.data(), block.data());
145 BlockIndex const blockEndIndex = blockIndex + block.size();
146
147 // the block has a stored size and is either the last block or ends where another block starts
148 if (!(blockSizes[blockIndex] > 0 && (blockEndIndex == blockContents.size() || blockSizes[blockEndIndex] != 0))) {
149 return false;
150 }
151
152 // The index of the first element must always match this block index (even if the element belongs to a subblock, it must have the same index)
153 if (elementToBlockIndex[block[0]] != blockIndex) {
154 return false;
155 }
156
157 // Check if the inverse mapping is consistent for the first and the last element (must actually be true for all elements but checking that is too costly)
158 if (&blockContents[blockContentsInverse[block.front()]] != &block.front() || &blockContents[blockContentsInverse[block.back()]] != &block.back()) {
159 return false;
160 }
161
162 if (isBlockOfElement(block, block[0])) {
163 // this block has no proper sub-block
164 // The stored sizes should coincide
165 if (blockSizes[blockIndex] != block.size()) {
166 return false;
167 }
168 // all elements should point to this blockIndex
169 // we only check this for the first and last element.
170 return (elementToBlockIndex[block.front()] == blockIndex) && (elementToBlockIndex[block.back()] == blockIndex);
171 } else {
172 // this block has proper sub-blocks.
173 // The stored size should be strictly smaller
174 if (blockSizes[blockIndex] >= block.size()) {
175 return false;
176 }
177 // The block index of all elements should be within the range of this block.
178 return std::all_of(block.begin(), block.end(), [this, blockIndex, blockEndIndex](ElementIndex const& e) {
179 // e's block index should be in the range of this block
180 return elementToBlockIndex[e] >= blockIndex && elementToBlockIndex[e] < blockEndIndex;
181 });
182 }
183}
184
185bool Partition::isProperSuperBlock(Block const& block) const {
186 STORM_LOG_ASSERT(checkBlockValidity(block), "Tried to check if an invalid block is a proper superblock.");
187 // block is a proper super-block iff it has a proper sub-block iff the block of some element is not equal to block
188 return !isBlockOfElement(block, block[0]);
189}
190
191std::ostream& operator<<(std::ostream& os, const Partition& partition) {
192 os << "Partition (" << partition.getNumberOfBlocks() << " block(s), " << partition.getNumberOfElements() << " element(s)): {\n";
193 partition.forEachBlock([&os](Partition::Block const& block) {
194 os << "\t{";
195 for (bool first = true; typename Partition::ElementIndex const e : block) {
196 if (!first) {
197 os << ", ";
198 }
199 first = false;
200 os << e;
201 }
202 os << "}\n";
203 });
204 os << "}";
205 return os;
206}
207
208} // namespace storm::bisimulation
NonSuperBlockSet(Partition const &partition)
Creates an empty set associated with the given partition.
Definition Partition.cpp:20
void insert(Block const &block)
Inserts the given block into the set.
Definition Partition.cpp:34
Block pop()
Removes and returns an arbitrary block from the set.
Definition Partition.cpp:43
bool contains(Block const &block) const
Definition Partition.cpp:30
Represents a partition of a set of consecutive indices.
Definition Partition.h:20
bool contains(Block const &block, ElementIndex element) const
Definition Partition.cpp:79
void forEachBlock(Func const &f) const
Applies the given function on each block in the partition.
Definition Partition.h:184
uint64_t ElementIndex
The type used to index the elements of the partition.
Definition Partition.h:23
std::size_t getNumberOfBlocks() const
Retrieves the number of blocks.
Definition Partition.cpp:61
bool isSameBlock(ElementIndex const &element1, ElementIndex const &element2) const
Definition Partition.cpp:97
bool isBlockOfElement(Block const &block, ElementIndex const &element) const
std::size_t getNumberOfElements() const
Retrieves the number of elements.
Definition Partition.cpp:66
std::span< ElementIndex const > Block
A contiguous range of elements that form a (possibly proper super-) block of the partition.
Definition Partition.h:27
bool isSubBlockOf(Block const &subblock, Block const &superblock) const
Definition Partition.cpp:84
Block getBlockOfElement(ElementIndex element) const
Definition Partition.cpp:74
bool isProperSuperBlock(Block const &block) const
bool isEqualBlock(Block const &lhs, Block const &rhs) const
Definition Partition.cpp:91
bool checkBlockValidity(Block const &block) const
Checks if the given block is a valid block in the partition.
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:39
std::ostream & operator<<(std::ostream &os, const Partition &partition)