Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
Bisimulation.cpp
Go to the documentation of this file.
2
21
23
24template<typename ValueType>
26 std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, Options options) {
27 // Step 0: Sanity checks and set-up
29 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Bisimulation is not supported for Interval models.");
30 }
31 // When the caller does not pick an explicit tolerance, fall back to the model's stochastic tolerance. Exact value types use zero.
32 if (!options.tolerance.has_value()) {
35 } else {
37 }
38 }
39 storm::RationalNumber const& tolerance = *options.tolerance;
40 STORM_LOG_THROW(tolerance >= storm::utility::zero<storm::RationalNumber>(), storm::exceptions::InvalidArgumentException,
41 "Tolerance for bisimulation minimization must be non-negative, but was " << tolerance << ".");
42 bool const isWeak = options.bisimulationType == BisimulationType::Weak;
43 if (isWeak) {
44 STORM_LOG_THROW(!model.isNondeterministicModel(), storm::exceptions::NotSupportedException,
45 "Weak bisimulation is only supported for deterministic models, but the given model is of type " << model.getType() << ".");
47 "Using splitter-based refinement because weak bisimulation is not supported for signature-based refinement.");
48 }
49 // Weak bisimulation for signature-based refinement is not supported.
50 // Deterministic models default to splitter-based refinement, which is usually faster
51 bool const useSignatureRefinement = !isWeak && (model.isNondeterministicModel() || options.preferSignatureRefinement);
52 STORM_LOG_INFO("Using " << (useSignatureRefinement ? "signature" : "splitter") << "-based refinement for bisimulation minimization.");
54 STORM_LOG_STATISTICS("-------- Bisimulation Minimization --------");
55
56 // Step 1: Obtain an initial partition based on what needs to be preserved (labels, rewards, ...)
57 storm::bisimulation::Initialization<ValueType> initialization(model, options, formulas);
58 auto const preservationInformation = initialization.getPreservationInformation();
59 auto const choiceClasses = initialization.getChoiceClasses();
60 auto partition = initialization.getInitialStatePartition(choiceClasses);
61 STORM_LOG_STATISTICS(sw << " seconds for initial partition (" << partition.getNumberOfBlocks() << " blocks).");
62 sw.restart();
63
64 // Step 2: Apply refinement using the initial partition and choiceClasses. Initialize QuotientData.
65 std::optional<storm::bisimulation::QuotientData<ValueType>> quotientData;
66 // commonly called right after refinement.
67 auto initializeQuotientData = [&partition, &sw,
68 &quotientData](storm::OptionalRef<storm::storage::BitVector const> preferredRepresentatives = storm::NullRef) {
69 STORM_LOG_STATISTICS(sw << " seconds for refinement (" << partition.getNumberOfBlocks() << " blocks).");
70 sw.restart();
71 quotientData.emplace(partition, preferredRepresentatives);
72 };
74 // Unreachable, as Step 0 already threw for interval models. The branch remains so that the refinement below is not instantiated for them.
75 STORM_LOG_ASSERT(false, "Unexpected value type.");
76 } else if (isWeak) {
77 // Weak bisimulation additionally needs to know the divergent, step sensitive and silent states. Computing them further refines the partition.
78 // Both that computation and the refinement need the transposed transition matrix, so we build it once and share it.
79 auto const backwardTransitions = model.getBackwardTransitions();
80 auto weakData = initialization.getWeakBisimulationData(partition, backwardTransitions, preservationInformation);
81 STORM_LOG_STATISTICS(sw << " seconds for weak bisimulation initialization (" << partition.getNumberOfBlocks() << " blocks).");
82 sw.restart();
83 if (model.isDiscreteTimeModel()) {
85 model, backwardTransitions, partition, storm::utility::convertNumber<ValueType>(tolerance), weakData);
86 } else {
88 model, backwardTransitions, partition, storm::utility::convertNumber<ValueType>(tolerance), weakData);
89 }
90 // For weak bisimulation, the quotient transitions have to be derived from a state that can actually leave its block.
91 auto const nonSilentStates = ~weakData.silentStates;
92 initializeQuotientData(nonSilentStates);
93 quotientData->weakData.emplace(std::move(weakData));
94 if (options.createQuotientChoiceMapping) {
95 // For deterministic models, quotient state and choice mappings are identical.
96 STORM_LOG_ASSERT(!model.isNondeterministicModel(), "Expected a deterministic model.");
97 quotientData->toQuotientChoice = quotientData->toQuotientState;
98 }
99 } else if (useSignatureRefinement && storm::utility::isZero(tolerance)) {
100 storm::bisimulation::Signatures<ValueType, SignatureMode::Exact> signatures(model, choiceClasses, partition);
101 storm::bisimulation::performSignatureBasedRefinement(model, partition, signatures);
102 initializeQuotientData();
103 signatures.extendQuotientData(quotientData.value(), options.createQuotientChoiceMapping);
104 } else if (useSignatureRefinement && !storm::utility::isZero(tolerance)) {
105 if constexpr (std::is_same_v<ValueType, storm::RationalFunction>) {
106 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException,
107 "Bisimulation with positive tolerance " << tolerance << " (approximately " << storm::utility::convertNumber<double>(tolerance)
108 << ") is not supported for parametric models.");
109 } else {
110 storm::bisimulation::Signatures<ValueType, SignatureMode::Approximative> signatures(model, choiceClasses, partition,
112 storm::bisimulation::performSignatureBasedRefinement(model, partition, signatures);
113 initializeQuotientData();
114 signatures.extendQuotientData(quotientData.value(), options.createQuotientChoiceMapping);
115 }
116 } else {
117 // For deterministic models we do splitter based refinement.
118 STORM_LOG_ASSERT(!model.isNondeterministicModel(), "Splitter-based refinement is only applicable to deterministic models.");
121 initializeQuotientData();
122 if (options.createQuotientChoiceMapping) {
123 // For deterministic models, quotient state and choice mappings are identical.
124 quotientData->toQuotientChoice = quotientData->toQuotientState;
125 }
126 }
127
128 // Step 3: Extract the quotient
129 auto quotientModel = storm::bisimulation::Quotient<ValueType>::buildFromPartition(model, options, preservationInformation, quotientData.value());
130 STORM_LOG_STATISTICS(sw << " seconds for quotient extraction.");
131 STORM_LOG_STATISTICS("-------------------------------------------");
132
133 return {.quotient = std::move(quotientModel),
134 .toQuotientStateMapping = std::move(quotientData->toQuotientState),
135 .toQuotientChoiceMapping = std::move(quotientData->toQuotientChoice)};
136}
137
139 std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, Options options);
141 std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas,
142 Options options);
144 std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas,
145 Options options);
147 std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas,
148 Options options);
150 std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas,
151 Options options);
152
153} // namespace storm::bisimulation
Helper class that optionally holds a reference to an object of type T.
Definition OptionalRef.h:48
WeakBisimulationData getWeakBisimulationData(Partition &partition, storm::storage::SparseMatrix< ValueType > const &backwardTransitions, PreservationInformation const &preservationInformation) const
Computes the state-level information that weak bisimulation requires on top of the partition,...
std::optional< std::vector< uint64_t > > getChoiceClasses() const
Partition getInitialStatePartition(std::optional< std::vector< uint64_t > > const &choiceClasses={}) const
PreservationInformation getPreservationInformation() const
static std::shared_ptr< storm::models::sparse::Model< ValueType > > buildFromPartition(storm::models::sparse::Model< ValueType > const &model, storm::bisimulation::Options const &options, storm::bisimulation::PreservationInformation const &preservationInformation, QuotientData< ValueType > const &quotientData)
Builds the quotient model from the given partition (represented by quotientData), preserving the give...
Definition Quotient.cpp:24
Computes and caches the state signatures and provides state-based order / condition for splitting in ...
Definition Signatures.h:24
void extendQuotientData(QuotientData< ValueType > &quotientData, bool const createQuotientChoiceMapping) const
Fills in the choice mappings of the given (already state-mapped) quotient data: for every quotient st...
bool isNondeterministicModel() const
Returns true if the model is a nondeterministic model.
Definition ModelBase.cpp:31
bool isDiscreteTimeModel() const
Returns true if the model is a descrete-time model.
Definition ModelBase.cpp:41
virtual ModelType getType() const
Return the actual type of the model.
Definition ModelBase.cpp:7
Base class for all sparse models.
Definition Model.h:34
ValueType const & getStochasticTolerance() const
Retrieves the tolerance up to which the model components are considered valid.
Definition Model.cpp:713
storm::storage::SparseMatrix< ValueType > getBackwardTransitions() const
Retrieves the backward transition relation of the model, i.e.
Definition Model.cpp:186
A class that provides convenience operations to display run times.
Definition Stopwatch.h:13
void restart()
Reset the stopwatch and immediately start it.
Definition Stopwatch.cpp:59
#define STORM_LOG_INFO(message)
Definition logging.h:34
#define STORM_LOG_STATISTICS(message)
Definition logging.h:48
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_WARN_COND(cond, message)
Definition macros.h:54
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:39
ReturnType< ValueType > performBisimulationMinimization(storm::models::sparse::Model< ValueType > const &model, std::vector< std::shared_ptr< storm::logic::Formula const > > const &formulas, Options options)
Computes the bisimulation quotient of the given model.
void performSignatureBasedRefinement(storm::models::sparse::Model< ValueType > const &model, storm::bisimulation::Partition &partition, Signatures< ValueType, SignatureMode > &signatures)
void performSplitterBasedRefinement(storm::models::sparse::Model< ValueType > const &model, storm::storage::SparseMatrix< ValueType > const &backwardTransitions, storm::bisimulation::Partition &partition, ValueType const tolerance, storm::OptionalRef< WeakBisimulationData > weakData)
Performs splitter-based partition refinement.
bool isZero(ValueType const &a)
Definition constants.cpp:44
ValueType zero()
Definition constants.cpp:26
TargetType convertNumber(SourceType const &number)
constexpr bool IsIntervalType
Helper to check if a type is an interval.
constexpr NullRefType NullRef
Definition OptionalRef.h:31
static const bool IsExact
BisimulationType bisimulationType
Definition Options.h:32
std::optional< storm::RationalNumber > tolerance
Definition Options.h:35