Storm 1.11.1.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
model-handling.h
Go to the documentation of this file.
1#pragma once
2
3#include <filesystem>
4#include <type_traits>
5
6#include "storm/api/storm.h"
7
8#include "AutomaticSettings.h"
16#include "storm/io/file.h"
36#include "storm/storage/Qvbs.h"
47
48namespace storm {
49namespace cli {
50
52 // The symbolic model description.
53 boost::optional<storm::storage::SymbolicModelDescription> model;
54
55 // The original properties to check.
56 std::vector<storm::jani::Property> properties;
57
58 // The preprocessed properties to check (in case they needed amendment).
59 boost::optional<std::vector<storm::jani::Property>> preprocessedProperties;
60};
61
63 auto buildSettings = storm::settings::getModule<storm::settings::modules::BuildSettings>();
64 if (ioSettings.isPrismOrJaniInputSet()) {
65 storm::utility::Stopwatch modelParsingWatch(true);
66 if (ioSettings.isPrismInputSet()) {
67 input.model =
68 storm::api::parseProgram(ioSettings.getPrismInputFilename(), buildSettings.isPrismCompatibilityEnabled(), !buildSettings.isNoSimplifySet());
69 } else {
70 boost::optional<std::vector<std::string>> propertyFilter;
71 if (ioSettings.isJaniPropertiesSet()) {
72 if (ioSettings.areJaniPropertiesSelected()) {
73 propertyFilter = ioSettings.getSelectedJaniProperties();
74 } else {
75 propertyFilter = boost::none;
76 }
77 } else {
78 propertyFilter = std::vector<std::string>();
79 }
80 auto janiInput = storm::api::parseJaniModel(ioSettings.getJaniInputFilename(), propertyFilter);
81 input.model = std::move(janiInput.first);
82 if (ioSettings.isJaniPropertiesSet()) {
83 input.properties = std::move(janiInput.second);
84 }
85 }
86 modelParsingWatch.stop();
87 STORM_PRINT("Time for model input parsing: " << modelParsingWatch << ".\n\n");
88 }
89}
90
92 boost::optional<std::set<std::string>> const& propertyFilter) {
93 if (ioSettings.isPropertySet()) {
94 std::vector<storm::jani::Property> newProperties;
95 if (input.model) {
96 newProperties = storm::api::parsePropertiesForSymbolicModelDescription(ioSettings.getProperty(), input.model.get(), propertyFilter);
97 } else {
98 newProperties = storm::api::parseProperties(ioSettings.getProperty(), propertyFilter);
99 }
100
101 input.properties.insert(input.properties.end(), newProperties.begin(), newProperties.end());
102 }
103}
104
106 // Parse the model input
107 SymbolicInput input;
108 storm::storage::QvbsBenchmark benchmark(ioSettings.getQvbsModelName());
109 STORM_PRINT_AND_LOG(benchmark.getInfo(ioSettings.getQvbsInstanceIndex(), ioSettings.getQvbsPropertyFilter()));
110 storm::utility::Stopwatch modelParsingWatch(true);
111 auto janiInput = storm::api::parseJaniModel(benchmark.getJaniFile(ioSettings.getQvbsInstanceIndex()), ioSettings.getQvbsPropertyFilter());
112 input.model = std::move(janiInput.first);
113 input.properties = std::move(janiInput.second);
114 modelParsingWatch.stop();
115 STORM_PRINT("Time for model input parsing: " << modelParsingWatch << ".\n\n");
116
117 // Parse additional properties
118 boost::optional<std::set<std::string>> propertyFilter = storm::api::parsePropertyFilter(ioSettings.getPropertyFilter());
119 parseProperties(ioSettings, input, propertyFilter);
120
121 // Substitute constant definitions
122 auto constantDefinitions = input.model.get().parseConstantDefinitions(benchmark.getConstantDefinition(ioSettings.getQvbsInstanceIndex()));
123 input.model = input.model.get().preprocess(constantDefinitions);
124 if (!input.properties.empty()) {
125 input.properties = storm::api::substituteConstantsInProperties(input.properties, constantDefinitions);
126 }
127
128 return input;
129}
130
132 auto ioSettings = storm::settings::getModule<storm::settings::modules::IOSettings>();
133 if (ioSettings.isQvbsInputSet()) {
134 return parseSymbolicInputQvbs(ioSettings);
135 } else {
136 // Parse the property filter, if any is given.
137 boost::optional<std::set<std::string>> propertyFilter = storm::api::parsePropertyFilter(ioSettings.getPropertyFilter());
138
139 SymbolicInput input;
140 parseSymbolicModelDescription(ioSettings, input);
141 parseProperties(ioSettings, input, propertyFilter);
142 return input;
143 }
144}
145
147 // The engine to use
149
150 // If set, bisimulation will be applied.
152
153 // If set, a transformation to Jani will be enforced
155
156 // Which data type is to be used for numbers ...
158 ValueType buildValueType; // ... during model building
159 ValueType verificationValueType; // ... during model verification
160
161 // The Dd library to be used
163
164 // The environment used during model checking
166
167 // A flag which is set to true, if the settings were detected to be compatible.
168 // If this is false, it could be that the query can not be handled.
170};
171
173 auto hints = storm::settings::getModule<storm::settings::modules::HintSettings>();
174
175 STORM_LOG_THROW(input.model.is_initialized(), storm::exceptions::InvalidArgumentException, "Automatic engine requires a JANI input model.");
176 STORM_LOG_THROW(input.model->isJaniModel(), storm::exceptions::InvalidArgumentException, "Automatic engine requires a JANI input model.");
177 std::vector<storm::jani::Property> const& properties =
178 input.preprocessedProperties.is_initialized() ? input.preprocessedProperties.get() : input.properties;
179 STORM_LOG_THROW(!properties.empty(), storm::exceptions::InvalidArgumentException, "Automatic engine requires a property.");
180 STORM_LOG_WARN_COND(properties.size() == 1,
181 "Automatic engine does not support decisions based on multiple properties. Only the first property will be considered.");
182
184 if (hints.isNumberStatesSet()) {
185 as.predict(input.model->asJaniModel(), properties.front(), hints.getNumberStates());
186 } else {
187 as.predict(input.model->asJaniModel(), properties.front());
188 }
189
190 mpi.engine = as.getEngine();
191 if (as.enableBisimulation()) {
192 mpi.applyBisimulation = true;
193 }
196 }
197 STORM_PRINT_AND_LOG("Automatic engine picked the following settings: \n"
198 << "\tengine=" << mpi.engine << std::boolalpha << "\t bisimulation=" << mpi.applyBisimulation
199 << "\t exact=" << (mpi.verificationValueType != ModelProcessingInformation::ValueType::FinitePrecision) << std::noboolalpha << '\n');
200}
201
208 std::shared_ptr<SymbolicInput> const& transformedJaniInput = nullptr) {
210 auto ioSettings = storm::settings::getModule<storm::settings::modules::IOSettings>();
211 auto coreSettings = storm::settings::getModule<storm::settings::modules::CoreSettings>();
212 auto generalSettings = storm::settings::getModule<storm::settings::modules::GeneralSettings>();
213 auto bisimulationSettings = storm::settings::getModule<storm::settings::modules::BisimulationSettings>();
214
215 // Set the engine.
216 mpi.engine = coreSettings.getEngine();
217
218 // Set whether bisimulation is to be used.
219 mpi.applyBisimulation = generalSettings.isBisimulationSet();
220
221 // Set the value type used for numeric values
222 if (generalSettings.isParametricSet()) {
224 } else if (generalSettings.isExactSet()) {
226 } else {
228 }
229 auto originalVerificationValueType = mpi.verificationValueType;
230
231 // Since the remaining settings could depend on the ones above, we need apply the automatic engine now.
232 bool useAutomatic = input.model.is_initialized() && mpi.engine == storm::utility::Engine::Automatic;
233 if (useAutomatic) {
234 if (input.model->isJaniModel()) {
235 // This can potentially overwrite the settings above, but will not overwrite settings that were explicitly set by the user (e.g. we will not disable
236 // bisimulation or disable exact arithmetic)
238 } else {
239 // Transform Prism to jani first
240 STORM_LOG_ASSERT(input.model->isPrismProgram(), "Unexpected type of input.");
241 SymbolicInput janiInput;
242 janiInput.properties = input.properties;
243 storm::prism::Program const& prog = input.model.get().asPrismProgram();
244 auto modelAndProperties = prog.toJani(input.preprocessedProperties.is_initialized() ? input.preprocessedProperties.get() : input.properties);
245 janiInput.model = modelAndProperties.first;
246 if (!modelAndProperties.second.empty()) {
247 janiInput.preprocessedProperties = std::move(modelAndProperties.second);
248 }
249 // This can potentially overwrite the settings above, but will not overwrite settings that were explicitly set by the user (e.g. we will not disable
250 // bisimulation or disable exact arithmetic)
252 if (transformedJaniInput) {
253 // We cache the transformation result.
254 *transformedJaniInput = std::move(janiInput);
255 }
256 }
257 }
258
259 // Check whether these settings are compatible with the provided input.
260 if (input.model) {
261 auto checkCompatibleSettings = [&mpi, &input] {
262 switch (mpi.verificationValueType) {
265 mpi.engine, input.preprocessedProperties.is_initialized() ? input.preprocessedProperties.get() : input.properties, input.model.get());
268 mpi.engine, input.preprocessedProperties.is_initialized() ? input.preprocessedProperties.get() : input.properties, input.model.get());
269 break;
272 mpi.engine, input.preprocessedProperties.is_initialized() ? input.preprocessedProperties.get() : input.properties, input.model.get());
273 }
274 return false;
275 };
276 mpi.isCompatible = checkCompatibleSettings();
277 if (!mpi.isCompatible) {
278 if (useAutomatic) {
280 STORM_LOG_WARN("The settings picked by the automatic engine (engine="
281 << mpi.engine << ", bisim=" << mpi.applyBisimulation << ", exact=" << useExact
282 << ") are incompatible with this model. Falling back to default settings.");
284 mpi.applyBisimulation = false;
285 mpi.verificationValueType = originalVerificationValueType;
286 // Retry check with new settings
287 mpi.isCompatible = checkCompatibleSettings();
288 }
289 }
290 } else {
291 // If there is no input model, nothing has to be done, actually
292 mpi.isCompatible = true;
293 }
294
295 // Set whether a transformation to jani is required or necessary
296 mpi.transformToJani = ioSettings.isPrismToJaniSet();
297 if (input.model) {
298 auto builderType = storm::utility::getBuilderType(mpi.engine);
299 bool transformToJaniForDdMA = (builderType == storm::builder::BuilderType::Dd) &&
300 (input.model->getModelType() == storm::storage::SymbolicModelDescription::ModelType::MA) && (!input.model->isJaniModel());
301 STORM_LOG_WARN_COND(mpi.transformToJani || !transformToJaniForDdMA,
302 "Dd-based model builder for Markov Automata is only available for JANI models, automatically converting the input model.");
303 mpi.transformToJani |= transformToJaniForDdMA;
304 }
305
306 // Set the Valuetype used during model building
308 if (bisimulationSettings.useExactArithmeticInDdBisimulation()) {
312 }
313 } else {
314 STORM_LOG_WARN("Requested using exact arithmetic in Dd bisimulation but no dd bisimulation is applied.");
315 }
316 }
317
318 // Set the Dd library
319 mpi.ddType = coreSettings.getDdLibraryType();
320 if (mpi.ddType == storm::dd::DdType::CUDD && coreSettings.isDdLibraryTypeSetFromDefaultValue()) {
323 STORM_LOG_INFO("Switching to DD library sylvan to allow for rational arithmetic.");
325 }
326 }
327 return mpi;
328}
329
330auto castAndApply(std::shared_ptr<storm::models::ModelBase> const& model, auto const& callback) {
331 STORM_LOG_ASSERT(model, "Tried to cast a model that does not exist.");
332
333 // Helper to actually perform the cast once value type and model representation type is known
334 auto castAndApplyImpl = [&model, &callback]<typename TargetModelType> {
335 auto res = model->template as<TargetModelType>();
336 STORM_LOG_ASSERT(res, "Casting model pointer failed unexpectedly.");
337 return callback(res);
338 };
339
340 // Helper to branch on type of model representation
341 auto castAndApplyVT = [&]<typename ValueType> {
342 if (model->isSparseModel()) {
343 return castAndApplyImpl.template operator()<storm::models::sparse::Model<ValueType>>();
344 } else {
345 auto ddType = model->getDdType();
346 STORM_LOG_ASSERT(model->isSymbolicModel() && ddType.has_value(), "Unexpected model representation");
347 if constexpr (storm::IsIntervalType<ValueType>) {
348 // Avoiding a couple of unnecessary template instantiations
349 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Symbolic interval models are currently not supported.");
350 } else {
351 using enum storm::dd::DdType;
352 if (*ddType == CUDD) {
353 if constexpr (std::is_same_v<ValueType, double>) {
354 return castAndApplyImpl.template operator()<storm::models::symbolic::Model<CUDD, ValueType>>();
355 }
356 }
357 STORM_LOG_ASSERT(*ddType == Sylvan, "Unexpected Dd type");
358 return castAndApplyImpl.template operator()<storm::models::symbolic::Model<Sylvan, ValueType>>();
359 }
360 }
361 };
362
363 // branch on type of value representation
364 if (model->supportsParameters()) {
365 return castAndApplyVT.template operator()<storm::RationalFunction>();
366 } else if (model->isExact()) {
367 return castAndApplyVT.template operator()<storm::RationalNumber>();
368 } else if (model->supportsUncertainty()) {
369 return castAndApplyVT.template operator()<storm::Interval>();
370 } else {
371 return castAndApplyVT.template operator()<double>();
372 }
373}
374
377 switch (vt) {
378 case FinitePrecision:
379 return callback.template operator()<double>();
380 case Exact:
381 return callback.template operator()<storm::RationalNumber>();
382 case Parametric:
383 return callback.template operator()<storm::RationalFunction>();
384 }
385 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected value type.");
386}
387
389 using enum storm::dd::DdType;
391 switch (dd) {
392 case CUDD:
393 STORM_LOG_THROW(vt == FinitePrecision, storm::exceptions::UnexpectedException, "Unexpected value type for DD library Cudd.");
394 return callback.template operator()<CUDD, double>();
395 case Sylvan:
396 switch (vt) {
397 case FinitePrecision:
398 return callback.template operator()<Sylvan, double>();
399 case Exact:
400 return callback.template operator()<Sylvan, storm::RationalNumber>();
401 case Parametric:
402 return callback.template operator()<Sylvan, storm::RationalFunction>();
403 }
404 }
405 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected DDType or value type.");
406}
407
408inline void ensureNoUndefinedPropertyConstants(std::vector<storm::jani::Property> const& properties) {
409 // Make sure there are no undefined constants remaining in any property.
410 for (auto const& property : properties) {
411 std::set<storm::expressions::Variable> usedUndefinedConstants = property.getUndefinedConstants();
412 if (!usedUndefinedConstants.empty()) {
413 std::vector<std::string> undefinedConstantsNames;
414 for (auto const& constant : usedUndefinedConstants) {
415 undefinedConstantsNames.emplace_back(constant.getName());
416 }
418 false, storm::exceptions::InvalidArgumentException,
419 "The property '" << property << " still refers to the undefined constants " << boost::algorithm::join(undefinedConstantsNames, ",") << ".");
420 }
421 }
422}
423
424inline std::pair<SymbolicInput, ModelProcessingInformation> preprocessSymbolicInput(SymbolicInput const& input) {
425 auto ioSettings = storm::settings::getModule<storm::settings::modules::IOSettings>();
426
427 SymbolicInput output = input;
428
429 // Preprocess properties (if requested)
430 if (ioSettings.isPropertiesAsMultiSet()) {
431 STORM_LOG_THROW(!input.properties.empty(), storm::exceptions::InvalidArgumentException,
432 "Can not translate properties to multi-objective formula because no properties were specified.");
433 // If we come from storm-pars, the following fails as multiObjectiveSettings are not loaded
434 auto multiObjSettings = storm::settings::getModule<storm::settings::modules::MultiObjectiveSettings>();
435 output.properties = {storm::api::createMultiObjectiveProperty(output.properties, multiObjSettings.isLexicographicModelCheckingSet())};
436 }
437
438 // Substitute constant definitions in symbolic input.
439 std::string constantDefinitionString = ioSettings.getConstantDefinitionString();
440 std::map<storm::expressions::Variable, storm::expressions::Expression> constantDefinitions;
441 if (output.model) {
442 constantDefinitions = output.model.get().parseConstantDefinitions(constantDefinitionString);
443 output.model = output.model.get().preprocess(constantDefinitions);
444 }
445 if (!output.properties.empty()) {
446 output.properties = storm::api::substituteConstantsInProperties(output.properties, constantDefinitions);
447 }
449 auto transformedJani = std::make_shared<SymbolicInput>();
450 ModelProcessingInformation mpi = getModelProcessingInformation(output, transformedJani);
451
452 // Check whether conversion for PRISM to JANI is requested or necessary.
453 if (output.model && output.model.get().isPrismProgram()) {
454 if (mpi.transformToJani) {
455 if (transformedJani->model) {
456 // Use the cached transformation if possible
457 output = std::move(*transformedJani);
458 } else {
459 storm::prism::Program const& model = output.model.get().asPrismProgram();
460 auto modelAndProperties = model.toJani(output.properties);
461
462 output.model = modelAndProperties.first;
463
464 if (!modelAndProperties.second.empty()) {
465 output.preprocessedProperties = std::move(modelAndProperties.second);
466 }
467 }
468 }
469 }
470
471 if (output.model && output.model.get().isJaniModel()) {
473 storm::api::simplifyJaniModel(output.model.get().asJaniModel(), output.properties, supportedFeatures);
474
475 const auto& buildSettings = storm::settings::getModule<storm::settings::modules::BuildSettings>();
476 if (buildSettings.isLocationEliminationSet()) {
477 auto locationHeuristic = buildSettings.getLocationEliminationLocationHeuristic();
478 auto edgesHeuristic = buildSettings.getLocationEliminationEdgesHeuristic();
479 output.model->setModel(storm::jani::JaniLocalEliminator::eliminateAutomatically(output.model.get().asJaniModel(), output.properties,
480 locationHeuristic, edgesHeuristic));
481 }
482 }
483
484 return {output, mpi};
485}
486
487inline void exportSymbolicInput(SymbolicInput const& input) {
488 auto ioSettings = storm::settings::getModule<storm::settings::modules::IOSettings>();
489 if (input.model && input.model.get().isJaniModel()) {
490 storm::storage::SymbolicModelDescription const& model = input.model.get();
491 if (ioSettings.isExportJaniDotSet()) {
492 storm::api::exportJaniModelAsDot(model.asJaniModel(), ioSettings.getExportJaniDotFilename());
493 }
494 }
495}
496
497inline std::vector<std::shared_ptr<storm::logic::Formula const>> createFormulasToRespect(std::vector<storm::jani::Property> const& properties) {
498 std::vector<std::shared_ptr<storm::logic::Formula const>> result = storm::api::extractFormulasFromProperties(properties);
499
500 for (auto const& property : properties) {
501 if (!property.getFilter().getStatesFormula()->isInitialFormula()) {
502 result.push_back(property.getFilter().getStatesFormula());
503 }
504 }
505
506 return result;
507}
508
509template<storm::dd::DdType DdType, typename ValueType>
510std::shared_ptr<storm::models::ModelBase> buildModelDd(SymbolicInput const& input) {
511 if (DdType == storm::dd::DdType::Sylvan) {
512 auto numThreads = storm::settings::getModule<storm::settings::modules::SylvanSettings>().getNumberOfThreads();
513 STORM_PRINT_AND_LOG("Using Sylvan with " << numThreads << " parallel threads.\n");
514 }
515 auto buildSettings = storm::settings::getModule<storm::settings::modules::BuildSettings>();
516 return storm::api::buildSymbolicModel<DdType, ValueType>(input.model.get(), createFormulasToRespect(input.properties), buildSettings.isBuildFullModelSet(),
517 !buildSettings.isApplyNoMaximumProgressAssumptionSet());
518}
519
521 auto buildSettings = storm::settings::getModule<storm::settings::modules::BuildSettings>();
523 options.setBuildChoiceLabels(options.isBuildChoiceLabelsSet() || buildSettings.isBuildChoiceLabelsSet());
524 options.setBuildStateValuations(options.isBuildStateValuationsSet() || buildSettings.isBuildStateValuationsSet());
525 options.setBuildAllLabels(options.isBuildAllLabelsSet() || buildSettings.isBuildAllLabelsSet());
526 options.setBuildObservationValuations(options.isBuildObservationValuationsSet() || buildSettings.isBuildObservationValuationsSet());
527 bool buildChoiceOrigins = options.isBuildChoiceOriginsSet() || buildSettings.isBuildChoiceOriginsSet();
529 auto counterexampleGeneratorSettings = storm::settings::getModule<storm::settings::modules::CounterexampleGeneratorSettings>();
530 if (counterexampleGeneratorSettings.isCounterexampleSet()) {
531 buildChoiceOrigins |= counterexampleGeneratorSettings.isMinimalCommandSetGenerationSet();
532 }
533 }
534 options.setBuildChoiceOrigins(buildChoiceOrigins);
535
536 if (buildSettings.isApplyNoMaximumProgressAssumptionSet()) {
538 }
539
540 if (buildSettings.isExplorationChecksSet()) {
541 options.setExplorationChecks();
542 }
543 options.setReservedBitsForUnboundedVariables(buildSettings.getBitsForUnboundedVariables());
544
545 options.setAddOutOfBoundsState(buildSettings.isBuildOutOfBoundsStateSet());
546 if (buildSettings.isBuildFullModelSet()) {
547 options.clearTerminalStates();
549 options.setBuildAllLabels(true);
550 options.setBuildAllRewardModels(true);
551 }
552
553 if (buildSettings.isAddOverlappingGuardsLabelSet()) {
554 options.setAddOverlappingGuardsLabel(true);
555 }
556
557 auto ioSettings = storm::settings::getModule<storm::settings::modules::IOSettings>();
558 if (ioSettings.isComputeExpectedVisitingTimesSet() || ioSettings.isComputeSteadyStateDistributionSet()) {
559 options.clearTerminalStates();
560 }
561 return options;
562}
563
564template<typename ValueType>
565std::shared_ptr<storm::models::ModelBase> buildModelSparse(SymbolicInput const& input, storm::builder::BuilderOptions const& options) {
566 // Adapt the ValueType if it does not support intervals and the input is an interval model
567 if (!storm::IsIntervalType<ValueType> && input.model.is_initialized() && input.model->isPrismProgram() &&
568 input.model->asPrismProgram().hasIntervalUpdates()) {
569 STORM_LOG_THROW((std::is_same_v<ValueType, storm::IntervalBaseType<storm::Interval>>), storm::exceptions::NotSupportedException,
570 "Can not build interval model for the provided value type.");
571 return storm::api::buildSparseModel<storm::Interval>(input.model.get(), options);
572 } else {
573 return storm::api::buildSparseModel<ValueType>(input.model.get(), options);
574 }
575}
576
577template<typename ValueType>
578std::shared_ptr<storm::models::ModelBase> buildModelExplicit(storm::settings::modules::IOSettings const& ioSettings,
579 storm::settings::modules::BuildSettings const& buildSettings) {
580 std::shared_ptr<storm::models::ModelBase> result;
581 if (ioSettings.isExplicitSet()) {
582 result = storm::api::buildExplicitModel<ValueType>(
583 ioSettings.getTransitionFilename(), ioSettings.getLabelingFilename(),
584 ioSettings.isStateRewardsSet() ? boost::optional<std::string>(ioSettings.getStateRewardsFilename()) : boost::none,
585 ioSettings.isTransitionRewardsSet() ? boost::optional<std::string>(ioSettings.getTransitionRewardsFilename()) : boost::none,
586 ioSettings.isChoiceLabelingSet() ? boost::optional<std::string>(ioSettings.getChoiceLabelingFilename()) : boost::none);
587 } else if (ioSettings.isExplicitDRNSet()) {
589 options.buildChoiceLabeling = buildSettings.isBuildChoiceLabelsSet();
592 if constexpr (std::is_same_v<ValueType, double>) {
593 valueType = Double;
594 } else if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
595 valueType = Rational;
596 } else {
597 static_assert(std::is_same_v<ValueType, storm::RationalFunction>, "Unexpected value type.");
598 valueType = Parametric;
599 }
600 result = storm::api::buildExplicitDRNModel(ioSettings.getExplicitDRNFilename(), valueType, options);
601 } else {
602 STORM_LOG_THROW(ioSettings.isExplicitIMCASet(), storm::exceptions::InvalidSettingsException, "Unexpected explicit model input type.");
603 result = storm::api::buildExplicitIMCAModel<ValueType>(ioSettings.getExplicitIMCAFilename());
604 }
605 return result;
606}
607
608inline std::shared_ptr<storm::models::ModelBase> buildModel(SymbolicInput const& input, storm::settings::modules::IOSettings const& ioSettings,
609 ModelProcessingInformation const& mpi) {
610 storm::utility::Stopwatch modelBuildingWatch(true);
611
612 std::shared_ptr<storm::models::ModelBase> result;
613 if (input.model) {
614 auto builderType = storm::utility::getBuilderType(mpi.engine);
615 if (builderType == storm::builder::BuilderType::Dd) {
616 result = applyDdLibValueType(mpi.ddType, mpi.buildValueType, [&input]<storm::dd::DdType DD, typename VT>() { return buildModelDd<DD, VT>(input); });
617 } else if (builderType == storm::builder::BuilderType::Explicit) {
618 result = applyValueType(mpi.buildValueType, [&input]<typename VT>() {
619 auto options = createBuildOptionsSparseFromSettings(input);
620 return buildModelSparse<VT>(input, options);
621 });
622 }
623 } else if (ioSettings.isExplicitSet() || ioSettings.isExplicitDRNSet() || ioSettings.isExplicitIMCASet()) {
624 STORM_LOG_THROW(mpi.engine == storm::utility::Engine::Sparse, storm::exceptions::InvalidSettingsException,
625 "Can only use sparse engine with explicit input.");
626 result = applyValueType(mpi.buildValueType, [&ioSettings]<typename VT>() {
627 return buildModelExplicit<VT>(ioSettings, storm::settings::getModule<storm::settings::modules::BuildSettings>());
628 });
629 }
630
631 modelBuildingWatch.stop();
632 if (result) {
633 STORM_PRINT("Time for model construction: " << modelBuildingWatch << ".\n\n");
634 }
635
636 return result;
637}
638
639template<typename ValueType>
640std::shared_ptr<storm::models::sparse::Model<ValueType>> preprocessSparseMarkovAutomaton(
641 std::shared_ptr<storm::models::sparse::MarkovAutomaton<ValueType>> const& model) {
642 auto transformationSettings = storm::settings::getModule<storm::settings::modules::TransformationSettings>();
643 auto debugSettings = storm::settings::getModule<storm::settings::modules::DebugSettings>();
644
645 std::shared_ptr<storm::models::sparse::Model<ValueType>> result = model;
646 model->close();
647 STORM_LOG_WARN_COND(!debugSettings.isAdditionalChecksSet() || !model->containsZenoCycle(),
648 "MA contains a Zeno cycle. Model checking results cannot be trusted.");
649
650 if (model->isConvertibleToCtmc()) {
651 STORM_LOG_WARN_COND(false, "MA is convertible to a CTMC, consider using a CTMC instead.");
652 result = model->convertToCtmc();
653 }
654
655 if (transformationSettings.isChainEliminationSet()) {
656 if constexpr (storm::IsIntervalType<ValueType>) {
657 // Currently not enabling this for interval models, as this would require a number of additional template instantiations.
658 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Chain elimination not supported for interval models.");
659 } else {
660 // TODO: we should also transform the properties at this point.
662 transformationSettings.getLabelBehavior())
663 .first;
664 }
665 }
666
667 return result;
668}
669
670template<typename ValueType>
671std::shared_ptr<storm::models::sparse::Model<ValueType>> preprocessSparseModelBisimulation(
672 std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, SymbolicInput const& input,
673 storm::settings::modules::BisimulationSettings const& bisimulationSettings, bool graphPreserving = true) {
675 if (bisimulationSettings.isWeakBisimulationSet()) {
677 }
678
679 STORM_LOG_INFO("Performing bisimulation minimization...");
680 return storm::api::performBisimulationMinimization<ValueType>(model, createFormulasToRespect(input.properties), bisimType, graphPreserving);
681}
682
683template<typename ValueType>
684std::pair<std::shared_ptr<storm::models::ModelBase>, bool> preprocessModel(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model,
685 SymbolicInput const& input, ModelProcessingInformation const& mpi) {
686 STORM_LOG_THROW(mpi.buildValueType == mpi.verificationValueType, storm::exceptions::NotSupportedException,
687 "Converting value types for sparse engine is not supported.");
688 auto bisimulationSettings = storm::settings::getModule<storm::settings::modules::BisimulationSettings>();
689 auto ioSettings = storm::settings::getModule<storm::settings::modules::IOSettings>();
690 auto transformationSettings = storm::settings::getModule<storm::settings::modules::TransformationSettings>();
691
692 std::pair<std::shared_ptr<storm::models::sparse::Model<ValueType>>, bool> result = std::make_pair(model, false);
693
694 if (auto order = transformationSettings.getModelPermutation(); order.has_value()) {
695 auto seed = transformationSettings.getModelPermutationSeed();
696 STORM_PRINT_AND_LOG("Permuting model states using " << storm::utility::permutation::orderKindtoString(order.value()) << " order"
697 << (seed.has_value() ? " with seed " + std::to_string(seed.value()) : "") << ".\n");
698 result.first = storm::api::permuteModelStates(result.first, order.value(), seed);
699 result.second = true;
700 STORM_PRINT_AND_LOG("Transition matrix hash after permuting: " << result.first->getTransitionMatrix().hash() << ".\n");
701 }
702
703 if (result.first->isOfType(storm::models::ModelType::MarkovAutomaton)) {
705 result.second = true;
706 }
707
708 if (mpi.applyBisimulation) {
709 if constexpr (storm::IsIntervalType<ValueType>) {
710 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Bisimulation not supported for interval models.");
711 } else {
712 result.first = preprocessSparseModelBisimulation(result.first, input, bisimulationSettings);
713 result.second = true;
714 }
715 }
716
717 if (transformationSettings.isToDiscreteTimeModelSet()) {
718 if constexpr (storm::IsIntervalType<ValueType>) {
719 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Transformation to discrete time model not supported for interval models.");
720 } else {
721 // TODO: we should also transform the properties at this point.
723 !model->hasRewardModel("_time"),
724 "Scheduled transformation to discrete time model, but a reward model named '_time' is already present in this model. We might take "
725 "the wrong reward model later.");
726 result.first =
728 .first;
729 result.second = true;
730 }
731 }
732
733 if (transformationSettings.isToNondeterministicModelSet()) {
734 result.first = storm::api::transformToNondeterministicModel<ValueType>(std::move(*result.first));
735 result.second = true;
736 }
737
738 return result;
739}
740
741template<typename ValueType>
742void exportModel(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, SymbolicInput const& input) {
743 auto ioSettings = storm::settings::getModule<storm::settings::modules::IOSettings>();
744
745 if (ioSettings.isExportBuildSet()) {
746 storm::utility::Stopwatch modelExportWatch;
747 modelExportWatch.start();
748 STORM_PRINT("\nExporting model to '" << ioSettings.getExportBuildFilename() << "'.\n");
749 switch (ioSettings.getExportBuildFormat()) {
751 storm::api::exportSparseModelAsDot(model, ioSettings.getExportBuildFilename(), ioSettings.getExportDotMaxWidth());
752 break;
755 options.allowPlaceholders = !ioSettings.isExplicitExportPlaceholdersDisabled();
756 options.compression = ioSettings.getCompressionMode();
757 if (ioSettings.isExportDigitsSet()) {
758 options.outputPrecision = ioSettings.getExportDigits();
759 }
760 storm::api::exportSparseModelAsDrn(model, ioSettings.getExportBuildFilename(), options,
761 input.model ? input.model.get().getParameterNames() : std::vector<std::string>());
762 break;
763 }
765 storm::api::exportSparseModelAsJson(model, ioSettings.getExportBuildFilename());
766 break;
767 default:
768 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException,
769 "Exporting sparse models in " << storm::io::toString(ioSettings.getExportBuildFormat()) << " format is not supported.");
770 }
771 modelExportWatch.stop();
772 STORM_PRINT("Time for model export: " << modelExportWatch << ".\n\n");
773 }
774
775 // TODO: The following options are depreciated and shall be removed at some point:
776
777 if (ioSettings.isExportExplicitSet()) {
778 storm::api::exportSparseModelAsDrn(model, ioSettings.getExportExplicitFilename(),
779 input.model ? input.model.get().getParameterNames() : std::vector<std::string>(),
780 !ioSettings.isExplicitExportPlaceholdersDisabled());
781 }
782
783 if (ioSettings.isExportDdSet()) {
784 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Exporting in drdd format is only supported for DDs.");
785 }
786
787 if (ioSettings.isExportDotSet()) {
788 storm::api::exportSparseModelAsDot(model, ioSettings.getExportDotFilename(), ioSettings.getExportDotMaxWidth());
789 }
790}
791
792template<storm::dd::DdType DdType, typename ValueType>
794 auto ioSettings = storm::settings::getModule<storm::settings::modules::IOSettings>();
795
796 if (ioSettings.isExportBuildSet()) {
797 switch (ioSettings.getExportBuildFormat()) {
799 storm::api::exportSymbolicModelAsDot(model, ioSettings.getExportBuildFilename());
800 break;
802 storm::api::exportSymbolicModelAsDrdd(model, ioSettings.getExportBuildFilename());
803 break;
804 default:
805 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException,
806 "Exporting symbolic models in " << storm::io::toString(ioSettings.getExportBuildFormat()) << " format is not supported.");
807 }
808 }
809
810 // TODO: The following options are depreciated and shall be removed at some point:
811
812 if (ioSettings.isExportExplicitSet()) {
813 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Exporting in drn format is only supported for sparse models.");
814 }
815
816 if (ioSettings.isExportDdSet()) {
817 storm::api::exportSymbolicModelAsDrdd(model, ioSettings.getExportDdFilename());
818 }
819
820 if (ioSettings.isExportDotSet()) {
821 storm::api::exportSymbolicModelAsDot(model, ioSettings.getExportDotFilename());
822 }
823}
824
825template<storm::dd::DdType DdType, typename ValueType>
826typename std::enable_if<DdType != storm::dd::DdType::Sylvan && !std::is_same<ValueType, double>::value, std::shared_ptr<storm::models::Model<ValueType>>>::type
828 return model;
829}
830
831template<storm::dd::DdType DdType, typename ValueType>
832typename std::enable_if<DdType == storm::dd::DdType::Sylvan || std::is_same<ValueType, double>::value, std::shared_ptr<storm::models::Model<ValueType>>>::type
834 auto ma = model->template as<storm::models::symbolic::MarkovAutomaton<DdType, ValueType>>();
835 if (!ma->isClosed()) {
836 return std::make_shared<storm::models::symbolic::MarkovAutomaton<DdType, ValueType>>(ma->close());
837 } else {
838 return model;
839 }
840}
841
842template<storm::dd::DdType DdType, typename ValueType, typename ExportValueType = ValueType>
843std::shared_ptr<storm::models::Model<ExportValueType>> preprocessDdModelBisimulation(
844 std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& model, SymbolicInput const& input,
845 storm::settings::modules::BisimulationSettings const& bisimulationSettings, ModelProcessingInformation const& mpi) {
846 STORM_LOG_WARN_COND(!bisimulationSettings.isWeakBisimulationSet(),
847 "Weak bisimulation is currently not supported on DDs. Falling back to strong bisimulation.");
848
849 auto quotientFormat = bisimulationSettings.getQuotientFormat();
851 bisimulationSettings.isQuotientFormatSetFromDefaultValue()) {
852 STORM_LOG_INFO("Setting bisimulation quotient format to 'sparse'.");
854 }
855 STORM_LOG_INFO("Performing bisimulation minimization...");
856 return storm::api::performBisimulationMinimization<DdType, ValueType, ExportValueType>(
857 model, createFormulasToRespect(input.properties), storm::storage::BisimulationType::Strong, bisimulationSettings.getSignatureMode(), quotientFormat);
858}
859
860template<typename ExportValueType, storm::dd::DdType DdType, typename ValueType>
861std::pair<std::shared_ptr<storm::models::ModelBase>, bool> preprocessDdModelImpl(
862 std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& model, SymbolicInput const& input, ModelProcessingInformation const& mpi) {
863 auto bisimulationSettings = storm::settings::getModule<storm::settings::modules::BisimulationSettings>();
864 std::pair<std::shared_ptr<storm::models::Model<ValueType>>, bool> intermediateResult = std::make_pair(model, false);
865
866 if (model->isOfType(storm::models::ModelType::MarkovAutomaton)) {
867 intermediateResult.first = preprocessDdMarkovAutomaton(intermediateResult.first->template as<storm::models::symbolic::Model<DdType, ValueType>>());
868 intermediateResult.second = true;
869 }
870
871 std::unique_ptr<std::pair<std::shared_ptr<storm::models::Model<ExportValueType>>, bool>> result;
872 auto symbolicModel = intermediateResult.first->template as<storm::models::symbolic::Model<DdType, ValueType>>();
873 if (mpi.applyBisimulation) {
874 std::shared_ptr<storm::models::Model<ExportValueType>> newModel =
875 preprocessDdModelBisimulation<DdType, ValueType, ExportValueType>(symbolicModel, input, bisimulationSettings, mpi);
876 result = std::make_unique<std::pair<std::shared_ptr<storm::models::Model<ExportValueType>>, bool>>(newModel, true);
877 } else {
878 result = std::make_unique<std::pair<std::shared_ptr<storm::models::Model<ExportValueType>>, bool>>(
879 symbolicModel->template toValueType<ExportValueType>(), !std::is_same<ValueType, ExportValueType>::value);
880 }
881
882 if (result && result->first->isSymbolicModel() && mpi.engine == storm::utility::Engine::DdSparse) {
883 // Mark as changed.
884 result->second = true;
885
886 std::shared_ptr<storm::models::symbolic::Model<DdType, ExportValueType>> symbolicModel =
887 result->first->template as<storm::models::symbolic::Model<DdType, ExportValueType>>();
888 std::vector<std::shared_ptr<storm::logic::Formula const>> formulas;
889 for (auto const& property : input.properties) {
890 formulas.emplace_back(property.getRawFormula());
891 }
892 result->first = storm::api::transformSymbolicToSparseModel(symbolicModel, formulas);
893 STORM_LOG_THROW(result, storm::exceptions::NotSupportedException, "The translation to a sparse model is not supported for the given model type.");
894 }
895
896 return *result;
897}
898
899template<storm::dd::DdType DdType, typename ValueType>
900std::pair<std::shared_ptr<storm::models::ModelBase>, bool> preprocessModel(std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& model,
901 SymbolicInput const& input, ModelProcessingInformation const& mpi) {
902 return applyValueType(mpi.verificationValueType, [&model, &input, &mpi]<typename VT>() -> std::pair<std::shared_ptr<storm::models::ModelBase>, bool> {
903 // To safe a few template instantiations, we only consider those combinations that actually occur in the CLI
904 if constexpr (std::is_same_v<ValueType, VT> ||
905 (DdType == storm::dd::DdType::Sylvan && std::is_same_v<ValueType, storm::RationalNumber> && std::is_same_v<VT, double>)) {
906 return preprocessDdModelImpl<VT>(model, input, mpi);
907 } else {
908 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException,
909 "Unexpected combination of DD library, build value type, and verification value type.");
910 }
911 });
912}
913
915 STORM_PRINT("Computing counterexample for property " << *property.getRawFormula() << " ...\n");
916}
917
918inline void printCounterexample(std::shared_ptr<storm::counterexamples::Counterexample> const& counterexample, storm::utility::Stopwatch* watch = nullptr) {
919 if (counterexample) {
920 STORM_PRINT(*counterexample << '\n');
921 if (watch) {
922 STORM_PRINT("Time for computation: " << *watch << ".\n");
923 }
924 } else {
925 STORM_PRINT(" failed.\n");
926 }
927}
928
929template<typename ModelType>
930 requires(!std::derived_from<ModelType, storm::models::sparse::Model<double>>)
931inline void generateCounterexamples(std::shared_ptr<ModelType> const&, SymbolicInput const&) {
932 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Counterexample generation is not supported for this data-type.");
933}
934
935template<typename ModelType>
936 requires(std::derived_from<ModelType, storm::models::sparse::Model<double>>)
937inline void generateCounterexamples(std::shared_ptr<ModelType> const& sparseModel, SymbolicInput const& input) {
938 using ValueType = typename ModelType::ValueType;
939
940 for (auto& rewModel : sparseModel->getRewardModels()) {
941 rewModel.second.reduceToStateBasedRewards(sparseModel->getTransitionMatrix(), true);
942 }
943
944 STORM_LOG_THROW(sparseModel->isOfType(storm::models::ModelType::Dtmc) || sparseModel->isOfType(storm::models::ModelType::Mdp),
945 storm::exceptions::NotSupportedException, "Counterexample is currently only supported for discrete-time models.");
946
947 auto counterexampleSettings = storm::settings::getModule<storm::settings::modules::CounterexampleGeneratorSettings>();
948 if (counterexampleSettings.isMinimalCommandSetGenerationSet()) {
949 bool useMilp = counterexampleSettings.isUseMilpBasedMinimalCommandSetGenerationSet();
950 for (auto const& property : input.properties) {
951 std::shared_ptr<storm::counterexamples::Counterexample> counterexample;
953 storm::utility::Stopwatch watch(true);
954 if (useMilp) {
955 STORM_LOG_THROW(sparseModel->isOfType(storm::models::ModelType::Mdp), storm::exceptions::NotSupportedException,
956 "Counterexample generation using MILP is currently only supported for MDPs.");
958 input.model.get(), sparseModel->template as<storm::models::sparse::Mdp<ValueType>>(), property.getRawFormula());
959 } else {
960 STORM_LOG_THROW(sparseModel->isOfType(storm::models::ModelType::Dtmc) || sparseModel->isOfType(storm::models::ModelType::Mdp),
961 storm::exceptions::NotSupportedException,
962 "Counterexample generation using MaxSAT is currently only supported for discrete-time models.");
963
964 if (sparseModel->isOfType(storm::models::ModelType::Dtmc)) {
966 input.model.get(), sparseModel->template as<storm::models::sparse::Dtmc<ValueType>>(), property.getRawFormula());
967 } else {
969 input.model.get(), sparseModel->template as<storm::models::sparse::Mdp<ValueType>>(), property.getRawFormula());
970 }
971 }
972 watch.stop();
973 printCounterexample(counterexample, &watch);
974 }
975 } else if (counterexampleSettings.isShortestPathGenerationSet()) {
976 for (auto const& property : input.properties) {
977 std::shared_ptr<storm::counterexamples::Counterexample> counterexample;
979 storm::utility::Stopwatch watch(true);
980 STORM_LOG_THROW(sparseModel->isOfType(storm::models::ModelType::Dtmc), storm::exceptions::NotSupportedException,
981 "Counterexample generation using shortest paths is currently only supported for DTMCs.");
983 property.getRawFormula(), counterexampleSettings.getShortestPathMaxK());
984 watch.stop();
985 printCounterexample(counterexample, &watch);
986 }
987 } else {
988 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "The selected counterexample formalism is unsupported.");
989 }
990}
991
992template<typename ValueType>
993 requires(!storm::IsIntervalType<ValueType>)
994void printFilteredResult(std::unique_ptr<storm::modelchecker::CheckResult> const& result, storm::modelchecker::FilterType ft) {
995 if (result->isQuantitative()) {
997 STORM_PRINT(*result);
998 } else {
999 ValueType resultValue;
1000 switch (ft) {
1002 resultValue = result->asQuantitativeCheckResult<ValueType>().sum();
1003 break;
1005 resultValue = result->asQuantitativeCheckResult<ValueType>().average();
1006 break;
1008 resultValue = result->asQuantitativeCheckResult<ValueType>().getMin();
1009 break;
1011 resultValue = result->asQuantitativeCheckResult<ValueType>().getMax();
1012 break;
1015 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Outputting states is not supported.");
1019 STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Filter type only defined for qualitative results.");
1020 default:
1021 STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Unhandled filter type.");
1022 }
1024 STORM_PRINT(resultValue << " (approx. " << storm::utility::convertNumber<double>(resultValue) << ")");
1025 } else {
1026 STORM_PRINT(resultValue);
1027 }
1028 }
1029 } else {
1030 switch (ft) {
1032 STORM_PRINT(*result << '\n');
1033 break;
1035 STORM_PRINT(result->asQualitativeCheckResult().existsTrue());
1036 break;
1038 STORM_PRINT(result->asQualitativeCheckResult().forallTrue());
1039 break;
1041 STORM_PRINT(result->asQualitativeCheckResult().count());
1042 break;
1045 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Outputting states is not supported.");
1050 STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Filter type only defined for quantitative results.");
1051 }
1052 }
1053 STORM_PRINT('\n');
1054}
1055
1057 STORM_PRINT("\nModel checking property \"" << property.getName() << "\": " << *property.getRawFormula() << " ...\n");
1058}
1059
1060template<typename ValueType>
1061 requires(!storm::IsIntervalType<ValueType>)
1062void printResult(std::unique_ptr<storm::modelchecker::CheckResult> const& result, storm::logic::Formula const& filterStatesFormula,
1063 storm::modelchecker::FilterType const& filterType, storm::utility::Stopwatch* watch = nullptr) {
1064 if (result) {
1065 std::stringstream ss;
1066 ss << "'" << filterStatesFormula << "'";
1067 STORM_PRINT((storm::utility::resources::isTerminate() ? "Result till abort" : "Result")
1068 << " (for " << (filterStatesFormula.isInitialFormula() ? "initial" : ss.str()) << " states): ");
1069 printFilteredResult<ValueType>(result, filterType);
1070 if (watch) {
1071 STORM_PRINT("Time for model checking: " << *watch << ".\n");
1072 }
1073 } else {
1074 STORM_LOG_ERROR("Property is unsupported by selected engine/settings.\n");
1075 }
1076}
1077
1078template<typename ValueType>
1079void printResult(std::unique_ptr<storm::modelchecker::CheckResult> const& result, storm::jani::Property const& property,
1080 storm::utility::Stopwatch* watch = nullptr) {
1081 printResult<ValueType>(result, *property.getFilter().getStatesFormula(), property.getFilter().getFilterType(), watch);
1082}
1083
1084using VerificationCallbackType = std::function<std::unique_ptr<storm::modelchecker::CheckResult>(std::shared_ptr<storm::logic::Formula const> const& formula,
1085 std::shared_ptr<storm::logic::Formula const> const& states)>;
1086using PostprocessingCallbackType = std::function<void(std::unique_ptr<storm::modelchecker::CheckResult> const&)>;
1087
1089 void operator()(std::unique_ptr<storm::modelchecker::CheckResult> const&) {
1090 // Intentionally left empty.
1091 }
1092};
1093
1100template<typename ValueType>
1101std::unique_ptr<storm::modelchecker::CheckResult> verifyProperty(std::shared_ptr<storm::logic::Formula const> const& formula,
1102 std::shared_ptr<storm::logic::Formula const> const& statesFilter,
1103 VerificationCallbackType const& verificationCallback) {
1104 auto transformationSettings = storm::settings::getModule<storm::settings::modules::TransformationSettings>();
1105
1106 try {
1107 if constexpr (storm::IsIntervalType<ValueType>) {
1108 STORM_LOG_ASSERT(!transformationSettings.isChainEliminationSet() && !transformationSettings.isToNondeterministicModelSet(),
1109 "Unsupported transformation has been invoked.");
1110 return verificationCallback(formula, statesFilter);
1111 }
1112 if (transformationSettings.isChainEliminationSet() && !storm::transformer::NonMarkovianChainTransformer<ValueType>::preservesFormula(*formula)) {
1113 STORM_LOG_WARN("Property is not preserved by elimination of non-markovian states.");
1114 } else if (transformationSettings.isToDiscreteTimeModelSet()) {
1115 auto transformedFormula = storm::api::checkAndTransformContinuousToDiscreteTimeFormula<ValueType>(*formula);
1116 auto transformedStatesFilter = storm::api::checkAndTransformContinuousToDiscreteTimeFormula<ValueType>(*statesFilter);
1117 if (transformedFormula && transformedStatesFilter) {
1118 // invoke verification algorithm on transformed formulas
1119 return verificationCallback(transformedFormula, transformedStatesFilter);
1120 } else {
1121 STORM_LOG_WARN("Property is not preserved by transformation to discrete time model.");
1122 }
1123 } else {
1124 // invoke verification algorithm on given formulas
1125 return verificationCallback(formula, statesFilter);
1126 }
1127 } catch (storm::exceptions::BaseException const& ex) {
1128 STORM_LOG_WARN("Cannot handle property: " << ex.what());
1129 }
1130 return nullptr;
1131}
1132
1139template<typename ValueType>
1140void verifyProperties(SymbolicInput const& input, VerificationCallbackType const& verificationCallback,
1141 PostprocessingCallbackType const& postprocessingCallback = PostprocessingIdentity()) {
1142 auto const& properties = input.preprocessedProperties ? input.preprocessedProperties.get() : input.properties;
1143 for (auto const& property : properties) {
1145 storm::utility::Stopwatch watch(true);
1146 auto result = verifyProperty<ValueType>(property.getRawFormula(), property.getFilter().getStatesFormula(), verificationCallback);
1147 watch.stop();
1148 if (result) {
1149 postprocessingCallback(result);
1150 }
1151 printResult<storm::IntervalBaseType<ValueType>>(result, property, &watch);
1152 }
1153}
1154
1164template<typename ValueType>
1165void computeStateValues(std::string const& description, std::function<std::unique_ptr<storm::modelchecker::CheckResult>()> const& computationCallback,
1166 SymbolicInput const& input, VerificationCallbackType const& verificationCallback,
1167 PostprocessingCallbackType const& postprocessingCallback = PostprocessingIdentity()) {
1168 // First compute the state values for all the states by invoking the computationCallback
1169 storm::utility::Stopwatch watch(true);
1170 STORM_PRINT("\nComputing " << description << " ...\n");
1171 std::unique_ptr<storm::modelchecker::CheckResult> result;
1172 try {
1173 result = computationCallback();
1174 } catch (storm::exceptions::BaseException const& ex) {
1175 STORM_LOG_ERROR("Cannot compute " << description << ": " << ex.what());
1176 }
1177 if (!result) {
1178 STORM_LOG_ERROR("Computation had no result.");
1179 return;
1180 }
1181 // Now process the (potentially filtered) result
1182 if (input.properties.empty()) {
1183 // Do not apply any filtering, consider result for *all* states
1184 postprocessingCallback(result);
1185 printResult<ValueType>(result, *storm::logic::Formula::getTrueFormula(), storm::modelchecker::FilterType::VALUES, &watch);
1186 } else {
1187 // Each property identifies a subset of states to which we restrict (aka filter) the state-value result to
1188 auto const& properties = input.preprocessedProperties ? input.preprocessedProperties.get() : input.properties;
1189 for (uint64_t propertyIndex = 0; propertyIndex < properties.size(); ++propertyIndex) {
1190 auto const& property = properties[propertyIndex];
1191 // As the property serves as filter, it should (a) be qualitative and should (b) not consider a filter itself.
1192 if (!property.getRawFormula()->hasQualitativeResult()) {
1193 STORM_LOG_ERROR("Property '" << *property.getRawFormula()
1194 << "' can not be used for filtering states as it does not have a qualitative result.");
1195 continue;
1196 }
1197
1198 // Invoke verification algorithm on filtering property
1199 auto propertyFilter = verifyProperty<ValueType>(property.getRawFormula(), storm::logic::Formula::getTrueFormula(), verificationCallback);
1200
1201 if (propertyFilter) {
1202 // Filter and process result
1203 std::unique_ptr<storm::modelchecker::CheckResult> filteredResult = result->clone();
1204 filteredResult->filter(propertyFilter->asQualitativeCheckResult());
1205 postprocessingCallback(filteredResult);
1206 printResult<ValueType>(filteredResult, *property.getRawFormula(), property.getFilter().getFilterType(),
1207 propertyIndex == properties.size() - 1 ? &watch : nullptr);
1208 }
1209 }
1210 }
1211}
1212
1213inline std::vector<storm::expressions::Expression> parseConstraints(storm::expressions::ExpressionManager const& expressionManager,
1214 std::string const& constraintsString) {
1215 std::vector<storm::expressions::Expression> constraints;
1216
1217 std::vector<std::string> constraintsAsStrings;
1218 boost::split(constraintsAsStrings, constraintsString, boost::is_any_of(","));
1219
1220 storm::parser::ExpressionParser expressionParser(expressionManager);
1221 std::unordered_map<std::string, storm::expressions::Expression> variableMapping;
1222 for (auto const& variableTypePair : expressionManager) {
1223 variableMapping[variableTypePair.first.getName()] = variableTypePair.first;
1224 }
1225 expressionParser.setIdentifierMapping(variableMapping);
1226
1227 for (auto const& constraintString : constraintsAsStrings) {
1228 if (constraintString.empty()) {
1229 continue;
1230 }
1231
1232 storm::expressions::Expression constraint = expressionParser.parseFromString(constraintString);
1233 STORM_LOG_TRACE("Adding special (user-provided) constraint " << constraint << ".");
1234 constraints.emplace_back(constraint);
1235 }
1236
1237 return constraints;
1238}
1239
1240inline std::vector<std::vector<storm::expressions::Expression>> parseInjectedRefinementPredicates(
1241 storm::expressions::ExpressionManager const& expressionManager, std::string const& refinementPredicatesString) {
1242 std::vector<std::vector<storm::expressions::Expression>> injectedRefinementPredicates;
1243
1244 storm::parser::ExpressionParser expressionParser(expressionManager);
1245 std::unordered_map<std::string, storm::expressions::Expression> variableMapping;
1246 for (auto const& variableTypePair : expressionManager) {
1247 variableMapping[variableTypePair.first.getName()] = variableTypePair.first;
1248 }
1249 expressionParser.setIdentifierMapping(variableMapping);
1250
1251 std::vector<std::string> predicateGroupsAsStrings;
1252 boost::split(predicateGroupsAsStrings, refinementPredicatesString, boost::is_any_of(";"));
1253
1254 if (!predicateGroupsAsStrings.empty()) {
1255 for (auto const& predicateGroupString : predicateGroupsAsStrings) {
1256 if (predicateGroupString.empty()) {
1257 continue;
1258 }
1259
1260 std::vector<std::string> predicatesAsStrings;
1261 boost::split(predicatesAsStrings, predicateGroupString, boost::is_any_of(":"));
1262
1263 if (!predicatesAsStrings.empty()) {
1264 injectedRefinementPredicates.emplace_back();
1265 for (auto const& predicateString : predicatesAsStrings) {
1266 storm::expressions::Expression predicate = expressionParser.parseFromString(predicateString);
1267 STORM_LOG_TRACE("Adding special (user-provided) refinement predicate " << predicateString << ".");
1268 injectedRefinementPredicates.back().emplace_back(predicate);
1269 }
1270
1271 STORM_LOG_THROW(!injectedRefinementPredicates.back().empty(), storm::exceptions::InvalidArgumentException,
1272 "Expecting non-empty list of predicates to inject for each (mentioned) refinement step.");
1273
1274 // Finally reverse the list, because we take the predicates from the back.
1275 std::reverse(injectedRefinementPredicates.back().begin(), injectedRefinementPredicates.back().end());
1276 }
1277 }
1278
1279 // Finally reverse the list, because we take the predicates from the back.
1280 std::reverse(injectedRefinementPredicates.begin(), injectedRefinementPredicates.end());
1281 }
1282
1283 return injectedRefinementPredicates;
1284}
1285
1286template<storm::dd::DdType DdType, typename ValueType>
1288 STORM_LOG_ASSERT(input.model, "Expected symbolic model description.");
1289 storm::settings::modules::AbstractionSettings const& abstractionSettings = storm::settings::getModule<storm::settings::modules::AbstractionSettings>();
1291 parseConstraints(input.model->getManager(), abstractionSettings.getConstraintString()),
1292 parseInjectedRefinementPredicates(input.model->getManager(), abstractionSettings.getInjectedRefinementPredicates()));
1293
1294 verifyProperties<ValueType>(input, [&input, &options, &mpi](std::shared_ptr<storm::logic::Formula const> const& formula,
1295 std::shared_ptr<storm::logic::Formula const> const& states) {
1296 STORM_LOG_THROW(states->isInitialFormula(), storm::exceptions::NotSupportedException, "Abstraction-refinement can only filter initial states.");
1297 return storm::gbar::api::verifyWithAbstractionRefinementEngine<DdType, ValueType>(mpi.env, input.model.get(),
1298 storm::api::createTask<ValueType>(formula, true), options);
1299 });
1300}
1301
1302template<typename ValueType>
1304 STORM_LOG_ASSERT(input.model, "Expected symbolic model description.");
1305 STORM_LOG_THROW((std::is_same<ValueType, double>::value), storm::exceptions::NotSupportedException,
1306 "Exploration does not support other data-types than floating points.");
1307 verifyProperties<ValueType>(
1308 input, [&input, &mpi](std::shared_ptr<storm::logic::Formula const> const& formula, std::shared_ptr<storm::logic::Formula const> const& states) {
1309 STORM_LOG_THROW(states->isInitialFormula(), storm::exceptions::NotSupportedException, "Exploration can only filter initial states.");
1310 return storm::api::verifyWithExplorationEngine<ValueType>(mpi.env, input.model.get(), storm::api::createTask<ValueType>(formula, true));
1311 });
1312}
1313
1314template<typename ValueType>
1315void verifyModel(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& sparseModel, SymbolicInput const& input,
1316 ModelProcessingInformation const& mpi) {
1317 auto const& ioSettings = storm::settings::getModule<storm::settings::modules::IOSettings>();
1318 auto verificationCallback = [&sparseModel, &ioSettings, &mpi](std::shared_ptr<storm::logic::Formula const> const& formula,
1319 std::shared_ptr<storm::logic::Formula const> const& states) {
1320 auto createTask = [&ioSettings](auto const& f, bool onlyInitialStates) {
1321 if constexpr (storm::IsIntervalType<ValueType>) {
1322 STORM_LOG_THROW(ioSettings.isUncertaintyResolutionModeSet(), storm::exceptions::InvalidSettingsException,
1323 "Uncertainty resolution mode required for uncertain (interval) models.");
1324 return storm::api::createTask<ValueType>(f, storm::solver::convert(ioSettings.getUncertaintyResolutionMode()), onlyInitialStates);
1325 } else {
1326 (void)ioSettings; // suppress unused lambda capture warning. [[maybe_unused]] doesn't work for lambda captures.
1327 return storm::api::createTask<ValueType>(f, onlyInitialStates);
1328 }
1329 };
1330 bool const filterForInitialStates = states->isInitialFormula();
1331 auto task = createTask(formula, filterForInitialStates);
1332 if (ioSettings.isExportSchedulerSet()) {
1333 task.setProduceSchedulers(true);
1334 }
1335 std::unique_ptr<storm::modelchecker::CheckResult> result = storm::api::verifyWithSparseEngine<ValueType>(mpi.env, sparseModel, task);
1336
1337 std::unique_ptr<storm::modelchecker::CheckResult> filter;
1338 if (filterForInitialStates) {
1339 filter = std::make_unique<storm::modelchecker::ExplicitQualitativeCheckResult>(sparseModel->getInitialStates());
1340 } else if (!states->isTrueFormula()) { // No need to apply filter if it is the formula 'true'
1341 filter = storm::api::verifyWithSparseEngine<ValueType>(mpi.env, sparseModel, createTask(states, false));
1342 }
1343 if (result && filter) {
1344 result->filter(filter->asQualitativeCheckResult());
1345 }
1346 return result;
1347 };
1348 uint64_t exportCount = 0; // this number will be prepended to the export file name of schedulers and/or check results in case of multiple properties.
1349 auto postprocessingCallback = [&sparseModel, &ioSettings, &input, &exportCount](std::unique_ptr<storm::modelchecker::CheckResult> const& result) {
1350 // Scheduler export
1351 STORM_LOG_WARN_COND(!ioSettings.isExportSchedulerSet() || result->hasScheduler(), "Scheduler requested but could not be generated.");
1352 if (ioSettings.isExportSchedulerSet() && result->hasScheduler()) {
1353 std::filesystem::path schedulerExportPath = ioSettings.getExportSchedulerFilename();
1354 if (exportCount > 0) {
1355 STORM_LOG_WARN("Prepending " << exportCount << " to scheduler file name for this property because there are multiple properties.");
1356 schedulerExportPath.replace_filename(std::to_string(exportCount) + schedulerExportPath.filename().string());
1357 }
1358 STORM_PRINT_AND_LOG("Exporting scheduler ... ");
1359 if (input.model) {
1360 STORM_LOG_WARN_COND(sparseModel->hasStateValuations(),
1361 "No information of state valuations available. The scheduler output will use internal state ids. You might be "
1362 "interested in building the model with state valuations using --buildstateval.");
1364 sparseModel->hasChoiceLabeling() || sparseModel->hasChoiceOrigins(),
1365 "No symbolic choice information is available. The scheduler output will use internal choice ids. You might be interested in "
1366 "building the model with choice labels or choice origins using --buildchoicelab or --buildchoiceorig.");
1367 STORM_LOG_WARN_COND(sparseModel->hasChoiceLabeling() && !sparseModel->hasChoiceOrigins(),
1368 "Only partial choice information is available. You might want to build the model with choice origins using "
1369 "--buildchoicelab or --buildchoiceorig.");
1370 }
1371 if (result->isExplicitQuantitativeCheckResult()) {
1372 if constexpr (storm::IsIntervalType<ValueType>) {
1373 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Scheduler export for interval models is not supported.");
1374 } else {
1375 storm::api::exportScheduler(sparseModel, result->template asExplicitQuantitativeCheckResult<ValueType>().getScheduler(),
1376 schedulerExportPath.string());
1377 }
1378 } else if (result->isExplicitParetoCurveCheckResult()) {
1379 if constexpr (std::is_same_v<ValueType, storm::RationalFunction> || storm::IsIntervalType<ValueType>) {
1380 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Scheduler export for models of this value type is not supported.");
1381 } else {
1382 auto const& paretoRes = result->template asExplicitParetoCurveCheckResult<ValueType>();
1383 storm::api::exportParetoScheduler(sparseModel, paretoRes.getPoints(), paretoRes.getSchedulers(), schedulerExportPath.string());
1384 }
1385 } else {
1386 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Scheduler export not supported for this value type.");
1387 }
1388 }
1389
1390 // Result export
1391 if (ioSettings.isExportCheckResultSet()) {
1392 if constexpr (storm::IsIntervalType<ValueType>) {
1393 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Result export for interval models is not supported.");
1394 } else {
1395 std::filesystem::path resultExportPath = ioSettings.getExportCheckResultFilename();
1396 if (exportCount > 0) {
1397 STORM_LOG_WARN("Prepending " << exportCount << " to result file name for this property because there are multiple properties.");
1398 resultExportPath.replace_filename(std::to_string(exportCount) + resultExportPath.filename().string());
1399 }
1400 STORM_LOG_WARN_COND(sparseModel->hasStateValuations(),
1401 "No information of state valuations available. The result output will use internal state ids. You might be interested in "
1402 "building the model with state valuations using --buildstateval.");
1403 storm::api::exportCheckResultToJson(sparseModel, result, resultExportPath);
1404 }
1405 }
1406 ++exportCount;
1407 };
1408 if (!(ioSettings.isComputeSteadyStateDistributionSet() || ioSettings.isComputeExpectedVisitingTimesSet())) {
1409 verifyProperties<ValueType>(input, verificationCallback, postprocessingCallback);
1410 }
1411 if (ioSettings.isComputeSteadyStateDistributionSet()) {
1412 if constexpr (storm::IsIntervalType<ValueType>) {
1413 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Computing steady state distribution is not supported for interval models.");
1414 } else {
1415 computeStateValues<ValueType>(
1416 "steady-state probabilities",
1417 [&mpi, &sparseModel]() { return storm::api::computeSteadyStateDistributionWithSparseEngine<ValueType>(mpi.env, sparseModel); }, input,
1418 verificationCallback, postprocessingCallback);
1419 }
1420 }
1421 if (ioSettings.isComputeExpectedVisitingTimesSet()) {
1422 if constexpr (storm::IsIntervalType<ValueType>) {
1423 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Computing expected visiting times is not supported for interval models.");
1424 } else {
1425 computeStateValues<ValueType>(
1426 "expected visiting times",
1427 [&mpi, &sparseModel]() { return storm::api::computeExpectedVisitingTimesWithSparseEngine<ValueType>(mpi.env, sparseModel); }, input,
1428 verificationCallback, postprocessingCallback);
1429 }
1430 }
1431}
1432
1433template<storm::dd::DdType DdType, typename ValueType>
1434void verifyWithHybridEngine(std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& symbolicModel, SymbolicInput const& input,
1435 ModelProcessingInformation const& mpi) {
1436 verifyProperties<ValueType>(
1437 input, [&symbolicModel, &mpi](std::shared_ptr<storm::logic::Formula const> const& formula, std::shared_ptr<storm::logic::Formula const> const& states) {
1438 bool filterForInitialStates = states->isInitialFormula();
1439 auto task = storm::api::createTask<ValueType>(formula, filterForInitialStates);
1440
1441 std::unique_ptr<storm::modelchecker::CheckResult> result = storm::api::verifyWithHybridEngine<DdType, ValueType>(mpi.env, symbolicModel, task);
1442
1443 std::unique_ptr<storm::modelchecker::CheckResult> filter;
1444 if (filterForInitialStates) {
1445 filter = std::make_unique<storm::modelchecker::SymbolicQualitativeCheckResult<DdType>>(symbolicModel->getReachableStates(),
1446 symbolicModel->getInitialStates());
1447 } else if (!states->isTrueFormula()) { // No need to apply filter if it is the formula 'true'
1448 filter = storm::api::verifyWithHybridEngine<DdType, ValueType>(mpi.env, symbolicModel, storm::api::createTask<ValueType>(states, false));
1449 }
1450 if (result && filter) {
1451 result->filter(filter->asQualitativeCheckResult());
1452 }
1453 return result;
1454 });
1455}
1456
1457template<storm::dd::DdType DdType, typename ValueType>
1458void verifyWithDdEngine(std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& symbolicModel, SymbolicInput const& input,
1459 ModelProcessingInformation const& mpi) {
1460 verifyProperties<ValueType>(
1461 input, [&symbolicModel, &mpi](std::shared_ptr<storm::logic::Formula const> const& formula, std::shared_ptr<storm::logic::Formula const> const& states) {
1462 bool filterForInitialStates = states->isInitialFormula();
1463 auto task = storm::api::createTask<ValueType>(formula, filterForInitialStates);
1464
1465 std::unique_ptr<storm::modelchecker::CheckResult> result =
1466 storm::api::verifyWithDdEngine<DdType, ValueType>(mpi.env, symbolicModel, storm::api::createTask<ValueType>(formula, true));
1467
1468 std::unique_ptr<storm::modelchecker::CheckResult> filter;
1469 if (filterForInitialStates) {
1470 filter = std::make_unique<storm::modelchecker::SymbolicQualitativeCheckResult<DdType>>(symbolicModel->getReachableStates(),
1471 symbolicModel->getInitialStates());
1472 } else if (!states->isTrueFormula()) { // No need to apply filter if it is the formula 'true'
1473 filter = storm::api::verifyWithDdEngine<DdType, ValueType>(mpi.env, symbolicModel, storm::api::createTask<ValueType>(states, false));
1474 }
1475 if (result && filter) {
1476 result->filter(filter->asQualitativeCheckResult());
1477 }
1478 return result;
1479 });
1480}
1481
1482template<storm::dd::DdType DdType, typename ValueType>
1484 ModelProcessingInformation const& mpi) {
1485 verifyProperties<ValueType>(
1486 input, [&symbolicModel, &mpi](std::shared_ptr<storm::logic::Formula const> const& formula, std::shared_ptr<storm::logic::Formula const> const& states) {
1487 STORM_LOG_THROW(states->isInitialFormula(), storm::exceptions::NotSupportedException, "Abstraction-refinement can only filter initial states.");
1488 return storm::gbar::api::verifyWithAbstractionRefinementEngine<DdType, ValueType>(mpi.env, symbolicModel,
1489 storm::api::createTask<ValueType>(formula, true));
1490 });
1491}
1492
1493template<storm::dd::DdType DdType, typename ValueType>
1494typename std::enable_if<DdType != storm::dd::DdType::CUDD || std::is_same<ValueType, double>::value, void>::type verifyModel(
1495 std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& symbolicModel, SymbolicInput const& input,
1496 ModelProcessingInformation const& mpi) {
1498 verifyWithHybridEngine<DdType, ValueType>(symbolicModel, input, mpi);
1499 } else if (mpi.engine == storm::utility::Engine::Dd) {
1500 verifyWithDdEngine<DdType, ValueType>(symbolicModel, input, mpi);
1501 } else {
1502 verifyWithAbstractionRefinementEngine<DdType, ValueType>(symbolicModel, input, mpi);
1503 }
1504}
1505
1506template<storm::dd::DdType DdType, typename ValueType>
1507typename std::enable_if<DdType == storm::dd::DdType::CUDD && !std::is_same<ValueType, double>::value, void>::type verifySymbolicModel(
1508 std::shared_ptr<storm::models::ModelBase> const&, SymbolicInput const&, ModelProcessingInformation const&) {
1509 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "CUDD does not support the selected data-type.");
1510}
1511
1512inline std::shared_ptr<storm::models::ModelBase> buildPreprocessModel(SymbolicInput const& input, ModelProcessingInformation const& mpi) {
1513 auto ioSettings = storm::settings::getModule<storm::settings::modules::IOSettings>();
1514 auto buildSettings = storm::settings::getModule<storm::settings::modules::BuildSettings>();
1515
1516 std::shared_ptr<storm::models::ModelBase> model;
1517 if (!buildSettings.isNoBuildModelSet()) {
1518 model = buildModel(input, ioSettings, mpi);
1519 }
1520 if (!model) {
1521 STORM_LOG_THROW(input.properties.empty(), storm::exceptions::InvalidSettingsException, "No input model.");
1522 return nullptr;
1523 }
1524 model->printModelInformationToStream(std::cout);
1525
1526 storm::utility::Stopwatch preprocessingWatch(true);
1527 auto preprocessingResult = castAndApply(model, [&input, &mpi](auto const& m) { return preprocessModel(m, input, mpi); });
1528 preprocessingWatch.stop();
1529 if (preprocessingResult.second) {
1530 STORM_PRINT("\nTime for model preprocessing: " << preprocessingWatch << ".\n\n");
1531 model = preprocessingResult.first;
1532 model->printModelInformationToStream(std::cout);
1533 }
1534
1535 return model;
1536}
1537
1538inline std::shared_ptr<storm::models::ModelBase> buildPreprocessExportModel(SymbolicInput const& input, ModelProcessingInformation const& mpi) {
1539 auto model = buildPreprocessModel(input, mpi);
1540 if (model) {
1541 castAndApply(model, [&input](auto const& m) { exportModel(m, input); });
1542 }
1543 return model;
1544}
1545
1546inline void processInput(SymbolicInput const& input, ModelProcessingInformation const& mpi) {
1547 auto abstractionSettings = storm::settings::getModule<storm::settings::modules::AbstractionSettings>();
1548 auto counterexampleSettings = storm::settings::getModule<storm::settings::modules::CounterexampleGeneratorSettings>();
1549
1550 // For several engines, no model building step is performed, but the verification is started right away.
1552 abstractionSettings.getAbstractionRefinementMethod() == storm::settings::modules::AbstractionSettings::Method::Games) {
1554 [&input, &mpi]<storm::dd::DdType DD, typename VT>() { verifyWithAbstractionRefinementEngine<DD, VT>(input, mpi); });
1555 } else if (mpi.engine == storm::utility::Engine::Exploration) {
1556 applyValueType(mpi.verificationValueType, [&input, &mpi]<typename VT>() { verifyWithExplorationEngine<VT>(input, mpi); });
1557 } else {
1558 std::shared_ptr<storm::models::ModelBase> model = buildPreprocessExportModel(input, mpi);
1559 if (model) {
1560 if (counterexampleSettings.isCounterexampleSet()) {
1561 castAndApply(model, [&input](auto const& m) { generateCounterexamples(m, input); });
1562 } else {
1563 castAndApply(model, [&input, &mpi](auto const& m) { verifyModel(m, input, mpi); });
1564 }
1565 }
1566 }
1567}
1568} // namespace cli
1569} // namespace storm
BuilderOptions & setBuildAllLabels(bool newValue=true)
Should all reward models be built? If not set, only required reward models are build.
BuilderOptions & setExplorationChecks(bool newValue=true)
Should extra checks be performed during exploration.
BuilderOptions & setAddOutOfBoundsState(bool newValue=true)
Should a state for out of bounds be constructed.
BuilderOptions & setReservedBitsForUnboundedVariables(uint64_t value)
Sets the number of bits that will be reserved for unbounded integer variables.
BuilderOptions & setBuildChoiceLabels(bool newValue=true)
Should the choice labels be built?
BuilderOptions & setBuildAllRewardModels(bool newValue=true)
Should all reward models be built? If not set, only required reward models are build.
BuilderOptions & setBuildChoiceOrigins(bool newValue=true)
Should the origins the different choices be built?
BuilderOptions & setBuildStateValuations(bool newValue=true)
Should the state valuation mapping be built?
BuilderOptions & setApplyMaximalProgressAssumption(bool newValue=true)
Should the maximal progress assumption be applied when building a Markov Automaton?
BuilderOptions & setBuildObservationValuations(bool newValue=true)
Should a observation valuation mapping be built?
BuilderOptions & setAddOverlappingGuardsLabel(bool newValue=true)
Should a state be labelled for overlapping guards.
This class represents the base class of all exception classes.
virtual const char * what() const noexcept override
Retrieves the message associated with this exception.
This class is responsible for managing a set of typed variables and all expressions using these varia...
std::shared_ptr< storm::logic::Formula const > const & getStatesFormula() const
Definition Property.h:52
storm::modelchecker::FilterType getFilterType() const
Definition Property.h:56
static Model eliminateAutomatically(const Model &model, std::vector< jani::Property > properties, uint64_t locationHeuristic, uint64_t edgesHeuristic)
std::shared_ptr< storm::logic::Formula const > getRawFormula() const
Definition Property.cpp:92
std::string const & getName() const
Get the provided name.
Definition Property.cpp:23
FilterExpression const & getFilter() const
Definition Property.cpp:88
static std::shared_ptr< Formula const > getTrueFormula()
Definition Formula.cpp:213
virtual std::optional< storm::dd::DdType > getDdType() const
Definition ModelBase.cpp:19
This class represents a discrete-time Markov chain.
Definition Dtmc.h:13
This class represents a Markov automaton.
This class represents a (discrete-time) Markov decision process.
Definition Mdp.h:13
Base class for all sparse models.
Definition Model.h:32
Base class for all symbolic models.
Definition Model.h:42
storm::expressions::Expression parseFromString(std::string const &expressionString, bool ignoreError=false) const
Parses an expression from the given string.
void setIdentifierMapping(qi::symbols< char, storm::expressions::Expression > const *identifiers_)
Sets an identifier mapping that is used to determine valid variables in the expression.
storm::jani::Model toJani(bool allVariablesGlobal=true, std::string suffix="") const
Converts the PRISM model into an equivalent JANI model.
Definition Program.cpp:2340
This class represents the settings for the abstraction procedures.
std::string getInjectedRefinementPredicates() const
Retrieves a string containing refinement predicates to inject (if there are any).
std::string getConstraintString() const
Retrieves the string that specifies additional constraints.
This class represents the bisimulation settings.
storm::dd::bisimulation::QuotientFormat getQuotientFormat() const
Retrieves the format in which the quotient is to be extracted.
storm::dd::bisimulation::SignatureMode getSignatureMode() const
Retrieves the mode to compute signatures.
bool isQuotientFormatSetFromDefaultValue() const
Retrieves whether the format in which the quotient is to be extracted has been set from its default v...
bool isWeakBisimulationSet() const
Retrieves whether weak bisimulation is to be used.
bool isBuildChoiceLabelsSet() const
Retrieves whether the choice labels should be build.
This class represents the markov chain settings.
Definition IOSettings.h:21
bool isJaniPropertiesSet() const
Retrieves whether the jani-property option was set.
std::string getExplicitIMCAFilename() const
Retrieves the name of the file that contains the model in the IMCA format.
bool areJaniPropertiesSelected() const
Retrieves whether one or more jani-properties have been selected.
std::string getChoiceLabelingFilename() const
Retrieves the name of the file that contains the choice labeling if the model was given using the exp...
bool isStateRewardsSet() const
Retrieves whether the state reward option was set.
std::string getProperty() const
Retrieves the property specified with the property option.
std::string getJaniInputFilename() const
Retrieves the name of the file that contains the JANI model specification if the model was given usin...
bool isChoiceLabelingSet() const
Retrieves whether the choice labeling option was set.
bool isPrismOrJaniInputSet() const
Retrieves whether the JANI or PRISM input option was set.
std::string getPropertyFilter() const
Retrieves the property filter.
std::string getPrismInputFilename() const
Retrieves the name of the file that contains the PRISM model specification if the model was given usi...
bool isExplicitDRNSet() const
Retrieves whether the explicit option with DRN was set.
std::string getExplicitDRNFilename() const
Retrieves the name of the file that contains the model in the DRN format.
std::string getLabelingFilename() const
Retrieves the name of the file that contains the state labeling if the model was given using the expl...
boost::optional< std::vector< std::string > > getQvbsPropertyFilter() const
Retrieves the selected property names.
std::string getStateRewardsFilename() const
Retrieves the name of the file that contains the state rewards if the model was given using the expli...
std::string getTransitionRewardsFilename() const
Retrieves the name of the file that contains the transition rewards if the model was given using the ...
bool isPropertySet() const
Retrieves whether the property option was set.
std::string getQvbsModelName() const
Retrieves the specified model (short-)name of the QVBS.
std::vector< std::string > getSelectedJaniProperties() const
std::string getTransitionFilename() const
Retrieves the name of the file that contains the transitions if the model was given using the explici...
uint64_t getQvbsInstanceIndex() const
Retrieves the selected model instance (file + open parameters of the model)
bool isExplicitIMCASet() const
Retrieves whether the explicit option with IMCA was set.
bool isPrismInputSet() const
Retrieves whether the PRISM language option was set.
bool isTransitionRewardsSet() const
Retrieves whether the transition reward option was set.
bool isExplicitSet() const
Retrieves whether the explicit option was set.
This class provides easy access to a benchmark of the Quantitative Verification Benchmark Set http://...
Definition Qvbs.h:17
std::string const & getJaniFile(uint64_t instanceIndex=0) const
Definition Qvbs.cpp:107
std::string getInfo(uint64_t instanceIndex=0, boost::optional< std::vector< std::string > > propertyFilter=boost::none) const
Definition Qvbs.cpp:118
std::string const & getConstantDefinition(uint64_t instanceIndex=0) const
Definition Qvbs.cpp:112
storm::jani::Model const & asJaniModel() const
Transformer for eliminating chains of non-Markovian states (instantaneous path fragment leading to th...
storm::utility::Engine getEngine() const
Retrieve "good" settings after calling predict.
void predict(storm::jani::Model const &model, storm::jani::Property const &property)
Predicts "good" settings for the provided model checking query.
A class that provides convenience operations to display run times.
Definition Stopwatch.h:14
void start()
Start stopwatch (again) and start measuring time.
Definition Stopwatch.cpp:48
void stop()
Stop stopwatch and add measured time to total time.
Definition Stopwatch.cpp:42
#define STORM_LOG_INFO(message)
Definition logging.h:24
#define STORM_LOG_WARN(message)
Definition logging.h:25
#define STORM_LOG_TRACE(message)
Definition logging.h:12
#define STORM_LOG_ERROR(message)
Definition logging.h:26
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:11
#define STORM_LOG_WARN_COND(cond, message)
Definition macros.h:38
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
#define STORM_PRINT_AND_LOG(message)
Definition macros.h:68
#define STORM_PRINT(message)
Define the macros that print information and optionally also log it.
Definition macros.h:62
void exportScheduler(std::shared_ptr< storm::models::sparse::Model< ValueType > > const &model, storm::storage::Scheduler< ValueType > const &scheduler, std::string const &filename)
Definition export.h:69
void exportSymbolicModelAsDot(std::shared_ptr< storm::models::symbolic::Model< Type, ValueType > > const &model, std::string const &filename)
Definition export.h:64
storm::prism::Program parseProgram(std::string const &filename, bool prismCompatibility, bool simplify)
std::shared_ptr< storm::models::sparse::Model< ValueType > > permuteModelStates(std::shared_ptr< storm::models::sparse::Model< ValueType > > const &model, storm::utility::permutation::OrderKind order, std::optional< uint64_t > seed=std::nullopt)
Permutes the order of the states of the model according to the given order.
storm::jani::Property createMultiObjectiveProperty(std::vector< storm::jani::Property > const &properties, bool lexicographic)
std::shared_ptr< storm::counterexamples::Counterexample > computeHighLevelCounterexampleMilp(storm::storage::SymbolicModelDescription const &symbolicModel, std::shared_ptr< storm::models::sparse::Mdp< double > > mdp, std::shared_ptr< storm::logic::Formula const > const &formula)
std::pair< std::shared_ptr< storm::models::sparse::Model< ValueType > >, std::vector< std::shared_ptr< storm::logic::Formula const > > > eliminateNonMarkovianChains(std::shared_ptr< storm::models::sparse::MarkovAutomaton< ValueType > > const &ma, std::vector< std::shared_ptr< storm::logic::Formula const > > const &formulas, storm::transformer::EliminationLabelBehavior labelBehavior)
Eliminates chains of non-Markovian states from a given Markov Automaton.
std::pair< storm::jani::Model, std::vector< storm::jani::Property > > parseJaniModel(std::string const &filename, boost::optional< std::vector< std::string > > const &propertyFilter)
void exportJaniModelAsDot(storm::jani::Model const &model, std::string const &filename)
Definition export.cpp:7
std::vector< storm::jani::Property > parseProperties(storm::parser::FormulaParser &formulaParser, std::string const &inputString, boost::optional< std::set< std::string > > const &propertyFilter)
std::shared_ptr< storm::models::sparse::Model< ValueType > > transformSymbolicToSparseModel(std::shared_ptr< storm::models::symbolic::Model< Type, ValueType > > const &symbolicModel, std::vector< std::shared_ptr< storm::logic::Formula const > > const &formulas=std::vector< std::shared_ptr< storm::logic::Formula const > >())
Transforms the given symbolic model to a sparse model.
std::pair< std::shared_ptr< storm::models::sparse::Model< ValueType > >, std::vector< std::shared_ptr< storm::logic::Formula const > > > transformContinuousToDiscreteTimeSparseModel(std::shared_ptr< storm::models::sparse::Model< ValueType > > const &model, std::vector< std::shared_ptr< storm::logic::Formula const > > const &formulas)
Transforms the given continuous model to a discrete time model.
void exportSymbolicModelAsDrdd(std::shared_ptr< storm::models::symbolic::Model< Type, ValueType > > const &model, std::string const &filename)
Definition export.h:43
std::shared_ptr< storm::counterexamples::Counterexample > computeHighLevelCounterexampleMaxSmt(storm::storage::SymbolicModelDescription const &symbolicModel, std::shared_ptr< storm::models::sparse::Model< double > > model, std::shared_ptr< storm::logic::Formula const > const &formula)
std::vector< storm::jani::Property > parsePropertiesForSymbolicModelDescription(std::string const &inputString, storm::storage::SymbolicModelDescription const &modelDescription, boost::optional< std::set< std::string > > const &propertyFilter)
std::vector< std::shared_ptr< storm::logic::Formula const > > extractFormulasFromProperties(std::vector< storm::jani::Property > const &properties)
void simplifyJaniModel(storm::jani::Model &model, std::vector< storm::jani::Property > &properties, storm::jani::ModelFeatures const &supportedFeatures)
std::shared_ptr< storm::models::sparse::Model< ValueType > > buildExplicitDRNModel(std::string const &drnFile, storm::parser::DirectEncodingParserOptions const &options=storm::parser::DirectEncodingParserOptions())
boost::optional< std::set< std::string > > parsePropertyFilter(std::string const &propertyFilter)
storm::jani::ModelFeatures getSupportedJaniFeatures(storm::builder::BuilderType const &builderType)
Definition builder.h:31
void exportSparseModelAsDrn(std::shared_ptr< storm::models::sparse::Model< ValueType > > const &model, std::string const &filename, std::vector< std::string > const &parameterNames={}, bool allowPlaceholders=true)
Definition export.h:29
void exportCheckResultToJson(std::shared_ptr< storm::models::sparse::Model< ValueType > > const &model, std::unique_ptr< storm::modelchecker::CheckResult > const &checkResult, std::string const &filename)
Definition export.h:116
std::shared_ptr< storm::counterexamples::Counterexample > computeKShortestPathCounterexample(std::shared_ptr< storm::models::sparse::Model< double > > model, std::shared_ptr< storm::logic::Formula const > const &formula, size_t maxK)
void exportSparseModelAsJson(std::shared_ptr< storm::models::sparse::Model< ValueType > > const &model, std::string const &filename)
Definition export.h:56
void exportSparseModelAsDot(std::shared_ptr< storm::models::sparse::Model< ValueType > > const &model, std::string const &filename, size_t maxWidth=30)
Definition export.h:48
void exportParetoScheduler(std::shared_ptr< storm::models::sparse::Model< ValueType > > const &model, std::vector< PointType > const &points, std::vector< storm::storage::Scheduler< ValueType > > const &schedulers, std::string const &baseFilenameStr)
Definition export.h:83
std::vector< storm::jani::Property > substituteConstantsInProperties(std::vector< storm::jani::Property > const &properties, std::map< storm::expressions::Variable, storm::expressions::Expression > const &substitution)
void exportModel(std::shared_ptr< storm::models::sparse::Model< ValueType > > const &model, SymbolicInput const &input)
void exportSymbolicInput(SymbolicInput const &input)
void parseSymbolicModelDescription(storm::settings::modules::IOSettings const &ioSettings, SymbolicInput &input)
void verifyWithAbstractionRefinementEngine(SymbolicInput const &input, ModelProcessingInformation const &mpi)
std::shared_ptr< storm::models::ModelBase > buildModelExplicit(storm::settings::modules::IOSettings const &ioSettings, storm::settings::modules::BuildSettings const &buildSettings)
std::enable_if< DdType==storm::dd::DdType::CUDD &&!std::is_same< ValueType, double >::value, void >::type verifySymbolicModel(std::shared_ptr< storm::models::ModelBase > const &, SymbolicInput const &, ModelProcessingInformation const &)
void verifyModel(std::shared_ptr< storm::models::sparse::Model< ValueType > > const &sparseModel, SymbolicInput const &input, ModelProcessingInformation const &mpi)
auto castAndApply(std::shared_ptr< storm::models::ModelBase > const &model, auto const &callback)
SymbolicInput parseSymbolicInputQvbs(storm::settings::modules::IOSettings const &ioSettings)
void getModelProcessingInformationAutomatic(SymbolicInput const &input, ModelProcessingInformation &mpi)
void verifyWithExplorationEngine(SymbolicInput const &input, ModelProcessingInformation const &mpi)
std::shared_ptr< storm::models::sparse::Model< ValueType > > preprocessSparseModelBisimulation(std::shared_ptr< storm::models::sparse::Model< ValueType > > const &model, SymbolicInput const &input, storm::settings::modules::BisimulationSettings const &bisimulationSettings, bool graphPreserving=true)
void printResult(std::unique_ptr< storm::modelchecker::CheckResult > const &result, storm::logic::Formula const &filterStatesFormula, storm::modelchecker::FilterType const &filterType, storm::utility::Stopwatch *watch=nullptr)
auto applyValueType(ModelProcessingInformation::ValueType vt, auto const &callback)
std::shared_ptr< storm::models::ModelBase > buildPreprocessExportModel(SymbolicInput const &input, ModelProcessingInformation const &mpi)
void verifyProperties(SymbolicInput const &input, VerificationCallbackType const &verificationCallback, PostprocessingCallbackType const &postprocessingCallback=PostprocessingIdentity())
Verifies all (potentially preprocessed) properties given in input.
std::vector< storm::expressions::Expression > parseConstraints(storm::expressions::ExpressionManager const &expressionManager, std::string const &constraintsString)
std::vector< std::vector< storm::expressions::Expression > > parseInjectedRefinementPredicates(storm::expressions::ExpressionManager const &expressionManager, std::string const &refinementPredicatesString)
SymbolicInput parseSymbolicInput()
void verifyWithDdEngine(std::shared_ptr< storm::models::symbolic::Model< DdType, ValueType > > const &symbolicModel, SymbolicInput const &input, ModelProcessingInformation const &mpi)
std::function< std::unique_ptr< storm::modelchecker::CheckResult >(std::shared_ptr< storm::logic::Formula const > const &formula, std::shared_ptr< storm::logic::Formula const > const &states)> VerificationCallbackType
std::shared_ptr< storm::models::ModelBase > buildModelSparse(SymbolicInput const &input, storm::builder::BuilderOptions const &options)
std::enable_if< DdType!=storm::dd::DdType::Sylvan &&!std::is_same< ValueType, double >::value, std::shared_ptr< storm::models::Model< ValueType > > >::type preprocessDdMarkovAutomaton(std::shared_ptr< storm::models::symbolic::Model< DdType, ValueType > > const &model)
std::shared_ptr< storm::models::sparse::Model< ValueType > > preprocessSparseMarkovAutomaton(std::shared_ptr< storm::models::sparse::MarkovAutomaton< ValueType > > const &model)
void printFilteredResult(std::unique_ptr< storm::modelchecker::CheckResult > const &result, storm::modelchecker::FilterType ft)
void computeStateValues(std::string const &description, std::function< std::unique_ptr< storm::modelchecker::CheckResult >()> const &computationCallback, SymbolicInput const &input, VerificationCallbackType const &verificationCallback, PostprocessingCallbackType const &postprocessingCallback=PostprocessingIdentity())
Computes values for each state (such as the steady-state probability distribution).
void printCounterexample(std::shared_ptr< storm::counterexamples::Counterexample > const &counterexample, storm::utility::Stopwatch *watch=nullptr)
std::pair< SymbolicInput, ModelProcessingInformation > preprocessSymbolicInput(SymbolicInput const &input)
std::shared_ptr< storm::models::ModelBase > buildModel(SymbolicInput const &input, storm::settings::modules::IOSettings const &ioSettings, ModelProcessingInformation const &mpi)
void printModelCheckingProperty(storm::jani::Property const &property)
void parseProperties(storm::settings::modules::IOSettings const &ioSettings, SymbolicInput &input, boost::optional< std::set< std::string > > const &propertyFilter)
void verifyWithHybridEngine(std::shared_ptr< storm::models::symbolic::Model< DdType, ValueType > > const &symbolicModel, SymbolicInput const &input, ModelProcessingInformation const &mpi)
ModelProcessingInformation getModelProcessingInformation(SymbolicInput const &input, std::shared_ptr< SymbolicInput > const &transformedJaniInput=nullptr)
Sets the model processing information based on the given input.
void printComputingCounterexample(storm::jani::Property const &property)
std::shared_ptr< storm::models::Model< ExportValueType > > preprocessDdModelBisimulation(std::shared_ptr< storm::models::symbolic::Model< DdType, ValueType > > const &model, SymbolicInput const &input, storm::settings::modules::BisimulationSettings const &bisimulationSettings, ModelProcessingInformation const &mpi)
auto applyDdLibValueType(storm::dd::DdType dd, ModelProcessingInformation::ValueType vt, auto const &callback)
std::function< void(std::unique_ptr< storm::modelchecker::CheckResult > const &)> PostprocessingCallbackType
std::shared_ptr< storm::models::ModelBase > buildPreprocessModel(SymbolicInput const &input, ModelProcessingInformation const &mpi)
storm::builder::BuilderOptions createBuildOptionsSparseFromSettings(SymbolicInput const &input)
void processInput(SymbolicInput const &input, ModelProcessingInformation const &mpi)
void ensureNoUndefinedPropertyConstants(std::vector< storm::jani::Property > const &properties)
void generateCounterexamples(std::shared_ptr< ModelType > const &, SymbolicInput const &)
std::vector< std::shared_ptr< storm::logic::Formula const > > createFormulasToRespect(std::vector< storm::jani::Property > const &properties)
std::pair< std::shared_ptr< storm::models::ModelBase >, bool > preprocessModel(std::shared_ptr< storm::models::sparse::Model< ValueType > > const &model, SymbolicInput const &input, ModelProcessingInformation const &mpi)
std::shared_ptr< storm::models::ModelBase > buildModelDd(SymbolicInput const &input)
std::pair< std::shared_ptr< storm::models::ModelBase >, bool > preprocessDdModelImpl(std::shared_ptr< storm::models::symbolic::Model< DdType, ValueType > > const &model, SymbolicInput const &input, ModelProcessingInformation const &mpi)
std::unique_ptr< storm::modelchecker::CheckResult > verifyProperty(std::shared_ptr< storm::logic::Formula const > const &formula, std::shared_ptr< storm::logic::Formula const > const &statesFilter, VerificationCallbackType const &verificationCallback)
Verifies the given formula plus a filter formula to identify relevant states and warns the user in ca...
std::string toString(CompressionMode const &input)
SettingsManager const & manager()
Retrieves the settings manager.
OptimizationDirection convert(OptimizationDirectionSetting s)
std::string orderKindtoString(OrderKind order)
Converts the given order to a string.
bool isTerminate()
Check whether the program should terminate (due to some abort signal).
bool isConstant(ValueType const &)
bool canHandle< storm::RationalFunction >(storm::utility::Engine const &engine, storm::storage::SymbolicModelDescription::ModelType const &modelType, storm::modelchecker::CheckTask< storm::logic::Formula, storm::RationalFunction > const &checkTask)
Definition Engine.cpp:196
Engine
An enumeration of all engines.
Definition Engine.h:31
template bool canHandle< storm::RationalNumber >(storm::utility::Engine const &, std::vector< storm::jani::Property > const &, storm::storage::SymbolicModelDescription const &)
template bool canHandle< double >(storm::utility::Engine const &, std::vector< storm::jani::Property > const &, storm::storage::SymbolicModelDescription const &)
storm::builder::BuilderType getBuilderType(Engine const &engine)
Returns the builder type used for the given engine.
Definition Engine.cpp:78
typename detail::IntervalMetaProgrammingHelper< ValueType >::BaseType IntervalBaseType
Helper to access the type in which interval boundaries are stored.
void operator()(std::unique_ptr< storm::modelchecker::CheckResult > const &)
std::vector< storm::jani::Property > properties
boost::optional< storm::storage::SymbolicModelDescription > model
boost::optional< std::vector< storm::jani::Property > > preprocessedProperties
bool allowPlaceholders
Allow placeholders for rational functions in the exported DRN file.
storm::io::CompressionMode compression
The type of compression used for the exported DRN file.
std::optional< std::size_t > outputPrecision
If set, the output precision for floating point numbers in the exported DRN file is set to the given ...