25using namespace bisimulation;
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()) {
38 this->keepRewards =
true;
40 if (formulas.size() == 1) {
41 this->preserveSingleFormula(
model, *formulas.front());
43 for (
auto const& formula : formulas) {
49template<
typename ModelType,
typename BlockDataType>
51 : measureDrivenInitialPartition(false),
54 respectedAtomicPropositions(),
63template<
typename ModelType,
typename BlockDataType>
66 measureDrivenInitialPartition =
false;
67 phiStates = boost::none;
68 psiStates = boost::none;
86template<
typename ModelType,
typename BlockDataType>
103 this->checkAndSetMeasureDrivenInitialPartition(
model, formula);
106template<
typename ModelType,
typename BlockDataType>
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) {
154 std::unique_ptr<storm::modelchecker::CheckResult> phiStatesCheckResult = checker.check(*leftSubformula);
155 std::unique_ptr<storm::modelchecker::CheckResult> psiStatesCheckResult = checker.check(*rightSubformula);
156 phiStates = phiStatesCheckResult->asExplicitQualitativeCheckResult().getTruthValuesVector();
157 psiStates = psiStatesCheckResult->asExplicitQualitativeCheckResult().getTruthValuesVector();
159 optimalityType = boost::none;
163template<
typename ModelType,
typename BlockDataType>
164void BisimulationDecomposition<ModelType, BlockDataType>::Options::addToRespectedAtomicPropositions(
165 std::vector<std::shared_ptr<storm::logic::AtomicExpressionFormula const>>
const& expressions,
166 std::vector<std::shared_ptr<storm::logic::AtomicLabelFormula const>>
const& labels) {
167 std::set<std::string> labelsToRespect;
168 for (
auto const& labelFormula : labels) {
169 labelsToRespect.insert(labelFormula->getLabel());
171 for (
auto const& expressionFormula : expressions) {
172 labelsToRespect.insert(expressionFormula->toString());
174 if (!respectedAtomicPropositions) {
175 respectedAtomicPropositions = labelsToRespect;
177 respectedAtomicPropositions.get().insert(labelsToRespect.begin(), labelsToRespect.end());
181template<
typename ModelType,
typename BlockDataType>
187template<
typename ModelType,
typename BlockDataType>
191 : model(model), backwardTransitions(backwardTransitions), options(options), partition(), comparator(options.getTolerance()), quotient(nullptr) {
193 "Bisimulation currently only supports models with at most one reward model.");
195 storm::exceptions::IllegalFunctionCallException,
196 "Bisimulation is currently supported for models with state or action rewards only. Consider converting the transition rewards to state "
197 "rewards (via suitable function calls).");
199 "Weak bisimulation cannot preserve bounded properties.");
201 "Weak bisimulation cannot preserve discounted properties.");
209template<
typename ModelType,
typename BlockDataType>
211 std::chrono::high_resolution_clock::time_point totalStart = std::chrono::high_resolution_clock::now();
213 std::chrono::high_resolution_clock::time_point initialPartitionStart = std::chrono::high_resolution_clock::now();
215 if (options.measureDrivenInitialPartition) {
216 STORM_LOG_THROW(options.phiStates, storm::exceptions::InvalidOptionException,
"Unable to compute measure-driven initial partition without phi states.");
217 STORM_LOG_THROW(options.psiStates, storm::exceptions::InvalidOptionException,
"Unable to compute measure-driven initial partition without psi states.");
218 this->initializeMeasureDrivenPartition();
220 this->initializeLabelBasedPartition();
222 STORM_LOG_WARN_COND(partition.size() > 1,
"Initial partition consists only of a single block.");
223 std::chrono::high_resolution_clock::duration initialPartitionTime = std::chrono::high_resolution_clock::now() - initialPartitionStart;
227 std::chrono::high_resolution_clock::time_point refinementStart = std::chrono::high_resolution_clock::now();
228 this->performPartitionRefinement();
229 std::chrono::high_resolution_clock::duration refinementTime = std::chrono::high_resolution_clock::now() - refinementStart;
231 std::chrono::high_resolution_clock::time_point extractionStart = std::chrono::high_resolution_clock::now();
232 this->extractDecompositionBlocks();
233 std::chrono::high_resolution_clock::duration extractionTime = std::chrono::high_resolution_clock::now() - extractionStart;
235 std::chrono::high_resolution_clock::time_point quotientBuildStart = std::chrono::high_resolution_clock::now();
236 if (options.buildQuotient) {
237 this->buildQuotient();
239 std::chrono::high_resolution_clock::duration quotientBuildTime = std::chrono::high_resolution_clock::now() - quotientBuildStart;
241 std::chrono::high_resolution_clock::duration totalTime = std::chrono::high_resolution_clock::now() - totalStart;
243 if (storm::settings::getModule<storm::settings::modules::CoreSettings>().isShowStatisticsSet()) {
244 std::chrono::milliseconds initialPartitionTimeInMilliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(initialPartitionTime);
245 std::chrono::milliseconds refinementTimeInMilliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(refinementTime);
246 std::chrono::milliseconds extractionTimeInMilliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(extractionTime);
247 std::chrono::milliseconds quotientBuildTimeInMilliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(quotientBuildTime);
248 std::chrono::milliseconds totalTimeInMilliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(totalTime);
249 std::cout <<
"\nTime breakdown:\n";
250 std::cout <<
" * time for initial partition: " << initialPartitionTimeInMilliseconds.count() <<
"ms\n";
251 std::cout <<
" * time for partitioning: " << refinementTimeInMilliseconds.count() <<
"ms\n";
252 std::cout <<
" * time for extraction: " << extractionTimeInMilliseconds.count() <<
"ms\n";
253 std::cout <<
" * time for building quotient: " << quotientBuildTimeInMilliseconds.count() <<
"ms\n";
254 std::cout <<
"------------------------------------------\n";
255 std::cout <<
" * total time: " << totalTimeInMilliseconds.count() <<
"ms\n\n";
259template<
typename ModelType,
typename BlockDataType>
262 std::vector<Block<BlockDataType>*> splitterQueue;
263 std::for_each(partition.getBlocks().begin(), partition.getBlocks().end(), [&](std::unique_ptr<
Block<BlockDataType>>
const& block) {
264 block->data().setSplitter();
265 splitterQueue.push_back(block.get());
269 uint_fast64_t iterations = 0;
270 while (!splitterQueue.empty()) {
276 std::sort(splitterQueue.begin(), splitterQueue.end(),
279 splitterQueue.pop_back();
280 splitter->
data().setSplitter(
false);
283 refinePartitionBasedOnSplitter(*splitter, splitterQueue);
286 std::cout <<
"Performed " << iterations <<
" iterations of partition refinement before abort.\n";
287 STORM_LOG_THROW(
false, storm::exceptions::AbortException,
"Aborted in bisimulation computation.");
293template<
typename ModelType,
typename BlockDataType>
295 STORM_LOG_THROW(this->quotient !=
nullptr, storm::exceptions::IllegalFunctionCallException,
296 "Unable to retrieve quotient model from bisimulation decomposition, because it was not built.");
297 return this->quotient;
300template<
typename ModelType,
typename BlockDataType>
302 auto const& rewardModel = model.getUniqueRewardModel();
303 if (rewardModel.hasStateRewards()) {
304 this->splitInitialPartitionBasedOnRewards(rewardModel.getStateRewardVector());
306 if (rewardModel.hasStateActionRewards()) {
307 if (model.isNondeterministicModel()) {
308 std::vector<std::set<ValueType>> actionRewards;
309 actionRewards.reserve(model.getNumberOfStates());
311 std::set<ValueType> rewardsAtState;
312 for (
auto choice = model.getTransitionMatrix().getRowGroupIndices()[state];
313 choice < model.getTransitionMatrix().getRowGroupIndices()[state + 1]; ++choice) {
314 rewardsAtState.insert(rewardModel.getStateActionReward(choice));
316 actionRewards.push_back(std::move(rewardsAtState));
318 this->splitInitialPartitionBasedOnActionRewards(actionRewards);
320 this->splitInitialPartitionBasedOnRewards(rewardModel.getStateActionRewardVector());
325template<
typename ModelType,
typename BlockDataType>
328 return rewardVector[a] < rewardVector[b];
332template<
typename ModelType,
typename BlockDataType>
335 return actionRewards[a] < actionRewards[b];
339template<
typename ModelType,
typename BlockDataType>
343 for (
auto const& label : options.respectedAtomicPropositions.get()) {
344 if (label ==
"init") {
347 partition.splitStates(model.getStates(label));
352 if (options.getKeepRewards() && model.hasRewardModel()) {
353 this->splitInitialPartitionBasedOnRewards();
357template<
typename ModelType,
typename BlockDataType>
359 std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = this->getStatesWithProbability01();
361 boost::optional<storm::storage::sparse::state_type> representativePsiState;
362 if (!options.psiStates.get().empty()) {
363 representativePsiState = *options.psiStates.get().begin();
367 model.getNumberOfStates(), statesWithProbability01.first,
368 options.getBounded() || options.getKeepRewards() ? options.psiStates.get() : statesWithProbability01.second, representativePsiState);
372 if (options.getKeepRewards() && model.hasRewardModel()) {
373 this->splitInitialPartitionBasedOnRewards();
377template<
typename ModelType,
typename BlockDataType>
382template<
typename ModelType,
typename BlockDataType>
386 this->blocks.resize(partition.size());
387 for (
auto const& blockPtr : partition.getBlocks()) {
389 partition.sortBlock(*blockPtr);
392 this->blocks[blockPtr->getId()] =
block_type(partition.begin(*blockPtr), partition.end(*blockPtr),
true);
This class is the superclass of all decompositions of a sparse model into its bisimulation quotient.
virtual void buildQuotient()=0
Builds the quotient model based on the previously computed equivalence classes (stored in the blocks ...
std::shared_ptr< ModelType > getQuotient() const
Retrieves the quotient of the model under the computed bisimulation.
void performPartitionRefinement()
Performs the partition refinement on the model and thereby computes the equivalence classes under str...
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 equivalance classes of a bisimulation.
void computeBisimulationDecomposition()
Computes the decomposition of the model into bisimulation equivalence classes.
void extractDecompositionBlocks()
Constructs the blocks of the decomposition object based on the current partition.
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()
A function that can initialize auxiliary data structures.
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()
bool isTerminate()
Check whether the program should terminate (due to some abort signal).
void preserveFormula(storm::logic::Formula const &formula)
Changes the options in a way that the given formula is preserved.
boost::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...
bool getKeepRewards() const
BisimulationType getType() const
bool getDiscounted() const