101 return ComparisonResult::equivalent;
104template<
typename ValueType, SignatureMode Mode>
105bool Signatures<ValueType, Mode>::ChoiceSignature::approximatelyEqual(ChoiceSignature
const& other, ValueType
const& tolerance)
const
108 if (compareStructure(other) != 0) {
111 auto otherIt = other.distr.begin();
112 for (
auto const& [block, value] : distr) {
121template<
typename ValueType, SignatureMode Mode>
122auto Signatures<ValueType, Mode>::StateSignature::find(ChoiceSignature
const& signature, ValueType
const tolerance)
const
123 -> std::pair<ChoiceSignatureIterator, bool> {
124 if (choices.empty()) {
125 return std::make_pair(choices.end(),
false);
127 auto it = std::lower_bound(choices.begin(), choices.end(), signature, [](ChoiceSignature
const& choice1, ChoiceSignature
const& choice2) {
128 return choice1.compare(choice2) == ChoiceSignature::ComparisonResult::less;
130 if constexpr (Mode == SignatureMode::Exact) {
131 return std::make_pair(it, it != choices.end() && it->compare(signature) == std::strong_ordering::equal);
133 if (signature.distr.empty()) {
135 return std::make_pair(it, it != choices.end() && it->compareStructure(signature) == std::strong_ordering::equal);
137 return findWithHint(it, signature, tolerance);
141template<
typename ValueType, SignatureMode Mode>
142auto Signatures<ValueType, Mode>::StateSignature::findWithHint(ChoiceSignatureIterator hint, ChoiceSignature
const& signature, ValueType
const tolerance)
const
143 -> std::pair<ChoiceSignatureIterator, bool>
144 requires(Mode == SignatureMode::Approximative)
151 STORM_LOG_ASSERT(hint >= choices.begin() && hint <= choices.end(),
"Hint iterator must be within the range of choices.");
152 STORM_LOG_ASSERT(!signature.distr.empty(),
"Distributions must not be empty.");
155 enum class Location { BeforeWindow, InWindow, AfterWindow };
157 auto const locate = [
this, &signature, &near](ChoiceSignatureIterator choiceIt) {
158 if (choiceIt == choices.end()) {
159 return Location::AfterWindow;
161 auto const& choice = *choiceIt;
162 if (
auto const cmp = choice.compareStructure(signature); cmp != 0) {
163 return cmp < 0 ? Location::BeforeWindow : Location::AfterWindow;
165 auto const& value = choice.distr.front().second;
166 auto const& signatureValue = signature.distr.front().second;
167 if (near(value, signatureValue)) {
168 return Location::InWindow;
170 return value < signatureValue ? Location::BeforeWindow : Location::AfterWindow;
173 auto const found = [&signature, &near](ChoiceSignature
const& choice) {
175 auto it1 = choice.distr.begin() + 1;
176 auto it2 = signature.distr.begin() + 1;
177 for (; it1 != choice.distr.end() && it2 != signature.distr.end(); ++it1, ++it2) {
178 if (!near(it1->second, it2->second)) {
186 auto const scanToRightBoundary = [&](ChoiceSignatureIterator it) {
188 while (locate(it) == Location::InWindow) {
190 return std::make_pair(it,
true);
194 return std::make_pair(it,
false);
197 auto const scanToLeftBoundary = [&](ChoiceSignatureIterator it) {
198 while (it != choices.begin()) {
200 if (locate(it) != Location::InWindow) {
204 return std::make_pair(it,
true);
207 return std::make_pair(it,
false);
212 auto const hintLocation = locate(hint);
213 if (hintLocation == Location::InWindow) {
215 return std::make_pair(hint,
true);
217 if (
auto resRight = scanToRightBoundary(hint); resRight.second) {
220 if (
auto resLeft = scanToLeftBoundary(hint); resLeft.second) {
225 auto startLocation = hintLocation;
226 if (hintLocation == Location::BeforeWindow) {
227 while (startLocation == Location::BeforeWindow) {
229 startLocation = locate(start);
232 while (startLocation == Location::AfterWindow && start != choices.begin()) {
234 startLocation = locate(start);
237 if (startLocation == Location::InWindow) {
239 return std::make_pair(start,
true);
241 if (
auto result = hintLocation == Location::BeforeWindow ? scanToRightBoundary(start) : scanToLeftBoundary(start); result.second) {
246 return std::make_pair(hint,
false);
249template<
typename ValueType, SignatureMode Mode>
250void Signatures<ValueType, Mode>::StateSignature::insert(ChoiceSignature
const& signature, ValueType
const tolerance) {
251 auto [it, found] = find(signature, tolerance);
253 choices.insert(it, signature);
257template<
typename ValueType, SignatureMode Mode>
261 : choiceSignatureCache(partition.getNumberOfElements()),
263 partition(partition),
264 choiceClasses(choiceClasses),
265 choiceDistributionStorage(model.getTransitionMatrix().getEntryCount()),
267 stateSignatureCache(model.getNumberOfStates()) {}
269template<
typename ValueType, SignatureMode Mode>
273 : choiceSignatureCache(partition.getNumberOfElements()),
275 partition(partition),
276 choiceClasses(choiceClasses),
277 choiceDistributionStorage(model.getTransitionMatrix().getEntryCount()),
278 halfTolerance(tolerance /
storm::
utility::convertNumber<ValueType, uint64_t>(2)),
279 stateSignatureCache(model.getNumberOfStates()) {}
281template<
typename ValueType, SignatureMode Mode>
282auto Signatures<ValueType, Mode>::buildChoiceSignature(uint64_t
const choiceIndex) -> ChoiceSignature {
283 auto const& matrix = model.getTransitionMatrix();
285 for (
auto const& entry : matrix.getRow(choiceIndex)) {
287 choiceSignatureCache.addValue(partition.getBlockOfElement(entry.getColumn()), entry.getValue());
291 auto const row = matrix.getRow(choiceIndex);
292 uint64_t
const offset = std::distance(matrix.begin(), row.begin());
293 BlockDistributionView distrStorage(choiceDistributionStorage.data() + offset, row.getNumberOfEntries());
296 return ChoiceSignature{.choiceClass = choiceClasses ? (*choiceClasses)[choiceIndex] : 0, .distr = choiceSignatureCache.extract(distrStorage)};
299template<
typename ValueType, SignatureMode Mode>
300auto Signatures<ValueType, Mode>::getChoiceSignature(uint64_t
const choiceIndex)
const -> ChoiceSignature {
302 auto const& matrix = model.getTransitionMatrix();
303 auto const row = matrix.getRow(choiceIndex);
304 uint64_t
const offset = std::distance(matrix.begin(), row.begin());
305 BlockDistributionView distrStorage(choiceDistributionStorage.data() + offset, row.getNumberOfEntries());
309 for (
auto const& [block, _] : distrStorage) {
316 return ChoiceSignature{.choiceClass = choiceClasses ? (*choiceClasses)[choiceIndex] : 0, .distr = distrStorage.first(size)};
319template<
typename ValueType, SignatureMode Mode>
321 auto& sig = stateSignatureCache[stateIndex];
323 for (uint64_t
const choiceIndex : model.getTransitionMatrix().getRowGroupIndices(stateIndex)) {
324 sig.insert(buildChoiceSignature(choiceIndex), halfTolerance);
328template<
typename ValueType, SignatureMode Mode>
335template<
typename ValueType, SignatureMode Mode>
342template<
typename ValueType, SignatureMode Mode>
344 auto const& sig1 = signatures[state1];
345 auto const& sig2 = signatures[state2];
346 if (
auto const cmp = sig1.choices.size() <=> sig2.choices.size(); cmp != 0) {
349 auto it2 = sig2.choices.begin();
350 for (
auto const& choice : sig1.choices) {
353 if (
auto const cmp = choice.compare(*it2); cmp != 0) {
358 if (
auto const cmp = choice.compareStructure(*it2); cmp != 0) {
367template<
typename ValueType, SignatureMode Mode>
374template<
typename ValueType, SignatureMode Mode>
378 auto const& sig1 = signatures[state1];
379 auto const& sig2 = signatures[state2];
382 STORM_LOG_ASSERT(sig1.choices.size() == sig2.choices.size(),
"SplitCondition should only be called for signatures with the same number of choices.");
383 auto it1 = sig1.choices.begin();
384 auto it2 = sig2.choices.begin();
385 for (; it1 != sig1.choices.end() && it2 != sig2.choices.end(); ++it1, ++it2) {
386 STORM_LOG_ASSERT(it1->distr.size() == it2->distr.size(),
"SplitCondition should only be called for signatures with pointwise same choice structure.");
387 if (it1->distr.empty()) {
391 auto const [choiceInSig1It, foundIn1] = sig1.findWithHint(it1, *it2, tolerance);
396 if (it1 != choiceInSig1It && !sig2.findWithHint(it2, *it1, tolerance).second) {
404template<
typename ValueType, SignatureMode Mode>
405void Signatures<ValueType, Mode>::addQuotientChoiceMapping(uint64_t
const state, StateSignature
const& representativeSignature,
406 std::vector<uint64_t>
const& choiceSignatureToQuotientChoiceIndex,
407 std::vector<uint64_t>& toQuotientChoice)
const {
408 constexpr uint64_t Invalid = std::numeric_limits<uint64_t>::max();
409 auto const& stateSignature = stateSignatureCache[state];
410 auto const choiceIndices = model.getTransitionMatrix().getRowGroupIndices(state);
411 STORM_LOG_ASSERT(stateSignature.choices.size() == representativeSignature.choices.size(),
"States in a block have different number of choice signatures.");
418 std::vector<uint64_t> choiceSignatureMatching;
422 STORM_LOG_ASSERT(std::all_of(choiceSignatureMatching.begin(), choiceSignatureMatching.end(),
423 [&stateSignature, &representativeSignature](uint64_t
const i) {
424 return stateSignature.choices[i].compare(representativeSignature.choices[i]) == std::strong_ordering::equal;
426 "In exact mode, choice signatures are expected to be equal for states in the same block.");
430 stateSignature.choices.size(), [&stateSignature, &representativeSignature,
this](uint64_t
const stateChoice, uint64_t
const representativeChoice) {
431 return stateSignature.choices[stateChoice].approximatelyEqual(representativeSignature.choices[representativeChoice], halfTolerance);
435 STORM_LOG_THROW(optionalChoiceSignatureMatching.has_value(), storm::exceptions::UnexpectedException,
436 "Unable to match the choices of state " << state <<
" with the choices of the state that represents it in the quotient. "
437 <<
"Try again with a smaller tolerance.");
438 choiceSignatureMatching = std::move(*optionalChoiceSignatureMatching);
441 for (uint64_t
const choiceIndex : choiceIndices) {
442 auto [it, found] = stateSignature.find(getChoiceSignature(choiceIndex), halfTolerance);
443 STORM_LOG_ASSERT(found,
"Expected to find the signature of a choice in the signature of its own state");
444 uint64_t
const indexInStateSignature = std::distance(stateSignature.choices.begin(), it);
445 uint64_t
const indexInRepresentativeSignature = choiceSignatureMatching[indexInStateSignature];
446 STORM_LOG_ASSERT(choiceSignatureToQuotientChoiceIndex[indexInRepresentativeSignature] != Invalid,
447 "Expected to have already seen this representative choice");
448 toQuotientChoice[choiceIndex] = choiceSignatureToQuotientChoiceIndex[indexInRepresentativeSignature];
453 std::set<uint64_t> seenQuotientChoices;
454 for (uint64_t
const choiceIndex : choiceIndices) {
455 seenQuotientChoices.insert(toQuotientChoice[choiceIndex]);
457 return seenQuotientChoices.size() == representativeSignature.choices.size();
459 "The mapping from state choices to quotient choices is not surjective.");
462template<
typename ValueType, SignatureMode Mode>
465 if (createQuotientChoiceMapping) {
471 std::vector<uint64_t> choiceSignatureToQuotientChoiceIndex;
472 constexpr uint64_t Invalid = std::numeric_limits<uint64_t>::max();
474 for (uint64_t quotientState = 0; quotientState < quotientData.
toRepresentativeState.size(); ++quotientState) {
476 uint64_t
const firstQuotientChoiceIndex = signatureData.toRepresentativeChoice.size();
477 signatureData.quotientChoiceGroupIndices.push_back(firstQuotientChoiceIndex);
479 auto const& representativeSignature = stateSignatureCache[representativeState];
485 choiceSignatureToQuotientChoiceIndex.assign(representativeSignature.choices.size(), Invalid);
486 for (uint64_t
const choiceIndex : model.getTransitionMatrix().getRowGroupIndices(representativeState)) {
487 auto [it, found] = representativeSignature.find(getChoiceSignature(choiceIndex), halfTolerance);
488 STORM_LOG_ASSERT(found,
"Expected to find the signature of representative state");
489 uint64_t
const choiceSignatureIndex = std::distance(representativeSignature.choices.begin(), it);
490 if (choiceSignatureToQuotientChoiceIndex[choiceSignatureIndex] == Invalid) {
492 uint64_t
const quotientChoiceIndex = signatureData.toRepresentativeChoice.size();
493 choiceSignatureToQuotientChoiceIndex[choiceSignatureIndex] = quotientChoiceIndex;
494 signatureData.toRepresentativeChoice.push_back(choiceIndex);
495 if (createQuotientChoiceMapping) {
499 auto& distr = signatureData.quotientChoiceDistributions.emplace_back();
500 for (
auto const& [successorBlock, value] : it->distr) {
501 distr.emplace(quotientData.
toQuotientState[successorBlock.front()], value);
505 if (createQuotientChoiceMapping) {
506 (*quotientData.
toQuotientChoice)[choiceIndex] = choiceSignatureToQuotientChoiceIndex[choiceSignatureIndex];
510 if (createQuotientChoiceMapping) {
513 for (uint64_t
const state : partition.getBlockOfElement(representativeState)) {
514 if (state != representativeState) {
515 addQuotientChoiceMapping(state, representativeSignature, choiceSignatureToQuotientChoiceIndex, *quotientData.
toQuotientChoice);
520 signatureData.quotientChoiceGroupIndices.push_back(signatureData.toRepresentativeChoice.size());
521 signatureData.quotientChoiceGroupIndices.shrink_to_fit();
522 signatureData.toRepresentativeChoice.shrink_to_fit();
523 signatureData.quotientChoiceDistributions.shrink_to_fit();