27template<
typename ModelType,
typename BlockDataType>
29 this->preserveSingleFormula(
model, formula);
32template<
typename ModelType,
typename BlockDataType>
34 std::vector<std::shared_ptr<storm::logic::Formula const>>
const& formulas)
36 if (formulas.empty()) {
37 this->respectedAtomicPropositions = model.getStateLabeling().getLabels();
38 this->keepRewards = true;
40 if (formulas.size() == 1) {
41 this->preserveSingleFormula(model, *formulas.front());
43 for (auto const& formula : formulas) {
44 preserveFormula(*formula);
49template<
typename ModelType,
typename BlockDataType>
63template<
typename ModelType,
typename BlockDataType>
86template<
typename ModelType,
typename BlockDataType>
87void BisimulationDecomposition<ModelType, BlockDataType>::Options::preserveSingleFormula(ModelType
const&
model,
storm::logic::Formula const& formula) {
103 this->checkAndSetMeasureDrivenInitialPartition(
model, formula);
106template<
typename ModelType,
typename BlockDataType>
107void BisimulationDecomposition<ModelType, BlockDataType>::Options::checkAndSetMeasureDrivenInitialPartition(ModelType
const&
model,
109 std::shared_ptr<storm::logic::Formula const> newFormula = formula.
asSharedPointer();
117 optimalityType = OptimizationDirection::Maximize;
119 optimalityType = OptimizationDirection::Minimize;
129 optimalityType = OptimizationDirection::Maximize;
131 optimalityType = OptimizationDirection::Minimize;
137 std::shared_ptr<storm::logic::Formula const> leftSubformula = std::make_shared<storm::logic::BooleanLiteralFormula>(
true);
138 std::shared_ptr<storm::logic::Formula const> rightSubformula;
139 if (newFormula->isUntilFormula()) {
140 leftSubformula = newFormula->asUntilFormula().getLeftSubformula().asSharedPointer();
141 rightSubformula = newFormula->asUntilFormula().getRightSubformula().asSharedPointer();
143 measureDrivenInitialPartition =
true;
145 }
else if (newFormula->isEventuallyFormula()) {
146 rightSubformula = newFormula->asEventuallyFormula().getSubformula().asSharedPointer();
148 measureDrivenInitialPartition =
true;
152 if (measureDrivenInitialPartition) {
153 storm::modelchecker::SparsePropositionalModelChecker<ModelType> checker(
model);
154 std::unique_ptr<storm::modelchecker::CheckResult> phiStatesCheckResult = checker.check(*leftSubformula);
155 std::unique_ptr<storm::modelchecker::CheckResult> psiStatesCheckResult = checker.check(*rightSubformula);
158 phiStates = phiStatesCheckResult->template asExplicitQualitativeCheckResult<SolutionType>().getTruthValuesVector();
159 psiStates = psiStatesCheckResult->template asExplicitQualitativeCheckResult<SolutionType>().getTruthValuesVector();
161 optimalityType.reset();
165template<
typename ModelType,
typename BlockDataType>
167 std::vector<std::shared_ptr<storm::logic::AtomicExpressionFormula const>>
const& expressions,
168 std::vector<std::shared_ptr<storm::logic::AtomicLabelFormula const>>
const& labels) {
169 std::set<std::string> labelsToRespect;
170 for (
auto const& labelFormula : labels) {
171 labelsToRespect.insert(labelFormula->getLabel());
173 for (
auto const& expressionFormula : expressions) {
174 labelsToRespect.insert(expressionFormula->toString());
176 if (!respectedAtomicPropositions) {
177 respectedAtomicPropositions = labelsToRespect;
179 respectedAtomicPropositions.value().insert(labelsToRespect.begin(), labelsToRespect.end());
183template<
typename ModelType,
typename BlockDataType>
189template<
typename ModelType,
typename BlockDataType>
195 "Bisimulation currently only supports models with at most one reward model.");
197 storm::exceptions::IllegalFunctionCallException,
198 "Bisimulation is currently supported for models with state or action rewards only. Consider converting the transition rewards to state "
199 "rewards (via suitable function calls).");
201 "Weak bisimulation cannot preserve bounded properties.");
203 "Weak bisimulation cannot preserve discounted properties.");
211template<
typename ModelType,
typename BlockDataType>
213 std::chrono::high_resolution_clock::time_point totalStart = std::chrono::high_resolution_clock::now();
215 std::chrono::high_resolution_clock::time_point initialPartitionStart = std::chrono::high_resolution_clock::now();
217 if (
options.measureDrivenInitialPartition) {
218 STORM_LOG_THROW(
options.phiStates, storm::exceptions::InvalidOptionException,
"Unable to compute measure-driven initial partition without phi states.");
219 STORM_LOG_THROW(
options.psiStates, storm::exceptions::InvalidOptionException,
"Unable to compute measure-driven initial partition without psi states.");
225 std::chrono::high_resolution_clock::duration initialPartitionTime = std::chrono::high_resolution_clock::now() - initialPartitionStart;
229 std::chrono::high_resolution_clock::time_point refinementStart = std::chrono::high_resolution_clock::now();
231 std::chrono::high_resolution_clock::duration refinementTime = std::chrono::high_resolution_clock::now() - refinementStart;
233 std::chrono::high_resolution_clock::time_point extractionStart = std::chrono::high_resolution_clock::now();
235 std::chrono::high_resolution_clock::duration extractionTime = std::chrono::high_resolution_clock::now() - extractionStart;
237 std::chrono::high_resolution_clock::time_point quotientBuildStart = std::chrono::high_resolution_clock::now();
241 std::chrono::high_resolution_clock::duration quotientBuildTime = std::chrono::high_resolution_clock::now() - quotientBuildStart;
243 std::chrono::high_resolution_clock::duration totalTime = std::chrono::high_resolution_clock::now() - totalStart;
246 std::chrono::milliseconds initialPartitionTimeInMilliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(initialPartitionTime);
247 std::chrono::milliseconds refinementTimeInMilliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(refinementTime);
248 std::chrono::milliseconds extractionTimeInMilliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(extractionTime);
249 std::chrono::milliseconds quotientBuildTimeInMilliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(quotientBuildTime);
250 std::chrono::milliseconds totalTimeInMilliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(totalTime);
251 std::cout <<
"\nTime breakdown:\n";
252 std::cout <<
" * time for initial partition: " << initialPartitionTimeInMilliseconds.count() <<
"ms\n";
253 std::cout <<
" * time for partitioning: " << refinementTimeInMilliseconds.count() <<
"ms\n";
254 std::cout <<
" * time for extraction: " << extractionTimeInMilliseconds.count() <<
"ms\n";
255 std::cout <<
" * time for building quotient: " << quotientBuildTimeInMilliseconds.count() <<
"ms\n";
256 std::cout <<
"------------------------------------------\n";
257 std::cout <<
" * total time: " << totalTimeInMilliseconds.count() <<
"ms\n\n";
261template<
typename ModelType,
typename BlockDataType>
264 std::vector<Block<BlockDataType>*> splitterQueue;
266 block->data().setSplitter();
267 splitterQueue.push_back(block.get());
271 uint_fast64_t iterations = 0;
272 while (!splitterQueue.empty()) {
278 std::sort(splitterQueue.begin(), splitterQueue.end(),
281 splitterQueue.pop_back();
282 splitter->
data().setSplitter(
false);
288 std::cout <<
"Performed " << iterations <<
" iterations of partition refinement before abort.\n";
289 STORM_LOG_THROW(
false, storm::exceptions::AbortException,
"Aborted in bisimulation computation.");
295template<
typename ModelType,
typename BlockDataType>
298 "Unable to retrieve quotient model from bisimulation decomposition, because it was not built.");
302template<
typename ModelType,
typename BlockDataType>
304 auto const& rewardModel =
model.getUniqueRewardModel();
305 if (rewardModel.hasStateRewards()) {
308 if (rewardModel.hasStateActionRewards()) {
309 if (
model.isNondeterministicModel()) {
310 std::vector<std::set<ValueType>> actionRewards;
311 actionRewards.reserve(
model.getNumberOfStates());
313 std::set<ValueType> rewardsAtState;
314 for (
auto choice =
model.getTransitionMatrix().getRowGroupIndices()[state];
315 choice <
model.getTransitionMatrix().getRowGroupIndices()[state + 1]; ++choice) {
316 rewardsAtState.insert(rewardModel.getStateActionReward(choice));
318 actionRewards.push_back(std::move(rewardsAtState));
327template<
typename ModelType,
typename BlockDataType>
330 return rewardVector[a] < rewardVector[b];
334template<
typename ModelType,
typename BlockDataType>
337 return actionRewards[a] < actionRewards[b];
341template<
typename ModelType,
typename BlockDataType>
345 for (
auto const& label :
options.respectedAtomicPropositions.value()) {
346 if (label ==
"init") {
354 if (
options.getKeepRewards() &&
model.hasRewardModel()) {
359template<
typename ModelType,
typename BlockDataType>
363 std::optional<storm::storage::sparse::state_type> representativePsiState;
364 if (!
options.psiStates.value().empty()) {
365 representativePsiState = *
options.psiStates.value().begin();
369 model.getNumberOfStates(), statesWithProbability01.first,
370 options.getBounded() ||
options.getKeepRewards() ?
options.psiStates.value() : statesWithProbability01.second, representativePsiState);
374 if (
options.getKeepRewards() &&
model.hasRewardModel()) {
379template<
typename ModelType,
typename BlockDataType>
384template<
typename ModelType,
typename BlockDataType>
389 for (
auto const& blockPtr :
partition.getBlocks()) {
This class is the superclass of all decompositions of a sparse model into its bisimulation quotient.
storm::storage::SparseMatrix< ValueType > backwardTransitions
virtual void buildQuotient()=0
virtual void refinePartitionBasedOnSplitter(bisimulation::Block< bisimulation::DeterministicBlockData > &splitter, std::vector< bisimulation::Block< bisimulation::DeterministicBlockData > * > &splitterVector)=0
storm::utility::ConstantsComparator< ValueType > comparator
std::shared_ptr< ModelType > getQuotient() const
Retrieves the quotient of the model under the computed bisimulation.
std::shared_ptr< ModelType > quotient
void performPartitionRefinement()
virtual void splitInitialPartitionBasedOnRewards()
Splits the initial partition based on the (unique) reward model of the current model.
virtual void splitInitialPartitionBasedOnActionRewards(std::vector< std::set< ValueType > > const &rewardVector)
Splits the initial partition based on the given vector of action rewards.
BisimulationDecomposition(ModelType const &model, Options const &options)
Decomposes the given model into equivalence classes of a bisimulation.
storm::storage::bisimulation::Partition< BlockDataType > partition
void computeBisimulationDecomposition()
Computes the decomposition of the model into bisimulation equivalence classes.
void extractDecompositionBlocks()
virtual std::pair< storm::storage::BitVector, storm::storage::BitVector > getStatesWithProbability01()=0
Computes the set of states with probability 0/1 for satisfying phi until psi.
virtual void initializeMeasureDrivenPartition()
Creates the measure-driven initial partition for reaching psi states from phi states.
virtual void initializeLabelBasedPartition()
Initializes the initial partition based on all respected labels.
virtual void initialize()
std::vector< block_type > blocks
A class that holds a possibly non-square matrix in the compressed row storage format.
#define STORM_LOG_WARN_COND(cond, message)
#define STORM_LOG_THROW(cond, exception, message)
FragmentSpecification propositional()
SettingsType const & getModule()
Get module.
bool isTerminate()
Check whether the program should terminate (due to some abort signal).
typename detail::IntervalMetaProgrammingHelper< ValueType >::BaseType IntervalBaseType
Helper to access the type in which interval boundaries are stored.
void preserveFormula(storm::logic::Formula const &formula)
Changes the options in a way that the given formula is preserved.
bool measureDrivenInitialPartition
std::optional< storm::storage::BitVector > phiStates
bool buildQuotient
A flag that governs whether the quotient model is actually built or only the decomposition is compute...
std::optional< std::set< std::string > > respectedAtomicPropositions
An optional set of strings that indicate which of the atomic propositions of the model are to be resp...
std::optional< storm::storage::BitVector > psiStates