35#pragma GCC diagnostic push
36#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
37#include <parallel_hashmap/phmap.h>
38#pragma GCC diagnostic pop
42namespace bisimulation {
44template<storm::dd::DdType DdType>
47template<storm::dd::DdType DdType>
78 this->ddman = this->internalDdManager->getCuddManager().getManager();
85 this->internalDdManager,
86 cudd::BDD(this->internalDdManager->getCuddManager(), this->getRepresentativesRec(this->partitionBdd.getInternalBdd().getCuddDdNode(),
87 this->rowVariablesCube.getInternalBdd().getCuddDdNode()))),
92 DdNodePtr getRepresentativesRec(DdNodePtr partitionNode, DdNodePtr stateVariablesCube) {
93 if (partitionNode == Cudd_ReadLogicZero(ddman)) {
98 if (visitedNodes.find(partitionNode) != visitedNodes.end()) {
99 return Cudd_ReadLogicZero(ddman);
103 if (Cudd_IsConstant(stateVariablesCube)) {
104 visitedNodes.emplace(partitionNode,
true);
105 return Cudd_ReadOne(ddman);
107 bool skipped =
false;
108 DdNodePtr elsePartitionNode;
109 DdNodePtr thenPartitionNode;
110 if (Cudd_NodeReadIndex(partitionNode) == Cudd_NodeReadIndex(stateVariablesCube)) {
111 elsePartitionNode = Cudd_E(partitionNode);
112 thenPartitionNode = Cudd_T(partitionNode);
114 if (Cudd_IsComplement(partitionNode)) {
115 elsePartitionNode = Cudd_Not(elsePartitionNode);
116 thenPartitionNode = Cudd_Not(thenPartitionNode);
119 elsePartitionNode = thenPartitionNode = partitionNode;
124 visitedNodes.emplace(partitionNode,
true);
128 DdNodePtr elseResult = getRepresentativesRec(elsePartitionNode, Cudd_T(stateVariablesCube));
129 Cudd_Ref(elseResult);
131 DdNodePtr thenResult =
nullptr;
133 thenResult = getRepresentativesRec(thenPartitionNode, Cudd_T(stateVariablesCube));
134 Cudd_Ref(thenResult);
136 if (thenResult == elseResult) {
137 Cudd_Deref(elseResult);
138 Cudd_Deref(thenResult);
141 bool complement = Cudd_IsComplement(thenResult);
142 auto result = cuddUniqueInter(ddman, Cudd_NodeReadIndex(stateVariablesCube), Cudd_Regular(thenResult),
143 complement ? Cudd_Not(elseResult) : elseResult);
144 Cudd_Deref(elseResult);
145 Cudd_Deref(thenResult);
146 return complement ? Cudd_Not(result) : result;
150 if (elseResult == Cudd_ReadLogicZero(ddman)) {
153 result = Cudd_Not(cuddUniqueInter(ddman, Cudd_NodeReadIndex(stateVariablesCube), Cudd_ReadOne(ddman), Cudd_Not(elseResult)));
155 Cudd_Deref(elseResult);
162 phmap::flat_hash_map<DdNode const*, bool> visitedNodes;
177 this->internalDdManager, sylvan::Bdd(this->getRepresentativesRec(this->partitionBdd.getInternalBdd().getSylvanBdd().GetBDD(),
178 this->rowVariablesCube.getInternalBdd().getSylvanBdd().GetBDD()))),
183 BDD getRepresentativesRec(BDD partitionNode, BDD stateVariablesCube) {
184 if (partitionNode == sylvan_false) {
189 if (visitedNodes.find(partitionNode) != visitedNodes.end()) {
194 if (sylvan_isconst(stateVariablesCube)) {
195 visitedNodes.emplace(partitionNode,
true);
198 bool skipped =
false;
199 BDD elsePartitionNode;
200 BDD thenPartitionNode;
202 elsePartitionNode = sylvan_low(partitionNode);
203 thenPartitionNode = sylvan_high(partitionNode);
205 elsePartitionNode = thenPartitionNode = partitionNode;
210 visitedNodes.emplace(partitionNode,
true);
214 BDD elseResult = getRepresentativesRec(elsePartitionNode, sylvan_high(stateVariablesCube));
215 mtbdd_refs_push(elseResult);
219 thenResult = getRepresentativesRec(thenPartitionNode, sylvan_high(stateVariablesCube));
220 mtbdd_refs_push(thenResult);
222 if (thenResult == elseResult) {
226 auto result = sylvan_makenode(sylvan_var(stateVariablesCube), elseResult, thenResult);
232 if (elseResult == sylvan_false) {
235 result = sylvan_makenode(sylvan_var(stateVariablesCube), elseResult, sylvan_false);
243 phmap::flat_hash_map<BDD, bool> visitedNodes;
246template<storm::dd::DdType DdType,
typename ValueType,
typename ExportValueType = ValueType>
249template<storm::dd::DdType DdType,
typename ValueType,
typename ExportValueType = ValueType>
310 std::vector<ExportValueType> reorderedValues(valueVector.size());
311 for (uint64_t pos = 0; pos < valueVector.size(); ++pos) {
314 return reorderedValues;
325 .toVector(this->
odd);
336 std::sort(row.begin(), row.end(),
345 [
this](uint64_t first, uint64_t second) { return this->rowToState[first] < this->rowToState[second]; });
348 uint64_t rowCounter = 0;
362 for (
auto const& entry : row) {
363 builder.
addNextValue(rowCounter, entry.getColumn(), entry.getValue());
378 return builder.
build();
381 void addMatrixEntry(uint64_t row, uint64_t column, ExportValueType
const& value) {
428 std::vector<std::vector<storm::storage::MatrixEntry<uint_fast64_t, ExportValueType>>>
matrixEntries;
437template<
typename ValueType>
444 ddman(this->manager.getInternalDdManager().getCuddManager().getManager()) {
445 this->createBlockToOffsetMapping();
450 this->createMatrixEntryStorage();
451 extractTransitionMatrixRec(matrix.
getInternalAdd().getCuddDdNode(), this->isNondeterministic ? this->nondeterminismOdd : this->odd, 0,
452 this->partitionBdd.getInternalBdd().getCuddDdNode(), this->representatives.getInternalBdd().getCuddDdNode(),
453 this->allSourceVariablesCube.getInternalBdd().getCuddDdNode(),
454 this->nondeterminismVariablesCube.getInternalBdd().getCuddDdNode(), this->isNondeterministic ? &this->odd : nullptr, 0);
455 return this->createMatrixFromEntries();
461 extractVectorRec(vector.
getInternalAdd().getCuddDdNode(), this->representatives.getInternalBdd().getCuddDdNode(),
466 void createBlockToOffsetMapping() {
467 this->createBlockToOffsetMappingRec(this->partitionBdd.getInternalBdd().getCuddDdNode(), this->representatives.getInternalBdd().getCuddDdNode(),
468 this->rowVariablesCube.getInternalBdd().getCuddDdNode(), this->odd, 0);
470 "Mismatching block-to-offset mapping: " << blockToOffset.size() <<
" vs. " << this->numberOfBlocks <<
".");
473 void createBlockToOffsetMappingRec(DdNodePtr partitionNode, DdNodePtr representativesNode, DdNodePtr variables,
storm::dd::Odd const& odd,
475 STORM_LOG_ASSERT(partitionNode != Cudd_ReadLogicZero(ddman) || representativesNode == Cudd_ReadLogicZero(ddman),
476 "Expected representative to be zero if the partition is zero.");
477 if (representativesNode == Cudd_ReadLogicZero(ddman) || partitionNode == Cudd_ReadLogicZero(ddman)) {
481 if (Cudd_IsConstant(variables)) {
483 STORM_LOG_ASSERT(blockToOffset.find(partitionNode) == blockToOffset.end(),
"Duplicate entry.");
484 blockToOffset[partitionNode] = offset;
487 DdNodePtr partitionT;
488 DdNodePtr partitionE;
489 if (Cudd_NodeReadIndex(partitionNode) == Cudd_NodeReadIndex(variables)) {
490 partitionT = Cudd_T(partitionNode);
491 partitionE = Cudd_E(partitionNode);
493 if (Cudd_IsComplement(partitionNode)) {
494 partitionE = Cudd_Not(partitionE);
495 partitionT = Cudd_Not(partitionT);
498 partitionT = partitionE = partitionNode;
501 DdNodePtr representativesT;
502 DdNodePtr representativesE;
503 if (Cudd_NodeReadIndex(representativesNode) == Cudd_NodeReadIndex(variables)) {
504 representativesT = Cudd_T(representativesNode);
505 representativesE = Cudd_E(representativesNode);
507 if (Cudd_IsComplement(representativesNode)) {
508 representativesE = Cudd_Not(representativesE);
509 representativesT = Cudd_Not(representativesT);
512 representativesT = representativesE = representativesNode;
515 createBlockToOffsetMappingRec(partitionE, representativesE, Cudd_T(variables), odd.
getElseSuccessor(), offset);
520 void extractVectorRec(DdNodePtr vector, DdNodePtr representativesNode, DdNodePtr variables,
storm::dd::Odd const& odd, uint64_t offset,
521 std::vector<ValueType>& result) {
522 if (representativesNode == Cudd_ReadLogicZero(ddman) || vector == Cudd_ReadZero(ddman)) {
526 if (Cudd_IsConstant(variables)) {
527 result[offset] = Cudd_V(vector);
531 if (Cudd_NodeReadIndex(vector) == Cudd_NodeReadIndex(variables)) {
532 vectorT = Cudd_T(vector);
533 vectorE = Cudd_E(vector);
535 vectorT = vectorE = vector;
538 DdNodePtr representativesT;
539 DdNodePtr representativesE;
540 if (Cudd_NodeReadIndex(representativesNode) == Cudd_NodeReadIndex(variables)) {
541 representativesT = Cudd_T(representativesNode);
542 representativesE = Cudd_E(representativesNode);
544 if (Cudd_IsComplement(representativesNode)) {
545 representativesT = Cudd_Not(representativesT);
546 representativesE = Cudd_Not(representativesE);
549 representativesT = representativesE = representativesNode;
552 extractVectorRec(vectorE, representativesE, Cudd_T(variables), odd.
getElseSuccessor(), offset, result);
557 void extractTransitionMatrixRec(DdNodePtr transitionMatrixNode,
storm::dd::Odd const& sourceOdd, uint64_t sourceOffset, DdNodePtr targetPartitionNode,
558 DdNodePtr representativesNode, DdNodePtr variables, DdNodePtr nondeterminismVariables,
storm::dd::Odd const* stateOdd,
559 uint64_t stateOffset) {
562 if (transitionMatrixNode == Cudd_ReadZero(ddman) || representativesNode == Cudd_ReadLogicZero(ddman)) {
567 if (Cudd_IsConstant(variables)) {
568 STORM_LOG_ASSERT(Cudd_IsConstant(transitionMatrixNode),
"Expected constant node.");
569 this->addMatrixEntry(sourceOffset, blockToOffset.at(targetPartitionNode), Cudd_V(transitionMatrixNode));
571 this->assignRowToState(sourceOffset, stateOffset);
575 bool nextVariableIsNondeterminismVariable =
576 !Cudd_IsConstant(nondeterminismVariables) && Cudd_NodeReadIndex(nondeterminismVariables) == Cudd_NodeReadIndex(variables);
578 if (nextVariableIsNondeterminismVariable) {
583 if (Cudd_NodeReadIndex(transitionMatrixNode) == Cudd_NodeReadIndex(variables)) {
584 t = Cudd_T(transitionMatrixNode);
585 e = Cudd_E(transitionMatrixNode);
587 t = e = transitionMatrixNode;
591 extractTransitionMatrixRec(e, sourceOdd.
getElseSuccessor(), sourceOffset, targetPartitionNode, representativesNode, Cudd_T(variables),
592 Cudd_T(nondeterminismVariables), stateOdd, stateOffset);
593 extractTransitionMatrixRec(t, sourceOdd.
getThenSuccessor(), sourceOffset + sourceOdd.
getElseOffset(), targetPartitionNode, representativesNode,
594 Cudd_T(variables), Cudd_T(nondeterminismVariables), stateOdd, stateOffset);
602 if (Cudd_NodeReadIndex(transitionMatrixNode) == Cudd_NodeReadIndex(variables)) {
604 t = Cudd_T(transitionMatrixNode);
605 e = Cudd_E(transitionMatrixNode);
607 t = e = transitionMatrixNode;
610 if (Cudd_NodeReadIndex(t) == Cudd_NodeReadIndex(variables) + 1) {
619 if (Cudd_NodeReadIndex(e) == Cudd_NodeReadIndex(variables) + 1) {
634 if (Cudd_NodeReadIndex(targetPartitionNode) == Cudd_NodeReadIndex(variables)) {
636 targetT = Cudd_T(targetPartitionNode);
637 targetE = Cudd_E(targetPartitionNode);
639 if (Cudd_IsComplement(targetPartitionNode)) {
640 targetT = Cudd_Not(targetT);
641 targetE = Cudd_Not(targetE);
645 targetT = targetE = targetPartitionNode;
648 DdNodePtr representativesT;
649 DdNodePtr representativesE;
650 if (Cudd_NodeReadIndex(representativesNode) == Cudd_NodeReadIndex(variables)) {
652 representativesT = Cudd_T(representativesNode);
653 representativesE = Cudd_E(representativesNode);
656 representativesT = representativesE = representativesNode;
659 if (representativesT != representativesE && Cudd_IsComplement(representativesNode)) {
660 representativesT = Cudd_Not(representativesT);
661 representativesE = Cudd_Not(representativesE);
664 extractTransitionMatrixRec(ee, sourceOdd.
getElseSuccessor(), sourceOffset, targetE, representativesE, Cudd_T(variables),
665 nondeterminismVariables, stateOdd ? &stateOdd->
getElseSuccessor() : stateOdd, stateOffset);
666 extractTransitionMatrixRec(et, sourceOdd.
getElseSuccessor(), sourceOffset, targetT, representativesE, Cudd_T(variables),
667 nondeterminismVariables, stateOdd ? &stateOdd->
getElseSuccessor() : stateOdd, stateOffset);
669 Cudd_T(variables), nondeterminismVariables, stateOdd ? &stateOdd->
getThenSuccessor() : stateOdd,
670 stateOffset + (stateOdd ? stateOdd->getElseOffset() : 0));
672 Cudd_T(variables), nondeterminismVariables, stateOdd ? &stateOdd->
getThenSuccessor() : stateOdd,
673 stateOffset + (stateOdd ? stateOdd->getElseOffset() : 0));
681 phmap::flat_hash_map<DdNode const*, uint64_t> blockToOffset;
684template<
typename ValueType,
typename ExportValueType>
693 this->createBlockToOffsetMapping();
698 this->createMatrixEntryStorage();
699 extractTransitionMatrixRec(matrix.
getInternalAdd().getSylvanMtbdd().GetMTBDD(), this->isNondeterministic ? this->nondeterminismOdd : this->odd, 0,
700 this->partitionBdd.getInternalBdd().getSylvanBdd().GetBDD(), this->representatives.getInternalBdd().getSylvanBdd().GetBDD(),
701 this->allSourceVariablesCube.getInternalBdd().getSylvanBdd().GetBDD(),
702 this->nondeterminismVariablesCube.getInternalBdd().getSylvanBdd().GetBDD(), this->isNondeterministic ? &this->odd : nullptr,
704 return this->createMatrixFromEntries();
711 extractVectorRec(vector.
getInternalAdd().getSylvanMtbdd().GetMTBDD(), this->representatives.getInternalBdd().getSylvanBdd().GetBDD(),
712 variablesCube.
getInternalBdd().getSylvanBdd().GetBDD(), odd, 0, result);
716 void extractVectorRec(MTBDD vector, BDD representativesNode, BDD variables,
storm::dd::Odd const& odd, uint64_t offset,
717 std::vector<ExportValueType>& result) {
718 if (representativesNode == sylvan_false || mtbdd_iszero(vector)) {
722 if (sylvan_isconst(variables)) {
728 vectorT = sylvan_high(vector);
729 vectorE = sylvan_low(vector);
731 vectorT = vectorE = vector;
734 BDD representativesT;
735 BDD representativesE;
737 representativesT = sylvan_high(representativesNode);
738 representativesE = sylvan_low(representativesNode);
740 representativesT = representativesE = representativesNode;
743 extractVectorRec(vectorE, representativesE, sylvan_high(variables), odd.
getElseSuccessor(), offset, result);
748 void createBlockToOffsetMapping() {
749 this->createBlockToOffsetMappingRec(this->partitionBdd.getInternalBdd().getSylvanBdd().GetBDD(),
750 this->representatives.getInternalBdd().getSylvanBdd().GetBDD(),
751 this->rowVariablesCube.getInternalBdd().getSylvanBdd().GetBDD(), this->odd, 0);
753 "Mismatching block-to-offset mapping: " << blockToOffset.size() <<
" vs. " << this->numberOfBlocks <<
".");
756 void createBlockToOffsetMappingRec(BDD partitionNode, BDD representativesNode, BDD variables,
storm::dd::Odd const& odd, uint64_t offset) {
757 STORM_LOG_ASSERT(partitionNode != sylvan_false || representativesNode == sylvan_false,
"Expected representative to be zero if the partition is zero.");
758 if (representativesNode == sylvan_false || partitionNode == sylvan_false) {
762 if (sylvan_isconst(variables)) {
764 STORM_LOG_ASSERT(blockToOffset.find(partitionNode) == blockToOffset.end(),
"Duplicate entry.");
765 blockToOffset[partitionNode] = offset;
771 partitionT = sylvan_high(partitionNode);
772 partitionE = sylvan_low(partitionNode);
774 partitionT = partitionE = partitionNode;
777 BDD representativesT;
778 BDD representativesE;
780 representativesT = sylvan_high(representativesNode);
781 representativesE = sylvan_low(representativesNode);
783 representativesT = representativesE = representativesNode;
786 createBlockToOffsetMappingRec(partitionE, representativesE, sylvan_high(variables), odd.
getElseSuccessor(), offset);
791 void extractTransitionMatrixRec(MTBDD transitionMatrixNode,
storm::dd::Odd const& sourceOdd, uint64_t sourceOffset, BDD targetPartitionNode,
792 BDD representativesNode, BDD variables, BDD nondeterminismVariables,
storm::dd::Odd const* stateOdd, uint64_t stateOffset) {
795 if (mtbdd_iszero(transitionMatrixNode) || representativesNode == sylvan_false) {
800 if (sylvan_isconst(variables)) {
801 STORM_LOG_ASSERT(mtbdd_isleaf(transitionMatrixNode),
"Expected constant node.");
802 this->addMatrixEntry(
803 sourceOffset, blockToOffset.at(targetPartitionNode),
806 this->assignRowToState(sourceOffset, stateOffset);
810 bool nextVariableIsNondeterminismVariable =
811 !sylvan_isconst(nondeterminismVariables) && sylvan_var(nondeterminismVariables) == sylvan_var(variables);
813 if (nextVariableIsNondeterminismVariable) {
819 t = sylvan_high(transitionMatrixNode);
820 e = sylvan_low(transitionMatrixNode);
822 t = e = transitionMatrixNode;
826 extractTransitionMatrixRec(e, sourceOdd.
getElseSuccessor(), sourceOffset, targetPartitionNode, representativesNode, sylvan_high(variables),
827 sylvan_high(nondeterminismVariables), stateOdd, stateOffset);
828 extractTransitionMatrixRec(t, sourceOdd.
getThenSuccessor(), sourceOffset + sourceOdd.
getElseOffset(), targetPartitionNode, representativesNode,
829 sylvan_high(variables), sylvan_high(nondeterminismVariables), stateOdd, stateOffset);
839 t = sylvan_high(transitionMatrixNode);
840 e = sylvan_low(transitionMatrixNode);
842 t = e = transitionMatrixNode;
871 targetT = sylvan_high(targetPartitionNode);
872 targetE = sylvan_low(targetPartitionNode);
875 targetT = targetE = targetPartitionNode;
878 BDD representativesT;
879 BDD representativesE;
882 representativesT = sylvan_high(representativesNode);
883 representativesE = sylvan_low(representativesNode);
886 representativesT = representativesE = representativesNode;
889 extractTransitionMatrixRec(ee, sourceOdd.
getElseSuccessor(), sourceOffset, targetE, representativesE, sylvan_high(variables),
890 nondeterminismVariables, stateOdd ? &stateOdd->
getElseSuccessor() : stateOdd, stateOffset);
891 extractTransitionMatrixRec(et, sourceOdd.
getElseSuccessor(), sourceOffset, targetT, representativesE, sylvan_high(variables),
892 nondeterminismVariables, stateOdd ? &stateOdd->
getElseSuccessor() : stateOdd, stateOffset);
894 sylvan_high(variables), nondeterminismVariables, stateOdd ? &stateOdd->
getThenSuccessor() : stateOdd,
895 stateOffset + (stateOdd ? stateOdd->getElseOffset() : 0));
897 sylvan_high(variables), nondeterminismVariables, stateOdd ? &stateOdd->
getThenSuccessor() : stateOdd,
898 stateOffset + (stateOdd ? stateOdd->getElseOffset() : 0));
904 phmap::flat_hash_map<BDD, uint64_t> blockToOffset;
907template<storm::dd::DdType DdType,
typename ValueType,
typename ExportValueType>
909 : useRepresentatives(false), quotientFormat(quotientFormat) {
910 auto const& settings = storm::settings::getModule<storm::settings::modules::BisimulationSettings>();
911 this->useRepresentatives = settings.isUseRepresentativesSet();
912 this->useOriginalVariables = settings.isUseOriginalVariablesSet();
915template<storm::dd::DdType DdType,
typename ValueType,
typename ExportValueType>
919 auto start = std::chrono::high_resolution_clock::now();
920 std::shared_ptr<storm::models::Model<ExportValueType>> result;
922 result = extractSparseQuotient(model, partition, preservationInformation);
924 result = extractDdQuotient(model, partition, preservationInformation);
926 auto end = std::chrono::high_resolution_clock::now();
927 STORM_LOG_INFO(
"Quotient extraction completed in " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() <<
"ms.");
929 STORM_LOG_THROW(result, storm::exceptions::NotSupportedException,
"Quotient could not be extracted.");
934template<storm::dd::DdType DdType,
typename ValueType,
typename ExportValueType>
943 auto start = std::chrono::high_resolution_clock::now();
947 "Representatives size does not match that of the partition: " << representatives.getNonZeroCount() <<
" vs. " << partition.
getNumberOfBlocks() <<
".");
949 "Representatives do not cover all blocks.");
953 auto end = std::chrono::high_resolution_clock::now();
954 STORM_LOG_INFO(
"Quotient transition matrix extracted in " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() <<
"ms.");
956 start = std::chrono::high_resolution_clock::now();
958 quotientStateLabeling.addLabel(
"init", sparseExtractor.extractSetExists(model.
getInitialStates()));
959 quotientStateLabeling.addLabel(
"deadlock", sparseExtractor.extractSetExists(model.
getDeadlockStates()));
961 for (
auto const& label : preservationInformation.getLabels()) {
962 quotientStateLabeling.addLabel(label, sparseExtractor.extractSetAll(model.
getStates(label)));
964 for (
auto const& expression : preservationInformation.getExpressions()) {
965 std::stringstream stream;
966 stream << expression;
967 std::string expressionAsString = stream.str();
969 if (quotientStateLabeling.containsLabel(expressionAsString)) {
970 STORM_LOG_WARN(
"Duplicate label '" << expressionAsString <<
"', dropping second label definition.");
972 quotientStateLabeling.addLabel(stream.str(), sparseExtractor.extractSetAll(model.
getStates(expression)));
975 end = std::chrono::high_resolution_clock::now();
976 STORM_LOG_INFO(
"Quotient labels extracted in " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).
count() <<
"ms.");
978 start = std::chrono::high_resolution_clock::now();
979 std::unordered_map<std::string, storm::models::sparse::StandardRewardModel<ExportValueType>> quotientRewardModels;
980 for (
auto const& rewardModelName : preservationInformation.getRewardModelNames()) {
983 std::optional<std::vector<ExportValueType>> quotientStateRewards;
984 if (rewardModel.hasStateRewards()) {
985 quotientStateRewards = sparseExtractor.extractStateVector(rewardModel.getStateRewardVector());
988 std::optional<std::vector<ExportValueType>> quotientStateActionRewards;
989 if (rewardModel.hasStateActionRewards()) {
990 quotientStateActionRewards = sparseExtractor.extractStateActionVector(rewardModel.getStateActionRewardVector());
994 std::move(quotientStateRewards), std::move(quotientStateActionRewards), std::nullopt));
996 end = std::chrono::high_resolution_clock::now();
997 STORM_LOG_INFO(
"Reward models extracted in " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).
count() <<
"ms.");
999 std::shared_ptr<storm::models::sparse::Model<ExportValueType>> result;
1001 result = std::make_shared<storm::models::sparse::Dtmc<ExportValueType>>(std::move(quotientTransitionMatrix), std::move(quotientStateLabeling),
1002 std::move(quotientRewardModels));
1004 result = std::make_shared<storm::models::sparse::Ctmc<ExportValueType>>(std::move(quotientTransitionMatrix), std::move(quotientStateLabeling),
1005 std::move(quotientRewardModels));
1007 result = std::make_shared<storm::models::sparse::Mdp<ExportValueType>>(std::move(quotientTransitionMatrix), std::move(quotientStateLabeling),
1008 std::move(quotientRewardModels));
1011 *model.template as<storm::models::symbolic::MarkovAutomaton<DdType, ValueType>>();
1013 boost::optional<storm::storage::BitVector> markovianStates = sparseExtractor.extractSetExists(markovAutomaton.
getMarkovianStates());
1015 std::move(quotientRewardModels),
false, std::move(markovianStates));
1016 modelComponents.exitRates = sparseExtractor.extractStateVector(markovAutomaton.
getExitRateVector());
1018 result = std::make_shared<storm::models::sparse::MarkovAutomaton<ExportValueType>>(std::move(modelComponents));
1024template<storm::dd::DdType DdType,
typename ValueType,
typename ExportValueType>
1025std::shared_ptr<storm::models::symbolic::Model<DdType, ExportValueType>> QuotientExtractor<DdType, ValueType, ExportValueType>::extractDdQuotient(
1027 PreservationInformation<DdType, ValueType>
const& preservationInformation) {
1028 if (this->useOriginalVariables) {
1029 return extractQuotientUsingOriginalVariables(model, partition, preservationInformation);
1031 return extractQuotientUsingBlockVariables(model, partition, preservationInformation);
1035template<storm::dd::DdType DdType,
typename ValueType,
typename ExportValueType>
1036std::shared_ptr<storm::models::symbolic::Model<DdType, ExportValueType>>
1037QuotientExtractor<DdType, ValueType, ExportValueType>::extractQuotientUsingBlockVariables(
1039 PreservationInformation<DdType, ValueType>
const& preservationInformation) {
1040 auto modelType = model.
getType();
1042 bool useRepresentativesForThisExtraction = this->useRepresentatives;
1048 "Mismatching partition.");
1050 std::set<storm::expressions::Variable> blockVariableSet = {partition.getBlockVariable()};
1051 std::set<storm::expressions::Variable> blockPrimeVariableSet = {partition.getPrimedBlockVariable()};
1052 std::vector<std::pair<storm::expressions::Variable, storm::expressions::Variable>> blockMetaVariablePairs = {
1053 std::make_pair(partition.getBlockVariable(), partition.getPrimedBlockVariable())};
1055 auto start = std::chrono::high_resolution_clock::now();
1058 storm::dd::Bdd<DdType> partitionAsBdd = partition.storedAsBdd() ? partition.asBdd() : partition.asAdd().notZero();
1060 auto representatives = InternalRepresentativeComputer<DdType>(partitionAsBdd, model.
getRowVariables()).getRepresentatives();
1062 if (useRepresentativesForThisExtraction) {
1065 (representatives && partitionAsBddOverPrimedBlockVariables).renameVariablesConcretize(model.
getRowVariables(), blockVariableSet);
1066 partitionAsBdd = (tmp && partitionAsBddOverPrimedBlockVariables).existsAbstract(blockPrimeVariableSet);
1072 std::map<std::string, storm::dd::Bdd<DdType>> preservedLabelBdds;
1073 for (
auto const& label : preservationInformation.getLabels()) {
1074 preservedLabelBdds.emplace(label, (model.
getStates(label) && partitionAsBdd).existsAbstract(model.
getRowVariables()));
1076 for (
auto const& expression : preservationInformation.getExpressions()) {
1077 std::stringstream stream;
1078 stream << expression;
1079 std::string expressionAsString = stream.str();
1081 auto it = preservedLabelBdds.find(expressionAsString);
1082 if (it != preservedLabelBdds.end()) {
1083 STORM_LOG_WARN(
"Duplicate label '" << expressionAsString <<
"', dropping second label definition.");
1085 preservedLabelBdds.emplace(stream.str(), (model.
getStates(expression) && partitionAsBdd).existsAbstract(model.
getRowVariables()));
1088 auto end = std::chrono::high_resolution_clock::now();
1089 STORM_LOG_INFO(
"Quotient labels extracted in " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).
count() <<
"ms.");
1091 start = std::chrono::high_resolution_clock::now();
1092 std::set<storm::expressions::Variable> blockAndRowVariables;
1094 std::inserter(blockAndRowVariables, blockAndRowVariables.end()));
1095 std::set<storm::expressions::Variable> blockPrimeAndColumnVariables;
1097 std::inserter(blockPrimeAndColumnVariables, blockPrimeAndColumnVariables.end()));
1103 partitionAsBdd &= representatives;
1104 partitionAsAdd *= partitionAsBdd.template toAdd<ValueType>();
1107 end = std::chrono::high_resolution_clock::now();
1110 if (std::is_same<ValueType, storm::RationalNumber>::value) {
1111 STORM_LOG_ASSERT(quotientTransitionMatrix.
greater(storm::utility::one<ValueType>()).isZero(),
"Illegal entries in quotient matrix.");
1112 }
else if (std::is_same<ValueType, storm::RationalFunction>::value) {
1115 STORM_LOG_ASSERT(quotientTransitionMatrix.
greater(storm::utility::one<ValueType>() + storm::utility::convertNumber<ValueType>(1e-6)).isZero(),
1116 "Illegal entries in quotient matrix.");
1119 .equalModuloPrecision(quotientTransitionMatrix.
notZero().existsAbstract(blockPrimeVariableSet).template toAdd<ValueType>(),
1120 storm::utility::convertNumber<ValueType>(1e-6)),
1121 "Illegal non-probabilistic matrix.");
1123 STORM_LOG_INFO(
"Quotient transition matrix extracted in " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).
count() <<
"ms.");
1128 blockPrimeAndNondeterminismVariables.insert(blockPrimeVariableSet.begin(), blockPrimeVariableSet.end());
1131 start = std::chrono::high_resolution_clock::now();
1132 std::unordered_map<std::string, storm::models::symbolic::StandardRewardModel<DdType, ValueType>> quotientRewardModels;
1133 for (
auto const& rewardModelName : preservationInformation.getRewardModelNames()) {
1136 boost::optional<storm::dd::Add<DdType, ValueType>> quotientStateRewards;
1137 if (rewardModel.hasStateRewards()) {
1141 boost::optional<storm::dd::Add<DdType, ValueType>> quotientStateActionRewards;
1142 if (rewardModel.hasStateActionRewards()) {
1143 quotientStateActionRewards = rewardModel.getStateActionRewardVector().multiplyMatrix(partitionAsAdd, model.
getRowVariables());
1147 quotientStateRewards, quotientStateActionRewards, boost::none));
1149 end = std::chrono::high_resolution_clock::now();
1150 STORM_LOG_INFO(
"Reward models extracted in " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).
count() <<
"ms.");
1152 std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> result;
1155 model.
getManager().asSharedPointer(), reachableStates, initialStates, deadlockStates, quotientTransitionMatrix, blockVariableSet,
1156 blockPrimeVariableSet, blockMetaVariablePairs, preservedLabelBdds, quotientRewardModels));
1159 model.
getManager().asSharedPointer(), reachableStates, initialStates, deadlockStates, quotientTransitionMatrix, blockVariableSet,
1160 blockPrimeVariableSet, blockMetaVariablePairs, preservedLabelBdds, quotientRewardModels));
1163 model.
getManager().asSharedPointer(), reachableStates, initialStates, deadlockStates, quotientTransitionMatrix, blockVariableSet,
1164 blockPrimeVariableSet, blockMetaVariablePairs, model.
getNondeterminismVariables(), preservedLabelBdds, quotientRewardModels));
1170 deadlockStates, quotientTransitionMatrix, blockVariableSet, blockPrimeVariableSet, blockMetaVariablePairs,
1174 return result->template toValueType<ExportValueType>();
1176 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"Cannot extract quotient for this model type.");
1180template<storm::dd::DdType DdType,
typename ValueType,
typename ExportValueType>
1181std::shared_ptr<storm::models::symbolic::Model<DdType, ExportValueType>>
1182QuotientExtractor<DdType, ValueType, ExportValueType>::extractQuotientUsingOriginalVariables(
1184 PreservationInformation<DdType, ValueType>
const& preservationInformation) {
1185 auto modelType = model.
getType();
1187 bool useRepresentativesForThisExtraction = this->useRepresentatives;
1190 STORM_LOG_WARN_COND(!this->useRepresentatives,
"Using representatives is unsupported for this extraction, falling back to regular extraction.");
1195 "Mismatching partition.");
1197 std::set<storm::expressions::Variable> blockVariableSet = {partition.getBlockVariable()};
1198 std::set<storm::expressions::Variable> blockPrimeVariableSet = {partition.getPrimedBlockVariable()};
1199 std::vector<std::pair<storm::expressions::Variable, storm::expressions::Variable>> blockMetaVariablePairs = {
1200 std::make_pair(partition.getBlockVariable(), partition.getPrimedBlockVariable())};
1202 auto start = std::chrono::high_resolution_clock::now();
1205 storm::dd::Bdd<DdType> partitionAsBdd = partition.storedAsBdd() ? partition.asBdd() : partition.asAdd().notZero();
1207 auto representatives = InternalRepresentativeComputer<DdType>(partitionAsBdd, model.
getRowVariables()).getRepresentatives();
1209 if (useRepresentativesForThisExtraction) {
1212 (representatives && partitionAsBddOverPrimedBlockVariables).renameVariablesConcretize(model.
getRowVariables(), blockVariableSet);
1213 partitionAsBdd = (tmp && partitionAsBddOverPrimedBlockVariables).existsAbstract(blockPrimeVariableSet);
1222 std::map<std::string, storm::dd::Bdd<DdType>> preservedLabelBdds;
1223 for (
auto const& label : preservationInformation.getLabels()) {
1224 preservedLabelBdds.emplace(label, (model.
getStates(label) && partitionAsBdd)
1228 for (
auto const& expression : preservationInformation.getExpressions()) {
1229 std::stringstream stream;
1230 stream << expression;
1231 std::string expressionAsString = stream.str();
1233 auto it = preservedLabelBdds.find(expressionAsString);
1234 if (it != preservedLabelBdds.end()) {
1235 STORM_LOG_WARN(
"Duplicate label '" << expressionAsString <<
"', dropping second label definition.");
1237 preservedLabelBdds.emplace(stream.str(), (model.
getStates(expression) && partitionAsBdd)
1242 auto end = std::chrono::high_resolution_clock::now();
1243 STORM_LOG_INFO(
"Quotient labels extracted in " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).
count() <<
"ms.");
1245 start = std::chrono::high_resolution_clock::now();
1246 std::set<storm::expressions::Variable> blockAndRowVariables;
1248 std::inserter(blockAndRowVariables, blockAndRowVariables.end()));
1249 std::set<storm::expressions::Variable> blockPrimeAndColumnVariables;
1251 std::inserter(blockPrimeAndColumnVariables, blockPrimeAndColumnVariables.end()));
1259 partitionAsBdd &= representatives;
1260 partitionAsAdd = partitionAsBdd.template toAdd<ValueType>();
1264 quotientTransitionMatrix = (quotientTransitionMatrix * partitionAsAdd)
1271 end = std::chrono::high_resolution_clock::now();
1274 if (std::is_same<ValueType, storm::RationalNumber>::value) {
1275 STORM_LOG_ASSERT(quotientTransitionMatrix.
greater(storm::utility::one<ValueType>()).isZero(),
"Illegal entries in quotient matrix.");
1277 STORM_LOG_ASSERT(quotientTransitionMatrix.
greater(storm::utility::one<ValueType>() + storm::utility::convertNumber<ValueType>(1e-6)).isZero(),
1278 "Illegal entries in quotient matrix.");
1281 .equalModuloPrecision(quotientTransitionMatrix.
notZero().existsAbstract(model.
getColumnVariables()).template toAdd<ValueType>(),
1282 storm::utility::convertNumber<ValueType>(1e-6)),
1283 "Illegal probabilistic matrix.");
1285 STORM_LOG_INFO(
"Quotient transition matrix extracted in " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).
count() <<
"ms.");
1289 std::set<storm::expressions::Variable> columnAndNondeterminismVariables = model.
getColumnVariables();
1293 start = std::chrono::high_resolution_clock::now();
1294 std::unordered_map<std::string, storm::models::symbolic::StandardRewardModel<DdType, ValueType>> quotientRewardModels;
1295 for (
auto const& rewardModelName : preservationInformation.getRewardModelNames()) {
1298 boost::optional<storm::dd::Add<DdType, ValueType>> quotientStateRewards;
1299 if (rewardModel.hasStateRewards()) {
1305 boost::optional<storm::dd::Add<DdType, ValueType>> quotientStateActionRewards;
1306 if (rewardModel.hasStateActionRewards()) {
1307 quotientStateActionRewards = rewardModel.getStateActionRewardVector()
1313 quotientStateRewards, quotientStateActionRewards, boost::none));
1315 end = std::chrono::high_resolution_clock::now();
1316 STORM_LOG_INFO(
"Reward models extracted in " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).
count() <<
"ms.");
1318 std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> result;
1321 model.
getManager().asSharedPointer(), reachableStates, initialStates, deadlockStates, quotientTransitionMatrix, model.
getRowVariables(),
1325 model.
getManager().asSharedPointer(), reachableStates, initialStates, deadlockStates, quotientTransitionMatrix, model.
getRowVariables(),
1329 model.
getManager().asSharedPointer(), reachableStates, initialStates, deadlockStates, quotientTransitionMatrix, model.
getRowVariables(),
1331 quotientRewardModels));
1341 return result->template toValueType<ExportValueType>();
1343 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"Cannot extract quotient for this model type.");
1347template class QuotientExtractor<storm::dd::DdType::CUDD, double>;
1349template class QuotientExtractor<storm::dd::DdType::Sylvan, double>;
1350template class QuotientExtractor<storm::dd::DdType::Sylvan, storm::RationalNumber>;
1351template class QuotientExtractor<storm::dd::DdType::Sylvan, storm::RationalNumber, double>;
1352template class QuotientExtractor<storm::dd::DdType::Sylvan, storm::RationalFunction>;
InternalAdd< LibraryType, ValueType > const & getInternalAdd() const
Retrieves the internal ADD.
Bdd< LibraryType > greater(Add< LibraryType, ValueType > const &other) const
Retrieves the function that maps all evaluations to one whose function value in the first ADD are gre...
Add< LibraryType, ValueType > renameVariables(std::set< storm::expressions::Variable > const &from, std::set< storm::expressions::Variable > const &to) const
Renames the given meta variables in the ADD.
Add< LibraryType, ValueType > sumAbstract(std::set< storm::expressions::Variable > const &metaVariables) const
Sum-abstracts from the given meta variables.
Add< LibraryType, ValueType > multiplyMatrix(Add< LibraryType, ValueType > const &otherMatrix, std::set< storm::expressions::Variable > const &summationMetaVariables) const
Multiplies the current ADD (representing a matrix) with the given matrix by summing over the given me...
Bdd< LibraryType > toBdd() const
Converts the ADD to a BDD by mapping all values unequal to zero to 1.
Bdd< LibraryType > notZero() const
Computes a BDD that represents the function in which all assignments with a function value unequal to...
Bdd< LibraryType > existsAbstract(std::set< storm::expressions::Variable > const &metaVariables) const
Existentially abstracts from the given meta variables.
Bdd< LibraryType > swapVariables(std::vector< std::pair< storm::expressions::Variable, storm::expressions::Variable > > const &metaVariablePairs) const
Swaps the given pairs of meta variables in the BDD.
Odd createOdd() const
Creates an ODD based on the current BDD.
static Bdd< LibraryType > getCube(DdManager< LibraryType > const &manager, std::set< storm::expressions::Variable > const &metaVariables)
Retrieves the cube of all given meta variables.
Bdd< LibraryType > renameVariables(std::set< storm::expressions::Variable > const &from, std::set< storm::expressions::Variable > const &to) const
Renames the given meta variables in the BDD.
bool isOne() const
Retrieves whether this DD represents the constant one function.
InternalBdd< LibraryType > const & getInternalBdd() const
Retrieves the internal BDD.
DdManager< LibraryType > & getDdManager() const
Retrieves the manager that is responsible for this DD.
storm::expressions::Variable getMetaVariable(std::string const &variableName) const
Retrieves the given meta variable by name.
InternalDdManager< LibraryType > & getInternalDdManager()
Retrieves the internal DD manager.
Bdd< LibraryType > getBddOne() const
Retrieves a BDD representing the constant one function.
Odd const & getThenSuccessor() const
Retrieves the then-successor of this ODD node.
uint_fast64_t getTotalOffset() const
Retrieves the total offset, i.e., the sum of the then- and else-offset.
uint_fast64_t getElseOffset() const
Retrieves the else-offset of this ODD node.
bool isTerminalNode() const
Checks whether the given ODD node is a terminal node, i.e.
Odd const & getElseSuccessor() const
Retrieves the else-successor of this ODD node.
InternalRepresentativeComputer(storm::dd::Bdd< storm::dd::DdType::CUDD > const &partitionBdd, std::set< storm::expressions::Variable > const &rowVariables)
storm::dd::Bdd< storm::dd::DdType::CUDD > getRepresentatives()
storm::dd::Bdd< storm::dd::DdType::Sylvan > getRepresentatives()
InternalRepresentativeComputer(storm::dd::Bdd< storm::dd::DdType::Sylvan > const &partitionBdd, std::set< storm::expressions::Variable > const &rowVariables)
std::set< storm::expressions::Variable > const & rowVariables
storm::dd::Bdd< DdType > partitionBdd
storm::dd::InternalDdManager< DdType > const * internalDdManager
storm::dd::Bdd< DdType > rowVariablesCube
storm::dd::DdManager< DdType > const * ddManager
InternalRepresentativeComputerBase(storm::dd::Bdd< DdType > const &partitionBdd, std::set< storm::expressions::Variable > const &rowVariables)
storm::expressions::Variable const & getBlockVariable() const
storm::dd::Bdd< DdType > const & asBdd() const
uint64_t getNumberOfBlocks() const
storm::dd::Bdd< DdType > getStates() const
storm::dd::Add< DdType, ValueType > const & asAdd() const
virtual ModelType getType() const
Return the actual type of the model.
This class manages the labeling of the state space with a number of (atomic) labels.
This class represents a continuous-time Markov chain.
This class represents a discrete-time Markov chain.
This class represents a discrete-time Markov decision process.
storm::dd::Bdd< Type > const & getMarkovianStates() const
storm::dd::Bdd< Type > const & getMarkovianMarker() const
storm::dd::Add< Type, ValueType > const & getExitRateVector() const
This class represents a discrete-time Markov decision process.
Base class for all symbolic models.
storm::dd::DdManager< Type > & getManager() const
Retrieves the manager responsible for the DDs that represent this model.
RewardModelType const & getRewardModel(std::string const &rewardModelName) const
Retrieves the reward model with the given name, if one exists.
storm::dd::Add< Type, ValueType > const & getTransitionMatrix() const
Retrieves the matrix representing the transitions of the model.
storm::dd::Bdd< Type > const & getDeadlockStates() const
virtual storm::dd::Bdd< Type > getQualitativeTransitionMatrix(bool keepNondeterminism=true) const
Retrieves the matrix qualitatively (i.e.
std::set< storm::expressions::Variable > const & getColumnVariables() const
Retrieves the meta variables used to encode the columns of the transition matrix and the vector indic...
storm::dd::Bdd< Type > const & getInitialStates() const
Retrieves the initial states of the model.
virtual std::set< storm::expressions::Variable > const & getNondeterminismVariables() const
Retrieves all meta variables used to encode the nondeterminism.
std::vector< std::pair< storm::expressions::Variable, storm::expressions::Variable > > const & getRowColumnMetaVariablePairs() const
Retrieves the pairs of row and column meta variables.
std::set< storm::expressions::Variable > const & getRowVariables() const
Retrieves the meta variables used to encode the rows of the transition matrix and the vector indices.
virtual storm::dd::Bdd< Type > getStates(std::string const &label) const
Returns the sets of states labeled with the given label.
storm::dd::Bdd< Type > const & getReachableStates() const
Retrieves the reachable states of the model.
virtual uint_fast64_t getNumberOfStates() const override
Returns the number of states of the model.
storm::dd::Add< Type, ValueType > const & getStateRewardVector() const
Retrieves the state rewards of the reward model.
A bit vector that is internally represented as a vector of 64-bit values.
A class that can be used to build a sparse matrix by adding value by value.
void addNextValue(index_type row, index_type column, value_type const &value)
Sets the matrix entry at the given row and column to the given value.
void newRowGroup(index_type startingRow)
Starts a new row group in the matrix.
SparseMatrix< value_type > build(index_type overriddenRowCount=0, index_type overriddenColumnCount=0, index_type overriddenRowGroupCount=0)
A class that holds a possibly non-square matrix in the compressed row storage format.
#define STORM_LOG_INFO(message)
#define STORM_LOG_WARN(message)
#define STORM_LOG_TRACE(message)
#define STORM_LOG_ASSERT(cond, message)
#define STORM_LOG_WARN_COND(cond, message)
#define STORM_LOG_THROW(cond, exception, message)
bool sylvan_bdd_matches_variable_index(BDD node, uint64_t variableIndex, int64_t offset)
Retrieves whether the topmost variable in the BDD is the one with the given index.
bool sylvan_mtbdd_matches_variable_index(MTBDD node, uint64_t variableIndex, int64_t offset)
Retrieves whether the topmost variable in the MTBDD is the one with the given index.
std::pair< storm::RationalNumber, storm::RationalNumber > count(std::vector< storm::storage::BitVector > const &origSets, std::vector< storm::storage::BitVector > const &intersects, std::vector< storm::storage::BitVector > const &intersectsInfo, storm::RationalNumber val, bool plus, uint64_t remdepth)