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();
590 result = storm::api::buildExplicitDRNModel<ValueType>(ioSettings.getExplicitDRNFilename(), options);
591 } else {
592 STORM_LOG_THROW(ioSettings.isExplicitIMCASet(), storm::exceptions::InvalidSettingsException, "Unexpected explicit model input type.");
593 result = storm::api::buildExplicitIMCAModel<ValueType>(ioSettings.getExplicitIMCAFilename());
594 }
595 return result;
596}
597
598inline std::shared_ptr<storm::models::ModelBase> buildModel(SymbolicInput const& input, storm::settings::modules::IOSettings const& ioSettings,
599 ModelProcessingInformation const& mpi) {
600 storm::utility::Stopwatch modelBuildingWatch(true);
601
602 std::shared_ptr<storm::models::ModelBase> result;
603 if (input.model) {
604 auto builderType = storm::utility::getBuilderType(mpi.engine);
605 if (builderType == storm::builder::BuilderType::Dd) {
606 result = applyDdLibValueType(mpi.ddType, mpi.buildValueType, [&input]<storm::dd::DdType DD, typename VT>() { return buildModelDd<DD, VT>(input); });
607 } else if (builderType == storm::builder::BuilderType::Explicit) {
608 result = applyValueType(mpi.buildValueType, [&input]<typename VT>() {
609 auto options = createBuildOptionsSparseFromSettings(input);
610 return buildModelSparse<VT>(input, options);
611 });
612 }
613 } else if (ioSettings.isExplicitSet() || ioSettings.isExplicitDRNSet() || ioSettings.isExplicitIMCASet()) {
614 STORM_LOG_THROW(mpi.engine == storm::utility::Engine::Sparse, storm::exceptions::InvalidSettingsException,
615 "Can only use sparse engine with explicit input.");
616 result = applyValueType(mpi.buildValueType, [&ioSettings]<typename VT>() {
617 return buildModelExplicit<VT>(ioSettings, storm::settings::getModule<storm::settings::modules::BuildSettings>());
618 });
619 }
620
621 modelBuildingWatch.stop();
622 if (result) {
623 STORM_PRINT("Time for model construction: " << modelBuildingWatch << ".\n\n");
624 }
625
626 return result;
627}
628
629template<typename ValueType>
630std::shared_ptr<storm::models::sparse::Model<ValueType>> preprocessSparseMarkovAutomaton(
631 std::shared_ptr<storm::models::sparse::MarkovAutomaton<ValueType>> const& model) {
632 auto transformationSettings = storm::settings::getModule<storm::settings::modules::TransformationSettings>();
633 auto debugSettings = storm::settings::getModule<storm::settings::modules::DebugSettings>();
634
635 std::shared_ptr<storm::models::sparse::Model<ValueType>> result = model;
636 model->close();
637 STORM_LOG_WARN_COND(!debugSettings.isAdditionalChecksSet() || !model->containsZenoCycle(),
638 "MA contains a Zeno cycle. Model checking results cannot be trusted.");
639
640 if (model->isConvertibleToCtmc()) {
641 STORM_LOG_WARN_COND(false, "MA is convertible to a CTMC, consider using a CTMC instead.");
642 result = model->convertToCtmc();
643 }
644
645 if (transformationSettings.isChainEliminationSet()) {
646 if constexpr (storm::IsIntervalType<ValueType>) {
647 // Currently not enabling this for interval models, as this would require a number of additional template instantiations.
648 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Chain elimination not supported for interval models.");
649 } else {
650 // TODO: we should also transform the properties at this point.
652 transformationSettings.getLabelBehavior())
653 .first;
654 }
655 }
656
657 return result;
658}
659
660template<typename ValueType>
661std::shared_ptr<storm::models::sparse::Model<ValueType>> preprocessSparseModelBisimulation(
662 std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, SymbolicInput const& input,
663 storm::settings::modules::BisimulationSettings const& bisimulationSettings, bool graphPreserving = true) {
665 if (bisimulationSettings.isWeakBisimulationSet()) {
667 }
668
669 STORM_LOG_INFO("Performing bisimulation minimization...");
670 return storm::api::performBisimulationMinimization<ValueType>(model, createFormulasToRespect(input.properties), bisimType, graphPreserving);
671}
672
673template<typename ValueType>
674std::pair<std::shared_ptr<storm::models::ModelBase>, bool> preprocessModel(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model,
675 SymbolicInput const& input, ModelProcessingInformation const& mpi) {
676 STORM_LOG_THROW(mpi.buildValueType == mpi.verificationValueType, storm::exceptions::NotSupportedException,
677 "Converting value types for sparse engine is not supported.");
678 auto bisimulationSettings = storm::settings::getModule<storm::settings::modules::BisimulationSettings>();
679 auto ioSettings = storm::settings::getModule<storm::settings::modules::IOSettings>();
680 auto transformationSettings = storm::settings::getModule<storm::settings::modules::TransformationSettings>();
681
682 std::pair<std::shared_ptr<storm::models::sparse::Model<ValueType>>, bool> result = std::make_pair(model, false);
683
684 if (auto order = transformationSettings.getModelPermutation(); order.has_value()) {
685 auto seed = transformationSettings.getModelPermutationSeed();
686 STORM_PRINT_AND_LOG("Permuting model states using " << storm::utility::permutation::orderKindtoString(order.value()) << " order"
687 << (seed.has_value() ? " with seed " + std::to_string(seed.value()) : "") << ".\n");
688 result.first = storm::api::permuteModelStates(result.first, order.value(), seed);
689 result.second = true;
690 STORM_PRINT_AND_LOG("Transition matrix hash after permuting: " << result.first->getTransitionMatrix().hash() << ".\n");
691 }
692
693 if (result.first->isOfType(storm::models::ModelType::MarkovAutomaton)) {
695 result.second = true;
696 }
697
698 if (mpi.applyBisimulation) {
699 if constexpr (storm::IsIntervalType<ValueType>) {
700 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Bisimulation not supported for interval models.");
701 } else {
702 result.first = preprocessSparseModelBisimulation(result.first, input, bisimulationSettings);
703 result.second = true;
704 }
705 }
706
707 if (transformationSettings.isToDiscreteTimeModelSet()) {
708 if constexpr (storm::IsIntervalType<ValueType>) {
709 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Transformation to discrete time model not supported for interval models.");
710 } else {
711 // TODO: we should also transform the properties at this point.
713 !model->hasRewardModel("_time"),
714 "Scheduled transformation to discrete time model, but a reward model named '_time' is already present in this model. We might take "
715 "the wrong reward model later.");
716 result.first =
718 .first;
719 result.second = true;
720 }
721 }
722
723 if (transformationSettings.isToNondeterministicModelSet()) {
724 result.first = storm::api::transformToNondeterministicModel<ValueType>(std::move(*result.first));
725 result.second = true;
726 }
727
728 return result;
729}
730
731template<typename ValueType>
732void exportModel(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, SymbolicInput const& input) {
733 auto ioSettings = storm::settings::getModule<storm::settings::modules::IOSettings>();
734
735 if (ioSettings.isExportBuildSet()) {
736 switch (ioSettings.getExportBuildFormat()) {
738 storm::api::exportSparseModelAsDot(model, ioSettings.getExportBuildFilename(), ioSettings.getExportDotMaxWidth());
739 break;
741 storm::api::exportSparseModelAsDrn(model, ioSettings.getExportBuildFilename(),
742 input.model ? input.model.get().getParameterNames() : std::vector<std::string>(),
743 !ioSettings.isExplicitExportPlaceholdersDisabled());
744 break;
746 storm::api::exportSparseModelAsJson(model, ioSettings.getExportBuildFilename());
747 break;
748 default:
749 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException,
750 "Exporting sparse models in " << storm::io::toString(ioSettings.getExportBuildFormat()) << " format is not supported.");
751 }
752 }
753
754 // TODO: The following options are depreciated and shall be removed at some point:
755
756 if (ioSettings.isExportExplicitSet()) {
757 storm::api::exportSparseModelAsDrn(model, ioSettings.getExportExplicitFilename(),
758 input.model ? input.model.get().getParameterNames() : std::vector<std::string>(),
759 !ioSettings.isExplicitExportPlaceholdersDisabled());
760 }
761
762 if (ioSettings.isExportDdSet()) {
763 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Exporting in drdd format is only supported for DDs.");
764 }
765
766 if (ioSettings.isExportDotSet()) {
767 storm::api::exportSparseModelAsDot(model, ioSettings.getExportDotFilename(), ioSettings.getExportDotMaxWidth());
768 }
769}
770
771template<storm::dd::DdType DdType, typename ValueType>
773 auto ioSettings = storm::settings::getModule<storm::settings::modules::IOSettings>();
774
775 if (ioSettings.isExportBuildSet()) {
776 switch (ioSettings.getExportBuildFormat()) {
778 storm::api::exportSymbolicModelAsDot(model, ioSettings.getExportBuildFilename());
779 break;
781 storm::api::exportSymbolicModelAsDrdd(model, ioSettings.getExportBuildFilename());
782 break;
783 default:
784 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException,
785 "Exporting symbolic models in " << storm::io::toString(ioSettings.getExportBuildFormat()) << " format is not supported.");
786 }
787 }
788
789 // TODO: The following options are depreciated and shall be removed at some point:
790
791 if (ioSettings.isExportExplicitSet()) {
792 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Exporting in drn format is only supported for sparse models.");
793 }
794
795 if (ioSettings.isExportDdSet()) {
796 storm::api::exportSymbolicModelAsDrdd(model, ioSettings.getExportDdFilename());
797 }
798
799 if (ioSettings.isExportDotSet()) {
800 storm::api::exportSymbolicModelAsDot(model, ioSettings.getExportDotFilename());
801 }
802}
803
804template<storm::dd::DdType DdType, typename ValueType>
805typename std::enable_if<DdType != storm::dd::DdType::Sylvan && !std::is_same<ValueType, double>::value, std::shared_ptr<storm::models::Model<ValueType>>>::type
807 return model;
808}
809
810template<storm::dd::DdType DdType, typename ValueType>
811typename std::enable_if<DdType == storm::dd::DdType::Sylvan || std::is_same<ValueType, double>::value, std::shared_ptr<storm::models::Model<ValueType>>>::type
813 auto ma = model->template as<storm::models::symbolic::MarkovAutomaton<DdType, ValueType>>();
814 if (!ma->isClosed()) {
815 return std::make_shared<storm::models::symbolic::MarkovAutomaton<DdType, ValueType>>(ma->close());
816 } else {
817 return model;
818 }
819}
820
821template<storm::dd::DdType DdType, typename ValueType, typename ExportValueType = ValueType>
822std::shared_ptr<storm::models::Model<ExportValueType>> preprocessDdModelBisimulation(
823 std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& model, SymbolicInput const& input,
824 storm::settings::modules::BisimulationSettings const& bisimulationSettings, ModelProcessingInformation const& mpi) {
825 STORM_LOG_WARN_COND(!bisimulationSettings.isWeakBisimulationSet(),
826 "Weak bisimulation is currently not supported on DDs. Falling back to strong bisimulation.");
827
828 auto quotientFormat = bisimulationSettings.getQuotientFormat();
830 bisimulationSettings.isQuotientFormatSetFromDefaultValue()) {
831 STORM_LOG_INFO("Setting bisimulation quotient format to 'sparse'.");
833 }
834 STORM_LOG_INFO("Performing bisimulation minimization...");
835 return storm::api::performBisimulationMinimization<DdType, ValueType, ExportValueType>(
836 model, createFormulasToRespect(input.properties), storm::storage::BisimulationType::Strong, bisimulationSettings.getSignatureMode(), quotientFormat);
837}
838
839template<typename ExportValueType, storm::dd::DdType DdType, typename ValueType>
840std::pair<std::shared_ptr<storm::models::ModelBase>, bool> preprocessDdModelImpl(
841 std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& model, SymbolicInput const& input, ModelProcessingInformation const& mpi) {
842 auto bisimulationSettings = storm::settings::getModule<storm::settings::modules::BisimulationSettings>();
843 std::pair<std::shared_ptr<storm::models::Model<ValueType>>, bool> intermediateResult = std::make_pair(model, false);
844
845 if (model->isOfType(storm::models::ModelType::MarkovAutomaton)) {
846 intermediateResult.first = preprocessDdMarkovAutomaton(intermediateResult.first->template as<storm::models::symbolic::Model<DdType, ValueType>>());
847 intermediateResult.second = true;
848 }
849
850 std::unique_ptr<std::pair<std::shared_ptr<storm::models::Model<ExportValueType>>, bool>> result;
851 auto symbolicModel = intermediateResult.first->template as<storm::models::symbolic::Model<DdType, ValueType>>();
852 if (mpi.applyBisimulation) {
853 std::shared_ptr<storm::models::Model<ExportValueType>> newModel =
854 preprocessDdModelBisimulation<DdType, ValueType, ExportValueType>(symbolicModel, input, bisimulationSettings, mpi);
855 result = std::make_unique<std::pair<std::shared_ptr<storm::models::Model<ExportValueType>>, bool>>(newModel, true);
856 } else {
857 result = std::make_unique<std::pair<std::shared_ptr<storm::models::Model<ExportValueType>>, bool>>(
858 symbolicModel->template toValueType<ExportValueType>(), !std::is_same<ValueType, ExportValueType>::value);
859 }
860
861 if (result && result->first->isSymbolicModel() && mpi.engine == storm::utility::Engine::DdSparse) {
862 // Mark as changed.
863 result->second = true;
864
865 std::shared_ptr<storm::models::symbolic::Model<DdType, ExportValueType>> symbolicModel =
866 result->first->template as<storm::models::symbolic::Model<DdType, ExportValueType>>();
867 std::vector<std::shared_ptr<storm::logic::Formula const>> formulas;
868 for (auto const& property : input.properties) {
869 formulas.emplace_back(property.getRawFormula());
870 }
871 result->first = storm::api::transformSymbolicToSparseModel(symbolicModel, formulas);
872 STORM_LOG_THROW(result, storm::exceptions::NotSupportedException, "The translation to a sparse model is not supported for the given model type.");
873 }
874
875 return *result;
876}
877
878template<storm::dd::DdType DdType, typename ValueType>
879std::pair<std::shared_ptr<storm::models::ModelBase>, bool> preprocessModel(std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& model,
880 SymbolicInput const& input, ModelProcessingInformation const& mpi) {
881 return applyValueType(mpi.verificationValueType, [&model, &input, &mpi]<typename VT>() -> std::pair<std::shared_ptr<storm::models::ModelBase>, bool> {
882 // To safe a few template instantiations, we only consider those combinations that actually occur in the CLI
883 if constexpr (std::is_same_v<ValueType, VT> ||
884 (DdType == storm::dd::DdType::Sylvan && std::is_same_v<ValueType, storm::RationalNumber> && std::is_same_v<VT, double>)) {
885 return preprocessDdModelImpl<VT>(model, input, mpi);
886 } else {
887 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException,
888 "Unexpected combination of DD library, build value type, and verification value type.");
889 }
890 });
891}
892
894 STORM_PRINT("Computing counterexample for property " << *property.getRawFormula() << " ...\n");
895}
896
897inline void printCounterexample(std::shared_ptr<storm::counterexamples::Counterexample> const& counterexample, storm::utility::Stopwatch* watch = nullptr) {
898 if (counterexample) {
899 STORM_PRINT(*counterexample << '\n');
900 if (watch) {
901 STORM_PRINT("Time for computation: " << *watch << ".\n");
902 }
903 } else {
904 STORM_PRINT(" failed.\n");
905 }
906}
907
908template<typename ModelType>
909 requires(!std::derived_from<ModelType, storm::models::sparse::Model<double>>)
910inline void generateCounterexamples(std::shared_ptr<ModelType> const&, SymbolicInput const&) {
911 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Counterexample generation is not supported for this data-type.");
912}
913
914template<typename ModelType>
915 requires(std::derived_from<ModelType, storm::models::sparse::Model<double>>)
916inline void generateCounterexamples(std::shared_ptr<ModelType> const& sparseModel, SymbolicInput const& input) {
917 using ValueType = typename ModelType::ValueType;
918
919 for (auto& rewModel : sparseModel->getRewardModels()) {
920 rewModel.second.reduceToStateBasedRewards(sparseModel->getTransitionMatrix(), true);
921 }
922
923 STORM_LOG_THROW(sparseModel->isOfType(storm::models::ModelType::Dtmc) || sparseModel->isOfType(storm::models::ModelType::Mdp),
924 storm::exceptions::NotSupportedException, "Counterexample is currently only supported for discrete-time models.");
925
926 auto counterexampleSettings = storm::settings::getModule<storm::settings::modules::CounterexampleGeneratorSettings>();
927 if (counterexampleSettings.isMinimalCommandSetGenerationSet()) {
928 bool useMilp = counterexampleSettings.isUseMilpBasedMinimalCommandSetGenerationSet();
929 for (auto const& property : input.properties) {
930 std::shared_ptr<storm::counterexamples::Counterexample> counterexample;
932 storm::utility::Stopwatch watch(true);
933 if (useMilp) {
934 STORM_LOG_THROW(sparseModel->isOfType(storm::models::ModelType::Mdp), storm::exceptions::NotSupportedException,
935 "Counterexample generation using MILP is currently only supported for MDPs.");
937 input.model.get(), sparseModel->template as<storm::models::sparse::Mdp<ValueType>>(), property.getRawFormula());
938 } else {
939 STORM_LOG_THROW(sparseModel->isOfType(storm::models::ModelType::Dtmc) || sparseModel->isOfType(storm::models::ModelType::Mdp),
940 storm::exceptions::NotSupportedException,
941 "Counterexample generation using MaxSAT is currently only supported for discrete-time models.");
942
943 if (sparseModel->isOfType(storm::models::ModelType::Dtmc)) {
945 input.model.get(), sparseModel->template as<storm::models::sparse::Dtmc<ValueType>>(), property.getRawFormula());
946 } else {
948 input.model.get(), sparseModel->template as<storm::models::sparse::Mdp<ValueType>>(), property.getRawFormula());
949 }
950 }
951 watch.stop();
952 printCounterexample(counterexample, &watch);
953 }
954 } else if (counterexampleSettings.isShortestPathGenerationSet()) {
955 for (auto const& property : input.properties) {
956 std::shared_ptr<storm::counterexamples::Counterexample> counterexample;
958 storm::utility::Stopwatch watch(true);
959 STORM_LOG_THROW(sparseModel->isOfType(storm::models::ModelType::Dtmc), storm::exceptions::NotSupportedException,
960 "Counterexample generation using shortest paths is currently only supported for DTMCs.");
962 property.getRawFormula(), counterexampleSettings.getShortestPathMaxK());
963 watch.stop();
964 printCounterexample(counterexample, &watch);
965 }
966 } else {
967 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "The selected counterexample formalism is unsupported.");
968 }
969}
970
971template<typename ValueType>
972 requires(!storm::IsIntervalType<ValueType>)
973void printFilteredResult(std::unique_ptr<storm::modelchecker::CheckResult> const& result, storm::modelchecker::FilterType ft) {
974 if (result->isQuantitative()) {
976 STORM_PRINT(*result);
977 } else {
978 ValueType resultValue;
979 switch (ft) {
981 resultValue = result->asQuantitativeCheckResult<ValueType>().sum();
982 break;
984 resultValue = result->asQuantitativeCheckResult<ValueType>().average();
985 break;
987 resultValue = result->asQuantitativeCheckResult<ValueType>().getMin();
988 break;
990 resultValue = result->asQuantitativeCheckResult<ValueType>().getMax();
991 break;
994 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Outputting states is not supported.");
998 STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Filter type only defined for qualitative results.");
999 default:
1000 STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Unhandled filter type.");
1001 }
1003 STORM_PRINT(resultValue << " (approx. " << storm::utility::convertNumber<double>(resultValue) << ")");
1004 } else {
1005 STORM_PRINT(resultValue);
1006 }
1007 }
1008 } else {
1009 switch (ft) {
1011 STORM_PRINT(*result << '\n');
1012 break;
1014 STORM_PRINT(result->asQualitativeCheckResult().existsTrue());
1015 break;
1017 STORM_PRINT(result->asQualitativeCheckResult().forallTrue());
1018 break;
1020 STORM_PRINT(result->asQualitativeCheckResult().count());
1021 break;
1024 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Outputting states is not supported.");
1029 STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Filter type only defined for quantitative results.");
1030 }
1031 }
1032 STORM_PRINT('\n');
1033}
1034
1036 STORM_PRINT("\nModel checking property \"" << property.getName() << "\": " << *property.getRawFormula() << " ...\n");
1037}
1038
1039template<typename ValueType>
1040 requires(!storm::IsIntervalType<ValueType>)
1041void printResult(std::unique_ptr<storm::modelchecker::CheckResult> const& result, storm::logic::Formula const& filterStatesFormula,
1042 storm::modelchecker::FilterType const& filterType, storm::utility::Stopwatch* watch = nullptr) {
1043 if (result) {
1044 std::stringstream ss;
1045 ss << "'" << filterStatesFormula << "'";
1046 STORM_PRINT((storm::utility::resources::isTerminate() ? "Result till abort" : "Result")
1047 << " (for " << (filterStatesFormula.isInitialFormula() ? "initial" : ss.str()) << " states): ");
1048 printFilteredResult<ValueType>(result, filterType);
1049 if (watch) {
1050 STORM_PRINT("Time for model checking: " << *watch << ".\n");
1051 }
1052 } else {
1053 STORM_LOG_ERROR("Property is unsupported by selected engine/settings.\n");
1054 }
1055}
1056
1057template<typename ValueType>
1058void printResult(std::unique_ptr<storm::modelchecker::CheckResult> const& result, storm::jani::Property const& property,
1059 storm::utility::Stopwatch* watch = nullptr) {
1060 printResult<ValueType>(result, *property.getFilter().getStatesFormula(), property.getFilter().getFilterType(), watch);
1061}
1062
1063using VerificationCallbackType = std::function<std::unique_ptr<storm::modelchecker::CheckResult>(std::shared_ptr<storm::logic::Formula const> const& formula,
1064 std::shared_ptr<storm::logic::Formula const> const& states)>;
1065using PostprocessingCallbackType = std::function<void(std::unique_ptr<storm::modelchecker::CheckResult> const&)>;
1066
1068 void operator()(std::unique_ptr<storm::modelchecker::CheckResult> const&) {
1069 // Intentionally left empty.
1070 }
1071};
1072
1079template<typename ValueType>
1080std::unique_ptr<storm::modelchecker::CheckResult> verifyProperty(std::shared_ptr<storm::logic::Formula const> const& formula,
1081 std::shared_ptr<storm::logic::Formula const> const& statesFilter,
1082 VerificationCallbackType const& verificationCallback) {
1083 auto transformationSettings = storm::settings::getModule<storm::settings::modules::TransformationSettings>();
1084
1085 try {
1086 if constexpr (storm::IsIntervalType<ValueType>) {
1087 STORM_LOG_ASSERT(!transformationSettings.isChainEliminationSet() && !transformationSettings.isToNondeterministicModelSet(),
1088 "Unsupported transformation has been invoked.");
1089 return verificationCallback(formula, statesFilter);
1090 }
1091 if (transformationSettings.isChainEliminationSet() && !storm::transformer::NonMarkovianChainTransformer<ValueType>::preservesFormula(*formula)) {
1092 STORM_LOG_WARN("Property is not preserved by elimination of non-markovian states.");
1093 } else if (transformationSettings.isToDiscreteTimeModelSet()) {
1094 auto transformedFormula = storm::api::checkAndTransformContinuousToDiscreteTimeFormula<ValueType>(*formula);
1095 auto transformedStatesFilter = storm::api::checkAndTransformContinuousToDiscreteTimeFormula<ValueType>(*statesFilter);
1096 if (transformedFormula && transformedStatesFilter) {
1097 // invoke verification algorithm on transformed formulas
1098 return verificationCallback(transformedFormula, transformedStatesFilter);
1099 } else {
1100 STORM_LOG_WARN("Property is not preserved by transformation to discrete time model.");
1101 }
1102 } else {
1103 // invoke verification algorithm on given formulas
1104 return verificationCallback(formula, statesFilter);
1105 }
1106 } catch (storm::exceptions::BaseException const& ex) {
1107 STORM_LOG_WARN("Cannot handle property: " << ex.what());
1108 }
1109 return nullptr;
1110}
1111
1118template<typename ValueType>
1119void verifyProperties(SymbolicInput const& input, VerificationCallbackType const& verificationCallback,
1120 PostprocessingCallbackType const& postprocessingCallback = PostprocessingIdentity()) {
1121 auto const& properties = input.preprocessedProperties ? input.preprocessedProperties.get() : input.properties;
1122 for (auto const& property : properties) {
1124 storm::utility::Stopwatch watch(true);
1125 auto result = verifyProperty<ValueType>(property.getRawFormula(), property.getFilter().getStatesFormula(), verificationCallback);
1126 watch.stop();
1127 if (result) {
1128 postprocessingCallback(result);
1129 }
1130 printResult<storm::IntervalBaseType<ValueType>>(result, property, &watch);
1131 }
1132}
1133
1143template<typename ValueType>
1144void computeStateValues(std::string const& description, std::function<std::unique_ptr<storm::modelchecker::CheckResult>()> const& computationCallback,
1145 SymbolicInput const& input, VerificationCallbackType const& verificationCallback,
1146 PostprocessingCallbackType const& postprocessingCallback = PostprocessingIdentity()) {
1147 // First compute the state values for all the states by invoking the computationCallback
1148 storm::utility::Stopwatch watch(true);
1149 STORM_PRINT("\nComputing " << description << " ...\n");
1150 std::unique_ptr<storm::modelchecker::CheckResult> result;
1151 try {
1152 result = computationCallback();
1153 } catch (storm::exceptions::BaseException const& ex) {
1154 STORM_LOG_ERROR("Cannot compute " << description << ": " << ex.what());
1155 }
1156 if (!result) {
1157 STORM_LOG_ERROR("Computation had no result.");
1158 return;
1159 }
1160 // Now process the (potentially filtered) result
1161 if (input.properties.empty()) {
1162 // Do not apply any filtering, consider result for *all* states
1163 postprocessingCallback(result);
1164 printResult<ValueType>(result, *storm::logic::Formula::getTrueFormula(), storm::modelchecker::FilterType::VALUES, &watch);
1165 } else {
1166 // Each property identifies a subset of states to which we restrict (aka filter) the state-value result to
1167 auto const& properties = input.preprocessedProperties ? input.preprocessedProperties.get() : input.properties;
1168 for (uint64_t propertyIndex = 0; propertyIndex < properties.size(); ++propertyIndex) {
1169 auto const& property = properties[propertyIndex];
1170 // As the property serves as filter, it should (a) be qualitative and should (b) not consider a filter itself.
1171 if (!property.getRawFormula()->hasQualitativeResult()) {
1172 STORM_LOG_ERROR("Property '" << *property.getRawFormula()
1173 << "' can not be used for filtering states as it does not have a qualitative result.");
1174 continue;
1175 }
1176
1177 // Invoke verification algorithm on filtering property
1178 auto propertyFilter = verifyProperty<ValueType>(property.getRawFormula(), storm::logic::Formula::getTrueFormula(), verificationCallback);
1179
1180 if (propertyFilter) {
1181 // Filter and process result
1182 std::unique_ptr<storm::modelchecker::CheckResult> filteredResult = result->clone();
1183 filteredResult->filter(propertyFilter->asQualitativeCheckResult());
1184 postprocessingCallback(filteredResult);
1185 printResult<ValueType>(filteredResult, *property.getRawFormula(), property.getFilter().getFilterType(),
1186 propertyIndex == properties.size() - 1 ? &watch : nullptr);
1187 }
1188 }
1189 }
1190}
1191
1192inline std::vector<storm::expressions::Expression> parseConstraints(storm::expressions::ExpressionManager const& expressionManager,
1193 std::string const& constraintsString) {
1194 std::vector<storm::expressions::Expression> constraints;
1195
1196 std::vector<std::string> constraintsAsStrings;
1197 boost::split(constraintsAsStrings, constraintsString, boost::is_any_of(","));
1198
1199 storm::parser::ExpressionParser expressionParser(expressionManager);
1200 std::unordered_map<std::string, storm::expressions::Expression> variableMapping;
1201 for (auto const& variableTypePair : expressionManager) {
1202 variableMapping[variableTypePair.first.getName()] = variableTypePair.first;
1203 }
1204 expressionParser.setIdentifierMapping(variableMapping);
1205
1206 for (auto const& constraintString : constraintsAsStrings) {
1207 if (constraintString.empty()) {
1208 continue;
1209 }
1210
1211 storm::expressions::Expression constraint = expressionParser.parseFromString(constraintString);
1212 STORM_LOG_TRACE("Adding special (user-provided) constraint " << constraint << ".");
1213 constraints.emplace_back(constraint);
1214 }
1215
1216 return constraints;
1217}
1218
1219inline std::vector<std::vector<storm::expressions::Expression>> parseInjectedRefinementPredicates(
1220 storm::expressions::ExpressionManager const& expressionManager, std::string const& refinementPredicatesString) {
1221 std::vector<std::vector<storm::expressions::Expression>> injectedRefinementPredicates;
1222
1223 storm::parser::ExpressionParser expressionParser(expressionManager);
1224 std::unordered_map<std::string, storm::expressions::Expression> variableMapping;
1225 for (auto const& variableTypePair : expressionManager) {
1226 variableMapping[variableTypePair.first.getName()] = variableTypePair.first;
1227 }
1228 expressionParser.setIdentifierMapping(variableMapping);
1229
1230 std::vector<std::string> predicateGroupsAsStrings;
1231 boost::split(predicateGroupsAsStrings, refinementPredicatesString, boost::is_any_of(";"));
1232
1233 if (!predicateGroupsAsStrings.empty()) {
1234 for (auto const& predicateGroupString : predicateGroupsAsStrings) {
1235 if (predicateGroupString.empty()) {
1236 continue;
1237 }
1238
1239 std::vector<std::string> predicatesAsStrings;
1240 boost::split(predicatesAsStrings, predicateGroupString, boost::is_any_of(":"));
1241
1242 if (!predicatesAsStrings.empty()) {
1243 injectedRefinementPredicates.emplace_back();
1244 for (auto const& predicateString : predicatesAsStrings) {
1245 storm::expressions::Expression predicate = expressionParser.parseFromString(predicateString);
1246 STORM_LOG_TRACE("Adding special (user-provided) refinement predicate " << predicateString << ".");
1247 injectedRefinementPredicates.back().emplace_back(predicate);
1248 }
1249
1250 STORM_LOG_THROW(!injectedRefinementPredicates.back().empty(), storm::exceptions::InvalidArgumentException,
1251 "Expecting non-empty list of predicates to inject for each (mentioned) refinement step.");
1252
1253 // Finally reverse the list, because we take the predicates from the back.
1254 std::reverse(injectedRefinementPredicates.back().begin(), injectedRefinementPredicates.back().end());
1255 }
1256 }
1257
1258 // Finally reverse the list, because we take the predicates from the back.
1259 std::reverse(injectedRefinementPredicates.begin(), injectedRefinementPredicates.end());
1260 }
1261
1262 return injectedRefinementPredicates;
1263}
1264
1265template<storm::dd::DdType DdType, typename ValueType>
1267 STORM_LOG_ASSERT(input.model, "Expected symbolic model description.");
1268 storm::settings::modules::AbstractionSettings const& abstractionSettings = storm::settings::getModule<storm::settings::modules::AbstractionSettings>();
1270 parseConstraints(input.model->getManager(), abstractionSettings.getConstraintString()),
1271 parseInjectedRefinementPredicates(input.model->getManager(), abstractionSettings.getInjectedRefinementPredicates()));
1272
1273 verifyProperties<ValueType>(input, [&input, &options, &mpi](std::shared_ptr<storm::logic::Formula const> const& formula,
1274 std::shared_ptr<storm::logic::Formula const> const& states) {
1275 STORM_LOG_THROW(states->isInitialFormula(), storm::exceptions::NotSupportedException, "Abstraction-refinement can only filter initial states.");
1276 return storm::gbar::api::verifyWithAbstractionRefinementEngine<DdType, ValueType>(mpi.env, input.model.get(),
1277 storm::api::createTask<ValueType>(formula, true), options);
1278 });
1279}
1280
1281template<typename ValueType>
1283 STORM_LOG_ASSERT(input.model, "Expected symbolic model description.");
1284 STORM_LOG_THROW((std::is_same<ValueType, double>::value), storm::exceptions::NotSupportedException,
1285 "Exploration does not support other data-types than floating points.");
1286 verifyProperties<ValueType>(
1287 input, [&input, &mpi](std::shared_ptr<storm::logic::Formula const> const& formula, std::shared_ptr<storm::logic::Formula const> const& states) {
1288 STORM_LOG_THROW(states->isInitialFormula(), storm::exceptions::NotSupportedException, "Exploration can only filter initial states.");
1289 return storm::api::verifyWithExplorationEngine<ValueType>(mpi.env, input.model.get(), storm::api::createTask<ValueType>(formula, true));
1290 });
1291}
1292
1293template<typename ValueType>
1294void verifyModel(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& sparseModel, SymbolicInput const& input,
1295 ModelProcessingInformation const& mpi) {
1296 auto const& ioSettings = storm::settings::getModule<storm::settings::modules::IOSettings>();
1297 auto verificationCallback = [&sparseModel, &ioSettings, &mpi](std::shared_ptr<storm::logic::Formula const> const& formula,
1298 std::shared_ptr<storm::logic::Formula const> const& states) {
1299 auto createTask = [&ioSettings](auto const& f, bool onlyInitialStates) {
1300 if constexpr (storm::IsIntervalType<ValueType>) {
1301 STORM_LOG_THROW(ioSettings.isUncertaintyResolutionModeSet(), storm::exceptions::InvalidSettingsException,
1302 "Uncertainty resolution mode required for uncertain (interval) models.");
1303 return storm::api::createTask<ValueType>(f, storm::solver::convert(ioSettings.getUncertaintyResolutionMode()), onlyInitialStates);
1304 } else {
1305 (void)ioSettings; // suppress unused lambda capture warning. [[maybe_unused]] doesn't work for lambda captures.
1306 return storm::api::createTask<ValueType>(f, onlyInitialStates);
1307 }
1308 };
1309 bool const filterForInitialStates = states->isInitialFormula();
1310 auto task = createTask(formula, filterForInitialStates);
1311 if (ioSettings.isExportSchedulerSet()) {
1312 task.setProduceSchedulers(true);
1313 }
1314 std::unique_ptr<storm::modelchecker::CheckResult> result = storm::api::verifyWithSparseEngine<ValueType>(mpi.env, sparseModel, task);
1315
1316 std::unique_ptr<storm::modelchecker::CheckResult> filter;
1317 if (filterForInitialStates) {
1318 filter = std::make_unique<storm::modelchecker::ExplicitQualitativeCheckResult>(sparseModel->getInitialStates());
1319 } else if (!states->isTrueFormula()) { // No need to apply filter if it is the formula 'true'
1320 filter = storm::api::verifyWithSparseEngine<ValueType>(mpi.env, sparseModel, createTask(states, false));
1321 }
1322 if (result && filter) {
1323 result->filter(filter->asQualitativeCheckResult());
1324 }
1325 return result;
1326 };
1327 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.
1328 auto postprocessingCallback = [&sparseModel, &ioSettings, &input, &exportCount](std::unique_ptr<storm::modelchecker::CheckResult> const& result) {
1329 // Scheduler export
1330 STORM_LOG_WARN_COND(!ioSettings.isExportSchedulerSet() || result->hasScheduler(), "Scheduler requested but could not be generated.");
1331 if (ioSettings.isExportSchedulerSet() && result->hasScheduler()) {
1332 std::filesystem::path schedulerExportPath = ioSettings.getExportSchedulerFilename();
1333 if (exportCount > 0) {
1334 STORM_LOG_WARN("Prepending " << exportCount << " to scheduler file name for this property because there are multiple properties.");
1335 schedulerExportPath.replace_filename(std::to_string(exportCount) + schedulerExportPath.filename().string());
1336 }
1337 STORM_PRINT_AND_LOG("Exporting scheduler ... ");
1338 if (input.model) {
1339 STORM_LOG_WARN_COND(sparseModel->hasStateValuations(),
1340 "No information of state valuations available. The scheduler output will use internal state ids. You might be "
1341 "interested in building the model with state valuations using --buildstateval.");
1343 sparseModel->hasChoiceLabeling() || sparseModel->hasChoiceOrigins(),
1344 "No symbolic choice information is available. The scheduler output will use internal choice ids. You might be interested in "
1345 "building the model with choice labels or choice origins using --buildchoicelab or --buildchoiceorig.");
1346 STORM_LOG_WARN_COND(sparseModel->hasChoiceLabeling() && !sparseModel->hasChoiceOrigins(),
1347 "Only partial choice information is available. You might want to build the model with choice origins using "
1348 "--buildchoicelab or --buildchoiceorig.");
1349 }
1350 if (result->isExplicitQuantitativeCheckResult()) {
1351 if constexpr (storm::IsIntervalType<ValueType>) {
1352 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Scheduler export for interval models is not supported.");
1353 } else {
1354 storm::api::exportScheduler(sparseModel, result->template asExplicitQuantitativeCheckResult<ValueType>().getScheduler(),
1355 schedulerExportPath.string());
1356 }
1357 } else if (result->isExplicitParetoCurveCheckResult()) {
1358 if constexpr (std::is_same_v<ValueType, storm::RationalFunction> || storm::IsIntervalType<ValueType>) {
1359 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Scheduler export for models of this value type is not supported.");
1360 } else {
1361 auto const& paretoRes = result->template asExplicitParetoCurveCheckResult<ValueType>();
1362 storm::api::exportParetoScheduler(sparseModel, paretoRes.getPoints(), paretoRes.getSchedulers(), schedulerExportPath.string());
1363 }
1364 } else {
1365 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Scheduler export not supported for this value type.");
1366 }
1367 }
1368
1369 // Result export
1370 if (ioSettings.isExportCheckResultSet()) {
1371 if constexpr (storm::IsIntervalType<ValueType>) {
1372 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Result export for interval models is not supported.");
1373 } else {
1374 std::filesystem::path resultExportPath = ioSettings.getExportCheckResultFilename();
1375 if (exportCount > 0) {
1376 STORM_LOG_WARN("Prepending " << exportCount << " to result file name for this property because there are multiple properties.");
1377 resultExportPath.replace_filename(std::to_string(exportCount) + resultExportPath.filename().string());
1378 }
1379 STORM_LOG_WARN_COND(sparseModel->hasStateValuations(),
1380 "No information of state valuations available. The result output will use internal state ids. You might be interested in "
1381 "building the model with state valuations using --buildstateval.");
1382 storm::api::exportCheckResultToJson(sparseModel, result, resultExportPath);
1383 }
1384 }
1385 ++exportCount;
1386 };
1387 if (!(ioSettings.isComputeSteadyStateDistributionSet() || ioSettings.isComputeExpectedVisitingTimesSet())) {
1388 verifyProperties<ValueType>(input, verificationCallback, postprocessingCallback);
1389 }
1390 if (ioSettings.isComputeSteadyStateDistributionSet()) {
1391 if constexpr (storm::IsIntervalType<ValueType>) {
1392 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Computing steady state distribution is not supported for interval models.");
1393 } else {
1394 computeStateValues<ValueType>(
1395 "steady-state probabilities",
1396 [&mpi, &sparseModel]() { return storm::api::computeSteadyStateDistributionWithSparseEngine<ValueType>(mpi.env, sparseModel); }, input,
1397 verificationCallback, postprocessingCallback);
1398 }
1399 }
1400 if (ioSettings.isComputeExpectedVisitingTimesSet()) {
1401 if constexpr (storm::IsIntervalType<ValueType>) {
1402 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Computing expected visiting times is not supported for interval models.");
1403 } else {
1404 computeStateValues<ValueType>(
1405 "expected visiting times",
1406 [&mpi, &sparseModel]() { return storm::api::computeExpectedVisitingTimesWithSparseEngine<ValueType>(mpi.env, sparseModel); }, input,
1407 verificationCallback, postprocessingCallback);
1408 }
1409 }
1410}
1411
1412template<storm::dd::DdType DdType, typename ValueType>
1413void verifyWithHybridEngine(std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& symbolicModel, SymbolicInput const& input,
1414 ModelProcessingInformation const& mpi) {
1415 verifyProperties<ValueType>(
1416 input, [&symbolicModel, &mpi](std::shared_ptr<storm::logic::Formula const> const& formula, std::shared_ptr<storm::logic::Formula const> const& states) {
1417 bool filterForInitialStates = states->isInitialFormula();
1418 auto task = storm::api::createTask<ValueType>(formula, filterForInitialStates);
1419
1420 std::unique_ptr<storm::modelchecker::CheckResult> result = storm::api::verifyWithHybridEngine<DdType, ValueType>(mpi.env, symbolicModel, task);
1421
1422 std::unique_ptr<storm::modelchecker::CheckResult> filter;
1423 if (filterForInitialStates) {
1424 filter = std::make_unique<storm::modelchecker::SymbolicQualitativeCheckResult<DdType>>(symbolicModel->getReachableStates(),
1425 symbolicModel->getInitialStates());
1426 } else if (!states->isTrueFormula()) { // No need to apply filter if it is the formula 'true'
1427 filter = storm::api::verifyWithHybridEngine<DdType, ValueType>(mpi.env, symbolicModel, storm::api::createTask<ValueType>(states, false));
1428 }
1429 if (result && filter) {
1430 result->filter(filter->asQualitativeCheckResult());
1431 }
1432 return result;
1433 });
1434}
1435
1436template<storm::dd::DdType DdType, typename ValueType>
1437void verifyWithDdEngine(std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& symbolicModel, SymbolicInput const& input,
1438 ModelProcessingInformation const& mpi) {
1439 verifyProperties<ValueType>(
1440 input, [&symbolicModel, &mpi](std::shared_ptr<storm::logic::Formula const> const& formula, std::shared_ptr<storm::logic::Formula const> const& states) {
1441 bool filterForInitialStates = states->isInitialFormula();
1442 auto task = storm::api::createTask<ValueType>(formula, filterForInitialStates);
1443
1444 std::unique_ptr<storm::modelchecker::CheckResult> result =
1445 storm::api::verifyWithDdEngine<DdType, ValueType>(mpi.env, symbolicModel, storm::api::createTask<ValueType>(formula, true));
1446
1447 std::unique_ptr<storm::modelchecker::CheckResult> filter;
1448 if (filterForInitialStates) {
1449 filter = std::make_unique<storm::modelchecker::SymbolicQualitativeCheckResult<DdType>>(symbolicModel->getReachableStates(),
1450 symbolicModel->getInitialStates());
1451 } else if (!states->isTrueFormula()) { // No need to apply filter if it is the formula 'true'
1452 filter = storm::api::verifyWithDdEngine<DdType, ValueType>(mpi.env, symbolicModel, storm::api::createTask<ValueType>(states, false));
1453 }
1454 if (result && filter) {
1455 result->filter(filter->asQualitativeCheckResult());
1456 }
1457 return result;
1458 });
1459}
1460
1461template<storm::dd::DdType DdType, typename ValueType>
1463 ModelProcessingInformation const& mpi) {
1464 verifyProperties<ValueType>(
1465 input, [&symbolicModel, &mpi](std::shared_ptr<storm::logic::Formula const> const& formula, std::shared_ptr<storm::logic::Formula const> const& states) {
1466 STORM_LOG_THROW(states->isInitialFormula(), storm::exceptions::NotSupportedException, "Abstraction-refinement can only filter initial states.");
1467 return storm::gbar::api::verifyWithAbstractionRefinementEngine<DdType, ValueType>(mpi.env, symbolicModel,
1468 storm::api::createTask<ValueType>(formula, true));
1469 });
1470}
1471
1472template<storm::dd::DdType DdType, typename ValueType>
1473typename std::enable_if<DdType != storm::dd::DdType::CUDD || std::is_same<ValueType, double>::value, void>::type verifyModel(
1474 std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& symbolicModel, SymbolicInput const& input,
1475 ModelProcessingInformation const& mpi) {
1477 verifyWithHybridEngine<DdType, ValueType>(symbolicModel, input, mpi);
1478 } else if (mpi.engine == storm::utility::Engine::Dd) {
1479 verifyWithDdEngine<DdType, ValueType>(symbolicModel, input, mpi);
1480 } else {
1481 verifyWithAbstractionRefinementEngine<DdType, ValueType>(symbolicModel, input, mpi);
1482 }
1483}
1484
1485template<storm::dd::DdType DdType, typename ValueType>
1486typename std::enable_if<DdType == storm::dd::DdType::CUDD && !std::is_same<ValueType, double>::value, void>::type verifySymbolicModel(
1487 std::shared_ptr<storm::models::ModelBase> const&, SymbolicInput const&, ModelProcessingInformation const&) {
1488 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "CUDD does not support the selected data-type.");
1489}
1490
1491inline std::shared_ptr<storm::models::ModelBase> buildPreprocessModel(SymbolicInput const& input, ModelProcessingInformation const& mpi) {
1492 auto ioSettings = storm::settings::getModule<storm::settings::modules::IOSettings>();
1493 auto buildSettings = storm::settings::getModule<storm::settings::modules::BuildSettings>();
1494
1495 std::shared_ptr<storm::models::ModelBase> model;
1496 if (!buildSettings.isNoBuildModelSet()) {
1497 model = buildModel(input, ioSettings, mpi);
1498 }
1499 if (!model) {
1500 STORM_LOG_THROW(input.properties.empty(), storm::exceptions::InvalidSettingsException, "No input model.");
1501 return nullptr;
1502 }
1503 model->printModelInformationToStream(std::cout);
1504
1505 storm::utility::Stopwatch preprocessingWatch(true);
1506 auto preprocessingResult = castAndApply(model, [&input, &mpi](auto const& m) { return preprocessModel(m, input, mpi); });
1507 preprocessingWatch.stop();
1508 if (preprocessingResult.second) {
1509 STORM_PRINT("\nTime for model preprocessing: " << preprocessingWatch << ".\n\n");
1510 model = preprocessingResult.first;
1511 model->printModelInformationToStream(std::cout);
1512 }
1513
1514 return model;
1515}
1516
1517inline std::shared_ptr<storm::models::ModelBase> buildPreprocessExportModel(SymbolicInput const& input, ModelProcessingInformation const& mpi) {
1518 auto model = buildPreprocessModel(input, mpi);
1519 if (model) {
1520 castAndApply(model, [&input](auto const& m) { exportModel(m, input); });
1521 }
1522 return model;
1523}
1524
1525inline void processInput(SymbolicInput const& input, ModelProcessingInformation const& mpi) {
1526 auto abstractionSettings = storm::settings::getModule<storm::settings::modules::AbstractionSettings>();
1527 auto counterexampleSettings = storm::settings::getModule<storm::settings::modules::CounterexampleGeneratorSettings>();
1528
1529 // For several engines, no model building step is performed, but the verification is started right away.
1531 abstractionSettings.getAbstractionRefinementMethod() == storm::settings::modules::AbstractionSettings::Method::Games) {
1533 [&input, &mpi]<storm::dd::DdType DD, typename VT>() { verifyWithAbstractionRefinementEngine<DD, VT>(input, mpi); });
1534 } else if (mpi.engine == storm::utility::Engine::Exploration) {
1535 applyValueType(mpi.verificationValueType, [&input, &mpi]<typename VT>() { verifyWithExplorationEngine<VT>(input, mpi); });
1536 } else {
1537 std::shared_ptr<storm::models::ModelBase> model = buildPreprocessExportModel(input, mpi);
1538 if (model) {
1539 if (counterexampleSettings.isCounterexampleSet()) {
1540 castAndApply(model, [&input](auto const& m) { generateCounterexamples(m, input); });
1541 } else {
1542 castAndApply(model, [&input, &mpi](auto const& m) { verifyModel(m, input, mpi); });
1543 }
1544 }
1545 }
1546}
1547} // namespace cli
1548} // 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:20
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 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:66
void exportSymbolicModelAsDot(std::shared_ptr< storm::models::symbolic::Model< Type, ValueType > > const &model, std::string const &filename)
Definition export.h:61
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:40
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)
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:113
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:53
void exportSparseModelAsDot(std::shared_ptr< storm::models::sparse::Model< ValueType > > const &model, std::string const &filename, size_t maxWidth=30)
Definition export.h:45
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:80
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(ModelExportFormat 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