35#include <parallel_hashmap/phmap.h>
39namespace bisimulation {
41template<storm::dd::DdType DdType>
44template<storm::dd::DdType DdType>
75 this->ddman = this->internalDdManager->getCuddManager().getManager();
82 this->internalDdManager,
83 cudd::BDD(this->internalDdManager->getCuddManager(), this->getRepresentativesRec(this->partitionBdd.getInternalBdd().getCuddDdNode(),
84 this->rowVariablesCube.getInternalBdd().getCuddDdNode()))),
89 DdNodePtr getRepresentativesRec(DdNodePtr partitionNode, DdNodePtr stateVariablesCube) {
90 if (partitionNode == Cudd_ReadLogicZero(ddman)) {
95 if (visitedNodes.find(partitionNode) != visitedNodes.end()) {
96 return Cudd_ReadLogicZero(ddman);
100 if (Cudd_IsConstant(stateVariablesCube)) {
101 visitedNodes.emplace(partitionNode,
true);
102 return Cudd_ReadOne(ddman);
104 bool skipped =
false;
105 DdNodePtr elsePartitionNode;
106 DdNodePtr thenPartitionNode;
107 if (Cudd_NodeReadIndex(partitionNode) == Cudd_NodeReadIndex(stateVariablesCube)) {
108 elsePartitionNode = Cudd_E(partitionNode);
109 thenPartitionNode = Cudd_T(partitionNode);
111 if (Cudd_IsComplement(partitionNode)) {
112 elsePartitionNode = Cudd_Not(elsePartitionNode);
113 thenPartitionNode = Cudd_Not(thenPartitionNode);
116 elsePartitionNode = thenPartitionNode = partitionNode;
121 visitedNodes.emplace(partitionNode,
true);
125 DdNodePtr elseResult = getRepresentativesRec(elsePartitionNode, Cudd_T(stateVariablesCube));
126 Cudd_Ref(elseResult);
128 DdNodePtr thenResult =
nullptr;
130 thenResult = getRepresentativesRec(thenPartitionNode, Cudd_T(stateVariablesCube));
131 Cudd_Ref(thenResult);
133 if (thenResult == elseResult) {
134 Cudd_Deref(elseResult);
135 Cudd_Deref(thenResult);
138 bool complement = Cudd_IsComplement(thenResult);
139 auto result = cuddUniqueInter(ddman, Cudd_NodeReadIndex(stateVariablesCube), Cudd_Regular(thenResult),
140 complement ? Cudd_Not(elseResult) : elseResult);
141 Cudd_Deref(elseResult);
142 Cudd_Deref(thenResult);
143 return complement ? Cudd_Not(result) : result;
147 if (elseResult == Cudd_ReadLogicZero(ddman)) {
150 result = Cudd_Not(cuddUniqueInter(ddman, Cudd_NodeReadIndex(stateVariablesCube), Cudd_ReadOne(ddman), Cudd_Not(elseResult)));
152 Cudd_Deref(elseResult);
159 phmap::flat_hash_map<DdNode const*, bool> visitedNodes;
174 this->internalDdManager, sylvan::Bdd(this->getRepresentativesRec(this->partitionBdd.getInternalBdd().getSylvanBdd().GetBDD(),
175 this->rowVariablesCube.getInternalBdd().getSylvanBdd().GetBDD()))),
180 BDD getRepresentativesRec(BDD partitionNode, BDD stateVariablesCube) {
181 if (partitionNode == sylvan_false) {
186 if (visitedNodes.find(partitionNode) != visitedNodes.end()) {
191 if (sylvan_isconst(stateVariablesCube)) {
192 visitedNodes.emplace(partitionNode,
true);
195 bool skipped =
false;
196 BDD elsePartitionNode;
197 BDD thenPartitionNode;
199 elsePartitionNode = sylvan_low(partitionNode);
200 thenPartitionNode = sylvan_high(partitionNode);
202 elsePartitionNode = thenPartitionNode = partitionNode;
207 visitedNodes.emplace(partitionNode,
true);
211 BDD elseResult = getRepresentativesRec(elsePartitionNode, sylvan_high(stateVariablesCube));
212 mtbdd_refs_push(elseResult);
216 thenResult = getRepresentativesRec(thenPartitionNode, sylvan_high(stateVariablesCube));
217 mtbdd_refs_push(thenResult);
219 if (thenResult == elseResult) {
223 auto result = sylvan_makenode(sylvan_var(stateVariablesCube), elseResult, thenResult);
229 if (elseResult == sylvan_false) {
232 result = sylvan_makenode(sylvan_var(stateVariablesCube), elseResult, sylvan_false);
240 phmap::flat_hash_map<BDD, bool> visitedNodes;
243template<storm::dd::DdType DdType,
typename ValueType,
typename ExportValueType = ValueType>
246template<storm::dd::DdType DdType,
typename ValueType,
typename ExportValueType = ValueType>
307 std::vector<ExportValueType> reorderedValues(valueVector.size());
308 for (uint64_t pos = 0; pos < valueVector.size(); ++pos) {
311 return reorderedValues;
322 .toVector(this->
odd);
333 std::sort(row.begin(), row.end(),
342 [
this](uint64_t first, uint64_t second) { return this->rowToState[first] < this->rowToState[second]; });
345 uint64_t rowCounter = 0;
359 for (
auto const& entry : row) {
360 builder.
addNextValue(rowCounter, entry.getColumn(), entry.getValue());
375 return builder.
build();
378 void addMatrixEntry(uint64_t row, uint64_t column, ExportValueType
const& value) {
425 std::vector<std::vector<storm::storage::MatrixEntry<uint_fast64_t, ExportValueType>>>
matrixEntries;
434template<
typename ValueType>
441 ddman(this->manager.getInternalDdManager().getCuddManager().getManager()) {
442 this->createBlockToOffsetMapping();
447 this->createMatrixEntryStorage();
448 extractTransitionMatrixRec(matrix.
getInternalAdd().getCuddDdNode(), this->isNondeterministic ? this->nondeterminismOdd : this->odd, 0,
449 this->partitionBdd.getInternalBdd().getCuddDdNode(), this->representatives.getInternalBdd().getCuddDdNode(),
450 this->allSourceVariablesCube.getInternalBdd().getCuddDdNode(),
451 this->nondeterminismVariablesCube.getInternalBdd().getCuddDdNode(), this->isNondeterministic ? &this->odd : nullptr, 0);
452 return this->createMatrixFromEntries();
458 extractVectorRec(vector.
getInternalAdd().getCuddDdNode(), this->representatives.getInternalBdd().getCuddDdNode(),
463 void createBlockToOffsetMapping() {
464 this->createBlockToOffsetMappingRec(this->partitionBdd.getInternalBdd().getCuddDdNode(), this->representatives.getInternalBdd().getCuddDdNode(),
465 this->rowVariablesCube.getInternalBdd().getCuddDdNode(), this->odd, 0);
467 "Mismatching block-to-offset mapping: " << blockToOffset.size() <<
" vs. " << this->numberOfBlocks <<
".");
470 void createBlockToOffsetMappingRec(DdNodePtr partitionNode, DdNodePtr representativesNode, DdNodePtr variables,
storm::dd::Odd const& odd,
472 STORM_LOG_ASSERT(partitionNode != Cudd_ReadLogicZero(ddman) || representativesNode == Cudd_ReadLogicZero(ddman),
473 "Expected representative to be zero if the partition is zero.");
474 if (representativesNode == Cudd_ReadLogicZero(ddman) || partitionNode == Cudd_ReadLogicZero(ddman)) {
478 if (Cudd_IsConstant(variables)) {
480 STORM_LOG_ASSERT(blockToOffset.find(partitionNode) == blockToOffset.end(),
"Duplicate entry.");
481 blockToOffset[partitionNode] = offset;
484 DdNodePtr partitionT;
485 DdNodePtr partitionE;
486 if (Cudd_NodeReadIndex(partitionNode) == Cudd_NodeReadIndex(variables)) {
487 partitionT = Cudd_T(partitionNode);
488 partitionE = Cudd_E(partitionNode);
490 if (Cudd_IsComplement(partitionNode)) {
491 partitionE = Cudd_Not(partitionE);
492 partitionT = Cudd_Not(partitionT);
495 partitionT = partitionE = partitionNode;
498 DdNodePtr representativesT;
499 DdNodePtr representativesE;
500 if (Cudd_NodeReadIndex(representativesNode) == Cudd_NodeReadIndex(variables)) {
501 representativesT = Cudd_T(representativesNode);
502 representativesE = Cudd_E(representativesNode);
504 if (Cudd_IsComplement(representativesNode)) {
505 representativesE = Cudd_Not(representativesE);
506 representativesT = Cudd_Not(representativesT);
509 representativesT = representativesE = representativesNode;
512 createBlockToOffsetMappingRec(partitionE, representativesE, Cudd_T(variables), odd.
getElseSuccessor(), offset);
517 void extractVectorRec(DdNodePtr vector, DdNodePtr representativesNode, DdNodePtr variables,
storm::dd::Odd const& odd, uint64_t offset,
518 std::vector<ValueType>& result) {
519 if (representativesNode == Cudd_ReadLogicZero(ddman) || vector == Cudd_ReadZero(ddman)) {
523 if (Cudd_IsConstant(variables)) {
524 result[offset] = Cudd_V(vector);
528 if (Cudd_NodeReadIndex(vector) == Cudd_NodeReadIndex(variables)) {
529 vectorT = Cudd_T(vector);
530 vectorE = Cudd_E(vector);
532 vectorT = vectorE = vector;
535 DdNodePtr representativesT;
536 DdNodePtr representativesE;
537 if (Cudd_NodeReadIndex(representativesNode) == Cudd_NodeReadIndex(variables)) {
538 representativesT = Cudd_T(representativesNode);
539 representativesE = Cudd_E(representativesNode);
541 if (Cudd_IsComplement(representativesNode)) {
542 representativesT = Cudd_Not(representativesT);
543 representativesE = Cudd_Not(representativesE);
546 representativesT = representativesE = representativesNode;
549 extractVectorRec(vectorE, representativesE, Cudd_T(variables), odd.
getElseSuccessor(), offset, result);
554 void extractTransitionMatrixRec(DdNodePtr transitionMatrixNode,
storm::dd::Odd const& sourceOdd, uint64_t sourceOffset, DdNodePtr targetPartitionNode,
555 DdNodePtr representativesNode, DdNodePtr variables, DdNodePtr nondeterminismVariables,
storm::dd::Odd const* stateOdd,
556 uint64_t stateOffset) {
559 if (transitionMatrixNode == Cudd_ReadZero(ddman) || representativesNode == Cudd_ReadLogicZero(ddman)) {
564 if (Cudd_IsConstant(variables)) {
565 STORM_LOG_ASSERT(Cudd_IsConstant(transitionMatrixNode),
"Expected constant node.");
566 this->addMatrixEntry(sourceOffset, blockToOffset.at(targetPartitionNode), Cudd_V(transitionMatrixNode));
568 this->assignRowToState(sourceOffset, stateOffset);
572 bool nextVariableIsNondeterminismVariable =
573 !Cudd_IsConstant(nondeterminismVariables) && Cudd_NodeReadIndex(nondeterminismVariables) == Cudd_NodeReadIndex(variables);
575 if (nextVariableIsNondeterminismVariable) {
580 if (Cudd_NodeReadIndex(transitionMatrixNode) == Cudd_NodeReadIndex(variables)) {
581 t = Cudd_T(transitionMatrixNode);
582 e = Cudd_E(transitionMatrixNode);
584 t = e = transitionMatrixNode;
588 extractTransitionMatrixRec(e, sourceOdd.
getElseSuccessor(), sourceOffset, targetPartitionNode, representativesNode, Cudd_T(variables),
589 Cudd_T(nondeterminismVariables), stateOdd, stateOffset);
590 extractTransitionMatrixRec(t, sourceOdd.
getThenSuccessor(), sourceOffset + sourceOdd.
getElseOffset(), targetPartitionNode, representativesNode,
591 Cudd_T(variables), Cudd_T(nondeterminismVariables), stateOdd, stateOffset);
599 if (Cudd_NodeReadIndex(transitionMatrixNode) == Cudd_NodeReadIndex(variables)) {
601 t = Cudd_T(transitionMatrixNode);
602 e = Cudd_E(transitionMatrixNode);
604 t = e = transitionMatrixNode;
607 if (Cudd_NodeReadIndex(t) == Cudd_NodeReadIndex(variables) + 1) {
616 if (Cudd_NodeReadIndex(e) == Cudd_NodeReadIndex(variables) + 1) {
631 if (Cudd_NodeReadIndex(targetPartitionNode) == Cudd_NodeReadIndex(variables)) {
633 targetT = Cudd_T(targetPartitionNode);
634 targetE = Cudd_E(targetPartitionNode);
636 if (Cudd_IsComplement(targetPartitionNode)) {
637 targetT = Cudd_Not(targetT);
638 targetE = Cudd_Not(targetE);
642 targetT = targetE = targetPartitionNode;
645 DdNodePtr representativesT;
646 DdNodePtr representativesE;
647 if (Cudd_NodeReadIndex(representativesNode) == Cudd_NodeReadIndex(variables)) {
649 representativesT = Cudd_T(representativesNode);
650 representativesE = Cudd_E(representativesNode);
653 representativesT = representativesE = representativesNode;
656 if (representativesT != representativesE && Cudd_IsComplement(representativesNode)) {
657 representativesT = Cudd_Not(representativesT);
658 representativesE = Cudd_Not(representativesE);
661 extractTransitionMatrixRec(ee, sourceOdd.
getElseSuccessor(), sourceOffset, targetE, representativesE, Cudd_T(variables),
662 nondeterminismVariables, stateOdd ? &stateOdd->
getElseSuccessor() : stateOdd, stateOffset);
663 extractTransitionMatrixRec(et, sourceOdd.
getElseSuccessor(), sourceOffset, targetT, representativesE, Cudd_T(variables),
664 nondeterminismVariables, stateOdd ? &stateOdd->
getElseSuccessor() : stateOdd, stateOffset);
666 Cudd_T(variables), nondeterminismVariables, stateOdd ? &stateOdd->
getThenSuccessor() : stateOdd,
667 stateOffset + (stateOdd ? stateOdd->getElseOffset() : 0));
669 Cudd_T(variables), nondeterminismVariables, stateOdd ? &stateOdd->
getThenSuccessor() : stateOdd,
670 stateOffset + (stateOdd ? stateOdd->getElseOffset() : 0));
678 phmap::flat_hash_map<DdNode const*, uint64_t> blockToOffset;
681template<
typename ValueType,
typename ExportValueType>
690 this->createBlockToOffsetMapping();
695 this->createMatrixEntryStorage();
696 extractTransitionMatrixRec(matrix.
getInternalAdd().getSylvanMtbdd().GetMTBDD(), this->isNondeterministic ? this->nondeterminismOdd : this->odd, 0,
697 this->partitionBdd.getInternalBdd().getSylvanBdd().GetBDD(), this->representatives.getInternalBdd().getSylvanBdd().GetBDD(),
698 this->allSourceVariablesCube.getInternalBdd().getSylvanBdd().GetBDD(),
699 this->nondeterminismVariablesCube.getInternalBdd().getSylvanBdd().GetBDD(), this->isNondeterministic ? &this->odd : nullptr,
701 return this->createMatrixFromEntries();
708 extractVectorRec(vector.
getInternalAdd().getSylvanMtbdd().GetMTBDD(), this->representatives.getInternalBdd().getSylvanBdd().GetBDD(),
709 variablesCube.
getInternalBdd().getSylvanBdd().GetBDD(), odd, 0, result);
713 void extractVectorRec(MTBDD vector, BDD representativesNode, BDD variables,
storm::dd::Odd const& odd, uint64_t offset,
714 std::vector<ExportValueType>& result) {
715 if (representativesNode == sylvan_false || mtbdd_iszero(vector)) {
719 if (sylvan_isconst(variables)) {
725 vectorT = sylvan_high(vector);
726 vectorE = sylvan_low(vector);
728 vectorT = vectorE = vector;
731 BDD representativesT;
732 BDD representativesE;
734 representativesT = sylvan_high(representativesNode);
735 representativesE = sylvan_low(representativesNode);
737 representativesT = representativesE = representativesNode;
740 extractVectorRec(vectorE, representativesE, sylvan_high(variables), odd.
getElseSuccessor(), offset, result);
745 void createBlockToOffsetMapping() {
746 this->createBlockToOffsetMappingRec(this->partitionBdd.getInternalBdd().getSylvanBdd().GetBDD(),
747 this->representatives.getInternalBdd().getSylvanBdd().GetBDD(),
748 this->rowVariablesCube.getInternalBdd().getSylvanBdd().GetBDD(), this->odd, 0);
750 "Mismatching block-to-offset mapping: " << blockToOffset.size() <<
" vs. " << this->numberOfBlocks <<
".");
753 void createBlockToOffsetMappingRec(BDD partitionNode, BDD representativesNode, BDD variables,
storm::dd::Odd const& odd, uint64_t offset) {
754 STORM_LOG_ASSERT(partitionNode != sylvan_false || representativesNode == sylvan_false,
"Expected representative to be zero if the partition is zero.");
755 if (representativesNode == sylvan_false || partitionNode == sylvan_false) {
759 if (sylvan_isconst(variables)) {
761 STORM_LOG_ASSERT(blockToOffset.find(partitionNode) == blockToOffset.end(),
"Duplicate entry.");
762 blockToOffset[partitionNode] = offset;
768 partitionT = sylvan_high(partitionNode);
769 partitionE = sylvan_low(partitionNode);
771 partitionT = partitionE = partitionNode;
774 BDD representativesT;
775 BDD representativesE;
777 representativesT = sylvan_high(representativesNode);
778 representativesE = sylvan_low(representativesNode);
780 representativesT = representativesE = representativesNode;
783 createBlockToOffsetMappingRec(partitionE, representativesE, sylvan_high(variables), odd.
getElseSuccessor(), offset);
788 void extractTransitionMatrixRec(MTBDD transitionMatrixNode,
storm::dd::Odd const& sourceOdd, uint64_t sourceOffset, BDD targetPartitionNode,
789 BDD representativesNode, BDD variables, BDD nondeterminismVariables,
storm::dd::Odd const* stateOdd, uint64_t stateOffset) {
792 if (mtbdd_iszero(transitionMatrixNode) || representativesNode == sylvan_false) {
797 if (sylvan_isconst(variables)) {
798 STORM_LOG_ASSERT(mtbdd_isleaf(transitionMatrixNode),
"Expected constant node.");
799 this->addMatrixEntry(
800 sourceOffset, blockToOffset.at(targetPartitionNode),
803 this->assignRowToState(sourceOffset, stateOffset);
807 bool nextVariableIsNondeterminismVariable =
808 !sylvan_isconst(nondeterminismVariables) && sylvan_var(nondeterminismVariables) == sylvan_var(variables);
810 if (nextVariableIsNondeterminismVariable) {
816 t = sylvan_high(transitionMatrixNode);
817 e = sylvan_low(transitionMatrixNode);
819 t = e = transitionMatrixNode;
823 extractTransitionMatrixRec(e, sourceOdd.
getElseSuccessor(), sourceOffset, targetPartitionNode, representativesNode, sylvan_high(variables),
824 sylvan_high(nondeterminismVariables), stateOdd, stateOffset);
825 extractTransitionMatrixRec(t, sourceOdd.
getThenSuccessor(), sourceOffset + sourceOdd.
getElseOffset(), targetPartitionNode, representativesNode,
826 sylvan_high(variables), sylvan_high(nondeterminismVariables), stateOdd, stateOffset);
836 t = sylvan_high(transitionMatrixNode);
837 e = sylvan_low(transitionMatrixNode);
839 t = e = transitionMatrixNode;
868 targetT = sylvan_high(targetPartitionNode);
869 targetE = sylvan_low(targetPartitionNode);
872 targetT = targetE = targetPartitionNode;
875 BDD representativesT;
876 BDD representativesE;
879 representativesT = sylvan_high(representativesNode);
880 representativesE = sylvan_low(representativesNode);
883 representativesT = representativesE = representativesNode;
886 extractTransitionMatrixRec(ee, sourceOdd.
getElseSuccessor(), sourceOffset, targetE, representativesE, sylvan_high(variables),
887 nondeterminismVariables, stateOdd ? &stateOdd->
getElseSuccessor() : stateOdd, stateOffset);
888 extractTransitionMatrixRec(et, sourceOdd.
getElseSuccessor(), sourceOffset, targetT, representativesE, sylvan_high(variables),
889 nondeterminismVariables, stateOdd ? &stateOdd->
getElseSuccessor() : stateOdd, stateOffset);
891 sylvan_high(variables), nondeterminismVariables, stateOdd ? &stateOdd->
getThenSuccessor() : stateOdd,
892 stateOffset + (stateOdd ? stateOdd->getElseOffset() : 0));
894 sylvan_high(variables), nondeterminismVariables, stateOdd ? &stateOdd->
getThenSuccessor() : stateOdd,
895 stateOffset + (stateOdd ? stateOdd->getElseOffset() : 0));
901 phmap::flat_hash_map<BDD, uint64_t> blockToOffset;
904template<storm::dd::DdType DdType,
typename ValueType,
typename ExportValueType>
906 : useRepresentatives(false), quotientFormat(quotientFormat) {
908 this->useRepresentatives = settings.isUseRepresentativesSet();
909 this->useOriginalVariables = settings.isUseOriginalVariablesSet();
912template<storm::dd::DdType DdType,
typename ValueType,
typename ExportValueType>
916 auto start = std::chrono::high_resolution_clock::now();
917 std::shared_ptr<storm::models::Model<ExportValueType>> result;
919 result = extractSparseQuotient(model, partition, preservationInformation);
921 result = extractDdQuotient(model, partition, preservationInformation);
923 auto end = std::chrono::high_resolution_clock::now();
924 STORM_LOG_INFO(
"Quotient extraction completed in " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() <<
"ms.");
926 STORM_LOG_THROW(result, storm::exceptions::NotSupportedException,
"Quotient could not be extracted.");
931template<storm::dd::DdType DdType,
typename ValueType,
typename ExportValueType>
940 auto start = std::chrono::high_resolution_clock::now();
944 "Representatives size does not match that of the partition: " << representatives.getNonZeroCount() <<
" vs. " << partition.
getNumberOfBlocks() <<
".");
946 "Representatives do not cover all blocks.");
950 auto end = std::chrono::high_resolution_clock::now();
951 STORM_LOG_INFO(
"Quotient transition matrix extracted in " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() <<
"ms.");
953 start = std::chrono::high_resolution_clock::now();
955 quotientStateLabeling.addLabel(
"init", sparseExtractor.extractSetExists(model.
getInitialStates()));
956 quotientStateLabeling.addLabel(
"deadlock", sparseExtractor.extractSetExists(model.
getDeadlockStates()));
958 for (
auto const& label : preservationInformation.getLabels()) {
959 quotientStateLabeling.addLabel(label, sparseExtractor.extractSetAll(model.
getStates(label)));
961 for (
auto const& expression : preservationInformation.getExpressions()) {
962 std::stringstream stream;
963 stream << expression;
964 std::string expressionAsString = stream.str();
966 if (quotientStateLabeling.containsLabel(expressionAsString)) {
967 STORM_LOG_WARN(
"Duplicate label '" << expressionAsString <<
"', dropping second label definition.");
969 quotientStateLabeling.addLabel(stream.str(), sparseExtractor.extractSetAll(model.
getStates(expression)));
972 end = std::chrono::high_resolution_clock::now();
973 STORM_LOG_INFO(
"Quotient labels extracted in " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).
count() <<
"ms.");
975 start = std::chrono::high_resolution_clock::now();
976 std::unordered_map<std::string, storm::models::sparse::StandardRewardModel<ExportValueType>> quotientRewardModels;
977 for (
auto const& rewardModelName : preservationInformation.getRewardModelNames()) {
980 std::optional<std::vector<ExportValueType>> quotientStateRewards;
981 if (rewardModel.hasStateRewards()) {
982 quotientStateRewards = sparseExtractor.extractStateVector(rewardModel.getStateRewardVector());
985 std::optional<std::vector<ExportValueType>> quotientStateActionRewards;
986 if (rewardModel.hasStateActionRewards()) {
987 quotientStateActionRewards = sparseExtractor.extractStateActionVector(rewardModel.getStateActionRewardVector());
991 std::move(quotientStateRewards), std::move(quotientStateActionRewards), std::nullopt));
993 end = std::chrono::high_resolution_clock::now();
994 STORM_LOG_INFO(
"Reward models extracted in " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).
count() <<
"ms.");
996 std::shared_ptr<storm::models::sparse::Model<ExportValueType>> result;
998 result = std::make_shared<storm::models::sparse::Dtmc<ExportValueType>>(std::move(quotientTransitionMatrix), std::move(quotientStateLabeling),
999 std::move(quotientRewardModels));
1001 result = std::make_shared<storm::models::sparse::Ctmc<ExportValueType>>(std::move(quotientTransitionMatrix), std::move(quotientStateLabeling),
1002 std::move(quotientRewardModels));
1004 result = std::make_shared<storm::models::sparse::Mdp<ExportValueType>>(std::move(quotientTransitionMatrix), std::move(quotientStateLabeling),
1005 std::move(quotientRewardModels));
1008 *model.template as<storm::models::symbolic::MarkovAutomaton<DdType, ValueType>>();
1010 boost::optional<storm::storage::BitVector> markovianStates = sparseExtractor.extractSetExists(markovAutomaton.
getMarkovianStates());
1012 std::move(quotientRewardModels),
false, std::move(markovianStates));
1013 modelComponents.exitRates = sparseExtractor.extractStateVector(markovAutomaton.
getExitRateVector());
1015 result = std::make_shared<storm::models::sparse::MarkovAutomaton<ExportValueType>>(std::move(modelComponents));
1021template<storm::dd::DdType DdType,
typename ValueType,
typename ExportValueType>
1022std::shared_ptr<storm::models::symbolic::Model<DdType, ExportValueType>> QuotientExtractor<DdType, ValueType, ExportValueType>::extractDdQuotient(
1024 PreservationInformation<DdType, ValueType>
const& preservationInformation) {
1025 if (this->useOriginalVariables) {
1026 return extractQuotientUsingOriginalVariables(model, partition, preservationInformation);
1028 return extractQuotientUsingBlockVariables(model, partition, preservationInformation);
1032template<storm::dd::DdType DdType,
typename ValueType,
typename ExportValueType>
1033std::shared_ptr<storm::models::symbolic::Model<DdType, ExportValueType>>
1034QuotientExtractor<DdType, ValueType, ExportValueType>::extractQuotientUsingBlockVariables(
1036 PreservationInformation<DdType, ValueType>
const& preservationInformation) {
1037 auto modelType = model.
getType();
1039 bool useRepresentativesForThisExtraction = this->useRepresentatives;
1045 "Mismatching partition.");
1047 std::set<storm::expressions::Variable> blockVariableSet = {partition.getBlockVariable()};
1048 std::set<storm::expressions::Variable> blockPrimeVariableSet = {partition.getPrimedBlockVariable()};
1049 std::vector<std::pair<storm::expressions::Variable, storm::expressions::Variable>> blockMetaVariablePairs = {
1050 std::make_pair(partition.getBlockVariable(), partition.getPrimedBlockVariable())};
1052 auto start = std::chrono::high_resolution_clock::now();
1055 storm::dd::Bdd<DdType> partitionAsBdd = partition.storedAsBdd() ? partition.asBdd() : partition.asAdd().notZero();
1057 auto representatives = InternalRepresentativeComputer<DdType>(partitionAsBdd, model.
getRowVariables()).getRepresentatives();
1059 if (useRepresentativesForThisExtraction) {
1062 (representatives && partitionAsBddOverPrimedBlockVariables).renameVariablesConcretize(model.
getRowVariables(), blockVariableSet);
1063 partitionAsBdd = (tmp && partitionAsBddOverPrimedBlockVariables).existsAbstract(blockPrimeVariableSet);
1069 std::map<std::string, storm::dd::Bdd<DdType>> preservedLabelBdds;
1070 for (
auto const& label : preservationInformation.getLabels()) {
1071 preservedLabelBdds.emplace(label, (model.
getStates(label) && partitionAsBdd).existsAbstract(model.
getRowVariables()));
1073 for (
auto const& expression : preservationInformation.getExpressions()) {
1074 std::stringstream stream;
1075 stream << expression;
1076 std::string expressionAsString = stream.str();
1078 auto it = preservedLabelBdds.find(expressionAsString);
1079 if (it != preservedLabelBdds.end()) {
1080 STORM_LOG_WARN(
"Duplicate label '" << expressionAsString <<
"', dropping second label definition.");
1082 preservedLabelBdds.emplace(stream.str(), (model.
getStates(expression) && partitionAsBdd).existsAbstract(model.
getRowVariables()));
1085 auto end = std::chrono::high_resolution_clock::now();
1086 STORM_LOG_INFO(
"Quotient labels extracted in " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).
count() <<
"ms.");
1088 start = std::chrono::high_resolution_clock::now();
1089 std::set<storm::expressions::Variable> blockAndRowVariables;
1091 std::inserter(blockAndRowVariables, blockAndRowVariables.end()));
1092 std::set<storm::expressions::Variable> blockPrimeAndColumnVariables;
1094 std::inserter(blockPrimeAndColumnVariables, blockPrimeAndColumnVariables.end()));
1100 partitionAsBdd &= representatives;
1101 partitionAsAdd *= partitionAsBdd.template toAdd<ValueType>();
1104 end = std::chrono::high_resolution_clock::now();
1107 if (std::is_same<ValueType, storm::RationalNumber>::value) {
1108 STORM_LOG_ASSERT(quotientTransitionMatrix.
greater(storm::utility::one<ValueType>()).isZero(),
"Illegal entries in quotient matrix.");
1109 }
else if (std::is_same<ValueType, storm::RationalFunction>::value) {
1112 STORM_LOG_ASSERT(quotientTransitionMatrix.
greater(storm::utility::one<ValueType>() + storm::utility::convertNumber<ValueType>(1e-6)).isZero(),
1113 "Illegal entries in quotient matrix.");
1116 .equalModuloPrecision(quotientTransitionMatrix.
notZero().existsAbstract(blockPrimeVariableSet).template toAdd<ValueType>(),
1117 storm::utility::convertNumber<ValueType>(1e-6)),
1118 "Illegal non-probabilistic matrix.");
1120 STORM_LOG_INFO(
"Quotient transition matrix extracted in " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).
count() <<
"ms.");
1125 blockPrimeAndNondeterminismVariables.insert(blockPrimeVariableSet.begin(), blockPrimeVariableSet.end());
1128 start = std::chrono::high_resolution_clock::now();
1129 std::unordered_map<std::string, storm::models::symbolic::StandardRewardModel<DdType, ValueType>> quotientRewardModels;
1130 for (
auto const& rewardModelName : preservationInformation.getRewardModelNames()) {
1133 boost::optional<storm::dd::Add<DdType, ValueType>> quotientStateRewards;
1134 if (rewardModel.hasStateRewards()) {
1138 boost::optional<storm::dd::Add<DdType, ValueType>> quotientStateActionRewards;
1139 if (rewardModel.hasStateActionRewards()) {
1140 quotientStateActionRewards = rewardModel.getStateActionRewardVector().multiplyMatrix(partitionAsAdd, model.
getRowVariables());
1144 quotientStateRewards, quotientStateActionRewards, boost::none));
1146 end = std::chrono::high_resolution_clock::now();
1147 STORM_LOG_INFO(
"Reward models extracted in " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).
count() <<
"ms.");
1149 std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> result;
1152 model.
getManager().asSharedPointer(), reachableStates, initialStates, deadlockStates, quotientTransitionMatrix, blockVariableSet,
1153 blockPrimeVariableSet, blockMetaVariablePairs, preservedLabelBdds, quotientRewardModels));
1156 model.
getManager().asSharedPointer(), reachableStates, initialStates, deadlockStates, quotientTransitionMatrix, blockVariableSet,
1157 blockPrimeVariableSet, blockMetaVariablePairs, preservedLabelBdds, quotientRewardModels));
1160 model.
getManager().asSharedPointer(), reachableStates, initialStates, deadlockStates, quotientTransitionMatrix, blockVariableSet,
1161 blockPrimeVariableSet, blockMetaVariablePairs, model.
getNondeterminismVariables(), preservedLabelBdds, quotientRewardModels));
1167 deadlockStates, quotientTransitionMatrix, blockVariableSet, blockPrimeVariableSet, blockMetaVariablePairs,
1171 return result->template toValueType<ExportValueType>();
1173 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"Cannot extract quotient for this model type.");
1177template<storm::dd::DdType DdType,
typename ValueType,
typename ExportValueType>
1178std::shared_ptr<storm::models::symbolic::Model<DdType, ExportValueType>>
1179QuotientExtractor<DdType, ValueType, ExportValueType>::extractQuotientUsingOriginalVariables(
1181 PreservationInformation<DdType, ValueType>
const& preservationInformation) {
1182 auto modelType = model.
getType();
1184 bool useRepresentativesForThisExtraction = this->useRepresentatives;
1187 STORM_LOG_WARN_COND(!this->useRepresentatives,
"Using representatives is unsupported for this extraction, falling back to regular extraction.");
1192 "Mismatching partition.");
1194 std::set<storm::expressions::Variable> blockVariableSet = {partition.getBlockVariable()};
1195 std::set<storm::expressions::Variable> blockPrimeVariableSet = {partition.getPrimedBlockVariable()};
1196 std::vector<std::pair<storm::expressions::Variable, storm::expressions::Variable>> blockMetaVariablePairs = {
1197 std::make_pair(partition.getBlockVariable(), partition.getPrimedBlockVariable())};
1199 auto start = std::chrono::high_resolution_clock::now();
1202 storm::dd::Bdd<DdType> partitionAsBdd = partition.storedAsBdd() ? partition.asBdd() : partition.asAdd().notZero();
1204 auto representatives = InternalRepresentativeComputer<DdType>(partitionAsBdd, model.
getRowVariables()).getRepresentatives();
1206 if (useRepresentativesForThisExtraction) {
1209 (representatives && partitionAsBddOverPrimedBlockVariables).renameVariablesConcretize(model.
getRowVariables(), blockVariableSet);
1210 partitionAsBdd = (tmp && partitionAsBddOverPrimedBlockVariables).existsAbstract(blockPrimeVariableSet);
1219 std::map<std::string, storm::dd::Bdd<DdType>> preservedLabelBdds;
1220 for (
auto const& label : preservationInformation.getLabels()) {
1221 preservedLabelBdds.emplace(label, (model.
getStates(label) && partitionAsBdd)
1225 for (
auto const& expression : preservationInformation.getExpressions()) {
1226 std::stringstream stream;
1227 stream << expression;
1228 std::string expressionAsString = stream.str();
1230 auto it = preservedLabelBdds.find(expressionAsString);
1231 if (it != preservedLabelBdds.end()) {
1232 STORM_LOG_WARN(
"Duplicate label '" << expressionAsString <<
"', dropping second label definition.");
1234 preservedLabelBdds.emplace(stream.str(), (model.
getStates(expression) && partitionAsBdd)
1239 auto end = std::chrono::high_resolution_clock::now();
1240 STORM_LOG_INFO(
"Quotient labels extracted in " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).
count() <<
"ms.");
1242 start = std::chrono::high_resolution_clock::now();
1243 std::set<storm::expressions::Variable> blockAndRowVariables;
1245 std::inserter(blockAndRowVariables, blockAndRowVariables.end()));
1246 std::set<storm::expressions::Variable> blockPrimeAndColumnVariables;
1248 std::inserter(blockPrimeAndColumnVariables, blockPrimeAndColumnVariables.end()));
1256 partitionAsBdd &= representatives;
1257 partitionAsAdd = partitionAsBdd.template toAdd<ValueType>();
1261 quotientTransitionMatrix = (quotientTransitionMatrix * partitionAsAdd)
1268 end = std::chrono::high_resolution_clock::now();
1271 if (std::is_same<ValueType, storm::RationalNumber>::value) {
1272 STORM_LOG_ASSERT(quotientTransitionMatrix.
greater(storm::utility::one<ValueType>()).isZero(),
"Illegal entries in quotient matrix.");
1274 STORM_LOG_ASSERT(quotientTransitionMatrix.
greater(storm::utility::one<ValueType>() + storm::utility::convertNumber<ValueType>(1e-6)).isZero(),
1275 "Illegal entries in quotient matrix.");
1278 .equalModuloPrecision(quotientTransitionMatrix.
notZero().existsAbstract(model.
getColumnVariables()).template toAdd<ValueType>(),
1279 storm::utility::convertNumber<ValueType>(1e-6)),
1280 "Illegal probabilistic matrix.");
1282 STORM_LOG_INFO(
"Quotient transition matrix extracted in " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).
count() <<
"ms.");
1286 std::set<storm::expressions::Variable> columnAndNondeterminismVariables = model.
getColumnVariables();
1290 start = std::chrono::high_resolution_clock::now();
1291 std::unordered_map<std::string, storm::models::symbolic::StandardRewardModel<DdType, ValueType>> quotientRewardModels;
1292 for (
auto const& rewardModelName : preservationInformation.getRewardModelNames()) {
1295 boost::optional<storm::dd::Add<DdType, ValueType>> quotientStateRewards;
1296 if (rewardModel.hasStateRewards()) {
1302 boost::optional<storm::dd::Add<DdType, ValueType>> quotientStateActionRewards;
1303 if (rewardModel.hasStateActionRewards()) {
1304 quotientStateActionRewards = rewardModel.getStateActionRewardVector()
1310 quotientStateRewards, quotientStateActionRewards, boost::none));
1312 end = std::chrono::high_resolution_clock::now();
1313 STORM_LOG_INFO(
"Reward models extracted in " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).
count() <<
"ms.");
1315 std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> result;
1318 model.
getManager().asSharedPointer(), reachableStates, initialStates, deadlockStates, quotientTransitionMatrix, model.
getRowVariables(),
1322 model.
getManager().asSharedPointer(), reachableStates, initialStates, deadlockStates, quotientTransitionMatrix, model.
getRowVariables(),
1326 model.
getManager().asSharedPointer(), reachableStates, initialStates, deadlockStates, quotientTransitionMatrix, model.
getRowVariables(),
1328 quotientRewardModels));
1338 return result->template toValueType<ExportValueType>();
1340 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"Cannot extract quotient for this model type.");
1344template class QuotientExtractor<storm::dd::DdType::CUDD, double>;
1346template class QuotientExtractor<storm::dd::DdType::Sylvan, double>;
1347template class QuotientExtractor<storm::dd::DdType::Sylvan, storm::RationalNumber>;
1348template class QuotientExtractor<storm::dd::DdType::Sylvan, storm::RationalNumber, double>;
1349template 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)
SettingsType const & getModule()
Get module.