29template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
35template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
41 "Upper bound '" <<
upperBound <<
"' is smaller than lower bound '" <<
lowerBound <<
"': Difference is " <<
diff <<
".");
50template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
59template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
68template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
69BeliefExplorationPomdpModelChecker<PomdpModelType, BeliefValueType, BeliefMDPType>::Statistics::Statistics()
70 : beliefMdpDetectedToBeFinite(false),
71 refinementFixpointDetected(false),
72 overApproximationBuildAborted(false),
73 underApproximationBuildAborted(false),
78template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
83 beliefTypeCC(
storm::
utility::convertNumber<BeliefValueType>(this->options.numericPrecision), false),
84 valueTypeCC(this->options.numericPrecision, false) {
86 STORM_LOG_ERROR_COND(inputPomdp->isCanonic(),
"Input Pomdp is not known to be canonic. This might lead to unexpected verification results.");
91template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
99 auto initialPomdpValueBounds = preProcessingMC.getValueBounds(preProcEnv, formula);
100 pomdpValueBounds.trivialPomdpValueBounds = initialPomdpValueBounds;
104 pomdpValueBounds.extremePomdpValueBound = preProcessingMC.getExtremeValueBound(preProcEnv, formula);
108template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
112 std::vector<std::vector<std::unordered_map<uint64_t, ValueType>>>
const& additionalUnderApproximationBounds) {
113 return check(env, formula, env, additionalUnderApproximationBounds);
116template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
119 storm::logic::Formula const& formula, std::vector<std::vector<std::unordered_map<uint64_t, ValueType>>>
const& additionalUnderApproximationBounds) {
121 return check(env, formula, env, additionalUnderApproximationBounds);
124template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
128 std::vector<std::vector<std::unordered_map<uint64_t, ValueType>>>
const& additionalUnderApproximationBounds) {
130 return check(env, formula, preProcEnv, additionalUnderApproximationBounds);
133template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
137 std::vector<std::vector<std::unordered_map<uint64_t, ValueType>>>
const& additionalUnderApproximationBounds) {
138 STORM_LOG_ASSERT(options.unfold || options.discretize || options.interactiveUnfolding,
139 "Invoked belief exploration but no task (unfold or discretize) given.");
141 preprocessedPomdp.reset();
144 statistics = Statistics();
145 statistics.totalTime.start();
150 if (!additionalUnderApproximationBounds.empty()) {
151 pomdpValueBounds.fmSchedulerValueList = additionalUnderApproximationBounds;
153 uint64_t initialPomdpState = pomdp().getInitialStates().getNextSetIndex(0);
154 Result result(pomdpValueBounds.trivialPomdpValueBounds.getHighestLowerBound(initialPomdpState),
155 pomdpValueBounds.trivialPomdpValueBounds.getSmallestUpperBound(initialPomdpState));
158 std::optional<std::string> rewardModelName;
159 std::set<uint32_t> targetObservations;
160 if (formulaInfo.isNonNestedReachabilityProbability() || formulaInfo.isNonNestedExpectedRewardFormula()) {
161 if (formulaInfo.getTargetStates().observationClosed) {
162 targetObservations = formulaInfo.getTargetStates().observations;
165 std::tie(preprocessedPomdp, targetObservations) = obsCloser.
transform(formulaInfo.getTargetStates().states);
167 if (formulaInfo.isNonNestedReachabilityProbability()) {
168 if (!formulaInfo.getSinkStates().empty()) {
172 auto matrix = pomdp().getTransitionMatrix();
173 matrix.makeRowGroupsAbsorbing(formulaInfo.getSinkStates().states);
176 if (pomdp().hasChoiceLabeling()) {
179 if (pomdp().hasObservationValuations()) {
182 preprocessedPomdp = std::make_shared<storm::models::sparse::Pomdp<ValueType>>(std::move(components),
true);
184 pomdp().getTransitionMatrix(), formulaInfo.getSinkStates().states, formulaInfo.getSinkStates().states, ~formulaInfo.getSinkStates().states);
185 reachableFromSinkStates &= ~formulaInfo.getSinkStates().states;
186 STORM_LOG_THROW(reachableFromSinkStates.empty(), storm::exceptions::NotSupportedException,
187 "There are sink states that can reach non-sink states. This is currently not supported.");
191 rewardModelName = formulaInfo.getRewardModelName();
194 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"Unsupported formula '" << formula <<
"'.");
198 statistics.beliefMdpDetectedToBeFinite =
true;
200 if (options.interactiveUnfolding) {
201 unfoldInteractively(env, targetObservations, formulaInfo.minimize(), rewardModelName, pomdpValueBounds, result);
203 refineReachability(env, targetObservations, formulaInfo.minimize(), rewardModelName, pomdpValueBounds, result);
206 if ((formulaInfo.minimize() && !options.discretize) || (formulaInfo.maximize() && !options.unfold)) {
209 if ((formulaInfo.maximize() && !options.discretize) || (formulaInfo.minimize() && !options.unfold)) {
214 statistics.aborted =
true;
216 statistics.totalTime.stop();
220template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
222 stream <<
"##### POMDP Approximation Statistics ######\n";
223 stream <<
"# Input model: \n";
224 pomdp().printModelInformationToStream(stream);
225 stream <<
"# Max. Number of states with same observation: " << pomdp().getMaxNrStatesWithSameObservation() <<
'\n';
226 if (statistics.beliefMdpDetectedToBeFinite) {
227 stream <<
"# Pre-computations detected that the belief MDP is finite.\n";
229 if (statistics.aborted) {
230 stream <<
"# Computation aborted early\n";
233 stream <<
"# Total check time: " << statistics.totalTime <<
'\n';
235 if (statistics.refinementSteps) {
236 stream <<
"# Number of refinement steps: " << statistics.refinementSteps.value() <<
'\n';
238 if (statistics.refinementFixpointDetected) {
239 stream <<
"# Detected a refinement fixpoint.\n";
243 if (statistics.overApproximationStates) {
244 stream <<
"# Number of states in the ";
245 if (options.refine) {
248 stream <<
"grid MDP for the over-approximation: ";
249 if (statistics.overApproximationBuildAborted) {
252 stream << statistics.overApproximationStates.value() <<
'\n';
253 stream <<
"# Maximal resolution for over-approximation: " << statistics.overApproximationMaxResolution.value() <<
'\n';
254 stream <<
"# Time spend for building the over-approx grid MDP(s): " << statistics.overApproximationBuildTime <<
'\n';
255 stream <<
"# Time spend for checking the over-approx grid MDP(s): " << statistics.overApproximationCheckTime <<
'\n';
259 if (statistics.underApproximationStates) {
260 stream <<
"# Number of states in the ";
261 if (options.refine) {
264 stream <<
"belief MDP for the under-approximation: ";
265 if (statistics.underApproximationBuildAborted) {
268 stream << statistics.underApproximationStates.value() <<
'\n';
269 if (statistics.nrClippingAttempts) {
270 stream <<
"# Clipping attempts (clipped states) for the under-approximation: ";
271 if (statistics.underApproximationBuildAborted) {
274 stream << statistics.nrClippingAttempts.value() <<
" (" << statistics.nrClippedStates.value() <<
")\n";
275 stream <<
"# Total clipping preprocessing time: " << statistics.clippingPreTime <<
"\n";
276 stream <<
"# Total clipping time: " << statistics.clipWatch <<
"\n";
277 }
else if (statistics.nrTruncatedStates) {
278 stream <<
"# Truncated states for the under-approximation: ";
279 if (statistics.underApproximationBuildAborted) {
282 stream << statistics.nrTruncatedStates.value() <<
"\n";
284 if (statistics.underApproximationStateLimit) {
285 stream <<
"# Exploration state limit for under-approximation: " << statistics.underApproximationStateLimit.value() <<
'\n';
287 stream <<
"# Time spend for building the under-approx grid MDP(s): " << statistics.underApproximationBuildTime <<
'\n';
288 stream <<
"# Time spend for checking the under-approx grid MDP(s): " << statistics.underApproximationCheckTime <<
'\n';
291 stream <<
"##########################################\n";
296template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
297PomdpModelType
const& BeliefExplorationPomdpModelChecker<PomdpModelType, BeliefValueType, BeliefMDPType>::pomdp()
const {
298 if (preprocessedPomdp) {
299 return *preprocessedPomdp;
305template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
306void BeliefExplorationPomdpModelChecker<PomdpModelType, BeliefValueType, BeliefMDPType>::refineReachability(
307 storm::Environment const& env, std::set<uint32_t>
const& targetObservations,
bool min, std::optional<std::string> rewardModelName,
309 statistics.refinementSteps = 0;
312 std::vector<BeliefValueType> observationResolutionVector;
313 std::shared_ptr<BeliefManagerType> overApproxBeliefManager;
314 std::shared_ptr<ExplorerType> overApproximation;
315 HeuristicParameters overApproxHeuristicPar{};
316 if (options.discretize) {
317 observationResolutionVector =
319 overApproxBeliefManager = std::make_shared<BeliefManagerType>(
321 options.dynamicTriangulation ? BeliefManagerType::TriangulationMode::Dynamic : BeliefManagerType::TriangulationMode::Static);
322 if (rewardModelName) {
323 overApproxBeliefManager->setRewardModel(rewardModelName);
326 overApproxHeuristicPar.gapThreshold = options.gapThresholdInit;
327 overApproxHeuristicPar.observationThreshold = options.obsThresholdInit;
328 overApproxHeuristicPar.sizeThreshold = options.sizeThresholdInit == 0 ? std::numeric_limits<uint64_t>::max() : options.sizeThresholdInit;
329 overApproxHeuristicPar.optimalChoiceValueEpsilon = options.optimalChoiceValueThresholdInit;
331 buildOverApproximation(env, targetObservations, min, rewardModelName.has_value(),
false, overApproxHeuristicPar, observationResolutionVector,
332 overApproxBeliefManager, overApproximation);
336 ValueType const& newValue = overApproximation->getComputedValueAtInitialState();
337 bool betterBound =
min ? result.updateLowerBound(newValue) : result.updateUpperBound(newValue);
339 STORM_LOG_INFO(
"Initial Over-approx result obtained after " << statistics.totalTime <<
". Value is '" << newValue <<
"'.\n");
343 std::shared_ptr<BeliefManagerType> underApproxBeliefManager;
344 std::shared_ptr<ExplorerType> underApproximation;
345 HeuristicParameters underApproxHeuristicPar{};
346 if (options.unfold) {
347 underApproxBeliefManager = std::make_shared<BeliefManagerType>(
349 options.dynamicTriangulation ? BeliefManagerType::TriangulationMode::Dynamic : BeliefManagerType::TriangulationMode::Static);
350 if (rewardModelName) {
351 underApproxBeliefManager->setRewardModel(rewardModelName);
353 underApproximation = std::make_shared<ExplorerType>(underApproxBeliefManager, trivialPOMDPBounds, options.explorationHeuristic);
354 underApproxHeuristicPar.gapThreshold = options.gapThresholdInit;
355 underApproxHeuristicPar.optimalChoiceValueEpsilon = options.optimalChoiceValueThresholdInit;
356 underApproxHeuristicPar.sizeThreshold = options.sizeThresholdInit;
357 if (underApproxHeuristicPar.sizeThreshold == 0) {
358 if (!options.refine && options.explorationTimeLimit != 0) {
359 underApproxHeuristicPar.sizeThreshold = std::numeric_limits<uint64_t>::max();
361 underApproxHeuristicPar.sizeThreshold = pomdp().getNumberOfStates() * pomdp().getMaxNrStatesWithSameObservation();
362 STORM_LOG_INFO(
"Heuristically selected an under-approximation MDP size threshold of " << underApproxHeuristicPar.sizeThreshold <<
".\n");
366 if (options.useClipping && rewardModelName.has_value()) {
372 buildUnderApproximation(env, targetObservations, min, rewardModelName.has_value(),
false, underApproxHeuristicPar, underApproxBeliefManager,
373 underApproximation,
false);
377 ValueType const& newValue = underApproximation->getComputedValueAtInitialState();
378 bool betterBound =
min ? result.updateUpperBound(newValue) : result.updateLowerBound(newValue);
380 STORM_LOG_INFO(
"Initial Under-approx result obtained after " << statistics.totalTime <<
". Value is '" << newValue <<
"'.\n");
385 STORM_LOG_INFO(
"Completed (initial) computation. Current checktime is " << statistics.totalTime <<
".");
386 bool computingLowerBound =
false;
387 bool computingUpperBound =
false;
388 if (options.discretize) {
389 STORM_LOG_INFO(
"\tOver-approx MDP has size " << overApproximation->getExploredMdp()->getNumberOfStates() <<
".");
390 (
min ? computingLowerBound : computingUpperBound) =
true;
392 if (options.unfold) {
393 STORM_LOG_INFO(
"\tUnder-approx MDP has size " << underApproximation->getExploredMdp()->getNumberOfStates() <<
".");
394 (
min ? computingUpperBound : computingLowerBound) =
true;
396 if (computingLowerBound && computingUpperBound) {
397 STORM_LOG_INFO(
"\tObtained result is [" << result.lowerBound <<
", " << result.upperBound <<
"].");
398 }
else if (computingLowerBound) {
399 STORM_LOG_INFO(
"\tObtained result is ≥" << result.lowerBound <<
".");
400 }
else if (computingUpperBound) {
401 STORM_LOG_INFO(
"\tObtained result is ≤" << result.upperBound <<
".");
405 if (options.refine) {
407 "No termination criterion for refinement given. Consider to specify a steplimit, a non-zero precisionlimit, or a timeout");
409 "Refinement goal precision is given, but only one bound is going to be refined.");
410 while ((options.refineStepLimit == 0 || statistics.refinementSteps.value() < options.refineStepLimit) && result.diff() > options.refinePrecision) {
411 bool overApproxFixPoint =
true;
412 bool underApproxFixPoint =
true;
413 if (options.discretize) {
416 overApproximation->takeCurrentValuesAsLowerBounds();
418 overApproximation->takeCurrentValuesAsUpperBounds();
420 overApproxHeuristicPar.gapThreshold *= options.gapThresholdFactor;
423 overApproxHeuristicPar.observationThreshold +=
425 overApproxHeuristicPar.optimalChoiceValueEpsilon *= options.optimalChoiceValueThresholdFactor;
426 overApproxFixPoint = buildOverApproximation(env, targetObservations, min, rewardModelName.has_value(),
true, overApproxHeuristicPar,
427 observationResolutionVector, overApproxBeliefManager, overApproximation);
429 ValueType const& newValue = overApproximation->getComputedValueAtInitialState();
430 bool betterBound =
min ? result.updateLowerBound(newValue) : result.updateUpperBound(newValue);
432 STORM_LOG_INFO(
"Over-approx result for refinement improved after " << statistics.totalTime <<
" in refinement step #"
433 << (statistics.refinementSteps.value() + 1) <<
". New value is '"
434 << newValue <<
"'.");
441 if (options.unfold && result.diff() > options.refinePrecision) {
443 underApproxHeuristicPar.gapThreshold *= options.gapThresholdFactor;
446 options.sizeThresholdFactor);
447 underApproxHeuristicPar.optimalChoiceValueEpsilon *= options.optimalChoiceValueThresholdFactor;
448 underApproxFixPoint = buildUnderApproximation(env, targetObservations, min, rewardModelName.has_value(),
true, underApproxHeuristicPar,
449 underApproxBeliefManager, underApproximation,
true);
451 ValueType const& newValue = underApproximation->getComputedValueAtInitialState();
452 bool betterBound =
min ? result.updateUpperBound(newValue) : result.updateLowerBound(newValue);
454 STORM_LOG_INFO(
"Under-approx result for refinement improved after " << statistics.totalTime <<
" in refinement step #"
455 << (statistics.refinementSteps.value() + 1) <<
". New value is '"
456 << newValue <<
"'.");
466 ++statistics.refinementSteps.value();
468 if (statistics.refinementSteps.value() <= 1000) {
469 STORM_LOG_INFO(
"Completed iteration #" << statistics.refinementSteps.value() <<
". Current checktime is " << statistics.totalTime <<
".");
470 computingLowerBound =
false;
471 computingUpperBound =
false;
472 if (options.discretize) {
473 STORM_LOG_INFO(
"\tOver-approx MDP has size " << overApproximation->getExploredMdp()->getNumberOfStates() <<
".");
474 (
min ? computingLowerBound : computingUpperBound) =
true;
476 if (options.unfold) {
477 STORM_LOG_INFO(
"\tUnder-approx MDP has size " << underApproximation->getExploredMdp()->getNumberOfStates() <<
".");
478 (
min ? computingUpperBound : computingLowerBound) =
true;
480 if (computingLowerBound && computingUpperBound) {
481 STORM_LOG_INFO(
"\tCurrent result is [" << result.lowerBound <<
", " << result.upperBound <<
"].");
482 }
else if (computingLowerBound) {
483 STORM_LOG_INFO(
"\tCurrent result is ≥" << result.lowerBound <<
".");
484 }
else if (computingUpperBound) {
485 STORM_LOG_INFO(
"\tCurrent result is ≤" << result.upperBound <<
".");
487 STORM_LOG_WARN_COND(statistics.refinementSteps.value() < 1000,
"Refinement requires more than 1000 iterations.");
490 if (overApproxFixPoint && underApproxFixPoint) {
491 STORM_LOG_INFO(
"Refinement fixpoint reached after " << statistics.refinementSteps.value() <<
" iterations.\n");
492 statistics.refinementFixpointDetected =
true;
498 if (options.discretize && overApproximation->hasComputedValues()) {
499 auto printOverInfo = [&overApproximation]() {
500 std::stringstream str;
501 str <<
"Explored and checked Over-Approximation MDP:\n";
502 overApproximation->getExploredMdp()->printModelInformationToStream(str);
507 if (options.unfold && underApproximation->hasComputedValues()) {
508 auto printUnderInfo = [&underApproximation]() {
509 std::stringstream str;
510 str <<
"Explored and checked Under-Approximation MDP:\n";
511 underApproximation->getExploredMdp()->printModelInformationToStream(str);
515 std::shared_ptr<storm::models::sparse::Model<ValueType>> scheduledModel = underApproximation->getExploredMdp();
516 if (!options.useStateEliminationCutoff) {
517 storm::models::sparse::StateLabeling newLabeling(scheduledModel->getStateLabeling());
518 auto nrPreprocessingScheds =
min ? underApproximation->getNrSchedulersForUpperBounds() : underApproximation->getNrSchedulersForLowerBounds();
519 for (uint64_t i = 0;
i < nrPreprocessingScheds; ++
i) {
520 newLabeling.addLabel(
"sched_" + std::to_string(i));
522 newLabeling.addLabel(
"cutoff");
523 newLabeling.addLabel(
"clipping");
524 newLabeling.addLabel(
"finite_mem");
526 auto transMatrix = scheduledModel->getTransitionMatrix();
527 for (uint64_t i = 0;
i < scheduledModel->getNumberOfStates(); ++
i) {
528 if (newLabeling.getStateHasLabel(
"truncated", i)) {
529 uint64_t localChosenActionIndex = underApproximation->getSchedulerForExploredMdp()->getChoice(i).getDeterministicChoice();
530 auto rowIndex = scheduledModel->getTransitionMatrix().getRowGroupIndices()[
i];
531 if (scheduledModel->getChoiceLabeling().getLabelsOfChoice(rowIndex + localChosenActionIndex).size() > 0) {
532 auto label = *(scheduledModel->getChoiceLabeling().getLabelsOfChoice(rowIndex + localChosenActionIndex).begin());
533 if (label.rfind(
"clip", 0) == 0) {
534 newLabeling.addLabelToState(
"clipping", i);
535 auto chosenRow = transMatrix.getRow(i, 0);
536 auto candidateIndex = (chosenRow.end() - 1)->getColumn();
537 transMatrix.makeRowDirac(transMatrix.getRowGroupIndices()[i], candidateIndex);
538 }
else if (label.rfind(
"mem_node", 0) == 0) {
539 if (!newLabeling.containsLabel(
"finite_mem_" + label.substr(9, 1))) {
540 newLabeling.addLabel(
"finite_mem_" + label.substr(9, 1));
542 newLabeling.addLabelToState(
"finite_mem_" + label.substr(9, 1), i);
543 newLabeling.addLabelToState(
"cutoff", i);
545 newLabeling.addLabelToState(label, i);
546 newLabeling.addLabelToState(
"cutoff", i);
551 newLabeling.removeLabel(
"truncated");
553 transMatrix.dropZeroEntries();
554 storm::storage::sparse::ModelComponents<ValueType> modelComponents(transMatrix, newLabeling);
555 if (scheduledModel->hasChoiceLabeling()) {
556 modelComponents.choiceLabeling = scheduledModel->getChoiceLabeling();
558 storm::models::sparse::Mdp<ValueType> newMDP(modelComponents);
559 auto inducedMC = newMDP.applyScheduler(*(underApproximation->getSchedulerForExploredMdp()),
true);
560 scheduledModel = std::static_pointer_cast<storm::models::sparse::Model<ValueType>>(inducedMC);
562 auto inducedMC = underApproximation->getExploredMdp()->applyScheduler(*(underApproximation->getSchedulerForExploredMdp()),
true);
563 scheduledModel = std::static_pointer_cast<storm::models::sparse::Model<ValueType>>(inducedMC);
565 result.schedulerAsMarkovChain = scheduledModel;
567 result.cutoffSchedulers = underApproximation->getUpperValueBoundSchedulers();
569 result.cutoffSchedulers = underApproximation->getLowerValueBoundSchedulers();
574template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
576 storm::Environment const& env, std::set<uint32_t>
const& targetObservations,
bool min, std::optional<std::string> rewardModelName,
578 statistics.refinementSteps = 0;
579 interactiveResult = result;
581 unfoldingControl = UnfoldingControl::Run;
584 std::shared_ptr<BeliefManagerType> underApproxBeliefManager;
585 HeuristicParameters underApproxHeuristicPar{};
586 bool firstIteration =
true;
588 underApproxBeliefManager = std::make_shared<BeliefManagerType>(
590 options.dynamicTriangulation ? BeliefManagerType::TriangulationMode::Dynamic : BeliefManagerType::TriangulationMode::Static);
591 if (rewardModelName) {
592 underApproxBeliefManager->setRewardModel(rewardModelName);
596 interactiveUnderApproximationExplorer = std::make_shared<ExplorerType>(underApproxBeliefManager, trivialPOMDPBounds, options.explorationHeuristic);
597 underApproxHeuristicPar.gapThreshold = options.gapThresholdInit;
598 underApproxHeuristicPar.optimalChoiceValueEpsilon = options.optimalChoiceValueThresholdInit;
599 underApproxHeuristicPar.sizeThreshold = std::numeric_limits<uint64_t>::max() - 1;
601 if (options.useClipping && rewardModelName.has_value()) {
610 while (!(unfoldingControl ==
611 storm::pomdp::modelchecker::BeliefExplorationPomdpModelChecker<PomdpModelType, BeliefValueType, BeliefMDPType>::UnfoldingControl::Terminate)) {
612 bool underApproxFixPoint =
true;
613 bool hasTruncatedStates =
false;
616 underApproxFixPoint = buildUnderApproximation(env, targetObservations, min, rewardModelName.has_value(),
false, underApproxHeuristicPar,
617 underApproxBeliefManager, interactiveUnderApproximationExplorer, firstIteration);
619 ValueType const& newValue = interactiveUnderApproximationExplorer->getComputedValueAtInitialState();
620 bool betterBound = min ? interactiveResult.updateUpperBound(newValue) : interactiveResult.updateLowerBound(newValue);
622 STORM_LOG_INFO(
"Under-approximation result improved after " << statistics.totalTime <<
" in step #"
623 << (statistics.refinementSteps.value() + 1) <<
". New value is '" << newValue
626 std::shared_ptr<storm::models::sparse::Model<ValueType>> scheduledModel = interactiveUnderApproximationExplorer->getExploredMdp();
627 if (!options.useStateEliminationCutoff) {
629 auto nrPreprocessingScheds = min ? interactiveUnderApproximationExplorer->getNrSchedulersForUpperBounds()
630 : interactiveUnderApproximationExplorer->getNrSchedulersForLowerBounds();
631 for (uint64_t i = 0; i < nrPreprocessingScheds; ++i) {
632 newLabeling.
addLabel(
"sched_" + std::to_string(i));
638 auto transMatrix = scheduledModel->getTransitionMatrix();
639 for (uint64_t i = 0; i < scheduledModel->getNumberOfStates(); ++i) {
641 hasTruncatedStates =
true;
642 uint64_t localChosenActionIndex =
643 interactiveUnderApproximationExplorer->getSchedulerForExploredMdp()->getChoice(i).getDeterministicChoice();
644 auto rowIndex = scheduledModel->getTransitionMatrix().getRowGroupIndices()[i];
645 if (scheduledModel->getChoiceLabeling().getLabelsOfChoice(rowIndex + localChosenActionIndex).size() > 0) {
646 auto label = *(scheduledModel->getChoiceLabeling().getLabelsOfChoice(rowIndex + localChosenActionIndex).begin());
647 if (label.rfind(
"clip", 0) == 0) {
649 auto chosenRow = transMatrix.getRow(i, 0);
650 auto candidateIndex = (chosenRow.end() - 1)->getColumn();
651 transMatrix.makeRowDirac(transMatrix.getRowGroupIndices()[i], candidateIndex);
652 }
else if (label.rfind(
"mem_node", 0) == 0) {
653 if (!newLabeling.
containsLabel(
"finite_mem_" + label.substr(9, 1))) {
654 newLabeling.
addLabel(
"finite_mem_" + label.substr(9, 1));
667 transMatrix.dropZeroEntries();
669 if (scheduledModel->hasChoiceLabeling()) {
670 modelComponents.
choiceLabeling = scheduledModel->getChoiceLabeling();
673 auto inducedMC = newMDP.
applyScheduler(*(interactiveUnderApproximationExplorer->getSchedulerForExploredMdp()),
true);
674 scheduledModel = std::static_pointer_cast<storm::models::sparse::Model<ValueType>>(inducedMC);
676 interactiveResult.schedulerAsMarkovChain = scheduledModel;
678 interactiveResult.cutoffSchedulers = interactiveUnderApproximationExplorer->getUpperValueBoundSchedulers();
680 interactiveResult.cutoffSchedulers = interactiveUnderApproximationExplorer->getLowerValueBoundSchedulers();
682 if (firstIteration) {
683 firstIteration =
false;
693 ++statistics.refinementSteps.value();
695 if (statistics.refinementSteps.value() <= 1000) {
696 STORM_LOG_INFO(
"Completed iteration #" << statistics.refinementSteps.value() <<
". Current checktime is " << statistics.totalTime <<
".");
697 bool computingLowerBound =
false;
698 bool computingUpperBound =
false;
699 if (options.unfold) {
700 STORM_LOG_INFO(
"\tUnder-approx MDP has size " << interactiveUnderApproximationExplorer->getExploredMdp()->getNumberOfStates() <<
".");
701 (min ? computingUpperBound : computingLowerBound) =
true;
703 if (computingLowerBound && computingUpperBound) {
704 STORM_LOG_INFO(
"\tCurrent result is [" << interactiveResult.lowerBound <<
", " << interactiveResult.upperBound <<
"].");
705 }
else if (computingLowerBound) {
706 STORM_LOG_INFO(
"\tCurrent result is ≥" << interactiveResult.lowerBound <<
".");
707 }
else if (computingUpperBound) {
708 STORM_LOG_INFO(
"\tCurrent result is ≤" << interactiveResult.upperBound <<
".");
712 if (underApproxFixPoint) {
713 STORM_LOG_INFO(
"Fixpoint reached after " << statistics.refinementSteps.value() <<
" iterations.\n");
714 statistics.refinementFixpointDetected =
true;
716 unfoldingControl = UnfoldingControl::Pause;
718 if (!hasTruncatedStates) {
719 STORM_LOG_INFO(
"No states have been truncated, so continued iteration does not yield new results.\n");
721 unfoldingControl = UnfoldingControl::Pause;
725 while (unfoldingControl ==
726 storm::pomdp::modelchecker::BeliefExplorationPomdpModelChecker<PomdpModelType, BeliefValueType, BeliefMDPType>::UnfoldingControl::Pause &&
734template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
736 std::set<uint32_t>
const& targetObservations,
bool min, std::optional<std::string> rewardModelName,
742template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
745 return interactiveResult;
748template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
769template<
typename ValueType>
770ValueType
getGap(ValueType
const& l, ValueType
const& u) {
772 "Gap computation currently does not handle negative values.");
789template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
790bool BeliefExplorationPomdpModelChecker<PomdpModelType, BeliefValueType, BeliefMDPType>::buildOverApproximation(
791 storm::Environment const& env, std::set<uint32_t>
const& targetObservations,
bool min,
bool computeRewards,
bool refine,
792 HeuristicParameters
const& heuristicParameters, std::vector<BeliefValueType>& observationResolutionVector,
793 std::shared_ptr<BeliefManagerType>& beliefManager, std::shared_ptr<ExplorerType>& overApproximation) {
795 bool fixPoint =
true;
797 statistics.overApproximationBuildTime.start();
801 if (computeRewards) {
808 overApproximation->computeOptimalChoicesAndReachableMdpStates(heuristicParameters.optimalChoiceValueEpsilon,
true);
811 auto obsRatings = getObservationRatings(overApproximation, observationResolutionVector);
814 if (std::any_of(obsRatings.begin(), obsRatings.end(),
815 [&numericPrecision](BeliefValueType
const& value) { return value + numericPrecision < storm::utility::one<BeliefValueType>(); })) {
816 STORM_LOG_INFO_COND(!fixPoint,
"Not reaching a refinement fixpoint because there are still observations to refine.");
820 return r <= storm::utility::convertNumber<BeliefValueType>(heuristicParameters.observationThreshold);
823 for (uint64_t obs : refinedObservations) {
836 overApproximation->restartExploration();
838 statistics.overApproximationMaxResolution =
storm::utility::ceil(*std::max_element(observationResolutionVector.begin(), observationResolutionVector.end()));
841 storm::utility::Stopwatch explorationTime;
842 if (options.explorationTimeLimit != 0) {
843 explorationTime.
start();
845 bool timeLimitExceeded =
false;
846 std::map<uint32_t, typename ExplorerType::SuccessorObservationInformation> gatheredSuccessorObservations;
847 uint64_t numRewiredOrExploredStates = 0;
848 while (overApproximation->hasUnexploredState()) {
849 if (!timeLimitExceeded && options.explorationTimeLimit != 0 &&
850 static_cast<uint64_t
>(explorationTime.
getTimeInSeconds()) > options.explorationTimeLimit) {
852 timeLimitExceeded =
true;
853 STORM_LOG_INFO_COND(!fixPoint,
"Not reaching a refinement fixpoint because the exploration time limit is exceeded.");
857 uint64_t currId = overApproximation->exploreNextState();
858 bool hasOldBehavior = refine && overApproximation->currentStateHasOldBehavior();
859 if (!hasOldBehavior) {
860 STORM_LOG_INFO_COND(!fixPoint,
"Not reaching a refinement fixpoint because a new state is explored");
863 uint32_t currObservation = beliefManager->getBeliefObservation(currId);
864 if (targetObservations.count(currObservation) != 0) {
865 overApproximation->setCurrentStateIsTarget();
866 overApproximation->addSelfloopTransition();
882 bool exploreAllActions =
false;
883 bool truncateAllActions =
false;
884 bool restoreAllActions =
false;
885 bool checkRewireForAllActions =
false;
887 ValueType gap =
getGap(overApproximation->getLowerValueBoundAtCurrentState(), overApproximation->getUpperValueBoundAtCurrentState());
888 if (!hasOldBehavior) {
892 if (!timeLimitExceeded && gap >= heuristicParameters.gapThreshold && numRewiredOrExploredStates < heuristicParameters.sizeThreshold) {
893 exploreAllActions =
true;
895 truncateAllActions =
true;
896 overApproximation->setCurrentStateIsTruncated();
898 }
else if (overApproximation->getCurrentStateWasTruncated()) {
900 if (!timeLimitExceeded && overApproximation->currentStateIsOptimalSchedulerReachable() && gap > heuristicParameters.gapThreshold &&
901 numRewiredOrExploredStates < heuristicParameters.sizeThreshold) {
902 exploreAllActions =
true;
903 STORM_LOG_INFO_COND(!fixPoint,
"Not reaching a refinement fixpoint because a previously truncated state is now explored.");
906 truncateAllActions =
true;
907 overApproximation->setCurrentStateIsTruncated();
911 STORM_LOG_INFO_COND(!fixPoint,
"Not reaching a refinement fixpoint because we truncate a state with non-zero gap "
912 << gap <<
" that is reachable via an optimal sched.");
924 if (!timeLimitExceeded && overApproximation->currentStateIsOptimalSchedulerReachable() && gap > heuristicParameters.gapThreshold &&
925 numRewiredOrExploredStates < heuristicParameters.sizeThreshold) {
926 checkRewireForAllActions =
true;
928 restoreAllActions =
true;
930 checkRewireForAllActions =
true;
933 bool expandedAtLeastOneAction =
false;
934 for (uint64_t action = 0, numActions = beliefManager->getBeliefNumberOfChoices(currId); action < numActions; ++action) {
935 bool expandCurrentAction = exploreAllActions || truncateAllActions;
936 if (checkRewireForAllActions) {
943 STORM_LOG_ASSERT(overApproximation->currentStateHasOldBehavior(),
"Expected old behavior.");
944 if (overApproximation->getCurrentStateActionExplorationWasDelayed(action) ||
945 overApproximation->currentStateHasSuccessorObservationInObservationSet(action, refinedObservations)) {
947 if (!restoreAllActions && overApproximation->actionAtCurrentStateWasOptimal(action)) {
949 expandCurrentAction =
true;
950 STORM_LOG_INFO_COND(!fixPoint,
"Not reaching a refinement fixpoint because we rewire a state.");
954 overApproximation->setCurrentChoiceIsDelayed(action);
957 if (!overApproximation->getCurrentStateActionExplorationWasDelayed(action) ||
958 (overApproximation->currentStateIsOptimalSchedulerReachable() &&
961 "Not reaching a refinement fixpoint because we delay a rewiring of a state with non-zero gap "
962 << gap <<
" that is reachable via an optimal scheduler.");
970 if (expandCurrentAction) {
971 expandedAtLeastOneAction =
true;
972 if (!truncateAllActions) {
974 auto successorGridPoints = beliefManager->expandAndTriangulate(env, currId, action, observationResolutionVector);
975 for (
auto const& successor : successorGridPoints) {
976 overApproximation->addTransitionToBelief(action, successor.first, successor.second,
false);
978 if (computeRewards) {
979 overApproximation->computeRewardAtCurrentState(action);
985 auto successorGridPoints = beliefManager->expandAndTriangulate(env, currId, action, observationResolutionVector);
986 for (
auto const& successor : successorGridPoints) {
987 bool added = overApproximation->addTransitionToBelief(action, successor.first, successor.second,
true);
990 truncationProbability += successor.second;
991 truncationValueBound += successor.second * (
min ? overApproximation->computeLowerValueBoundAtBelief(successor.first)
992 : overApproximation->computeUpperValueBoundAtBelief(successor.first));
995 if (computeRewards) {
997 overApproximation->addTransitionsToExtraStates(action, truncationProbability);
998 overApproximation->computeRewardAtCurrentState(action, truncationValueBound);
1000 overApproximation->addTransitionsToExtraStates(action, truncationValueBound, truncationProbability - truncationValueBound);
1005 overApproximation->restoreOldBehaviorAtCurrentState(action);
1008 if (expandedAtLeastOneAction) {
1009 ++numRewiredOrExploredStates;
1013 for (uint64_t action = 0, numActions = beliefManager->getBeliefNumberOfChoices(currId); action < numActions; ++action) {
1014 if (pomdp().hasChoiceLabeling()) {
1015 auto rowIndex = pomdp().getTransitionMatrix().getRowGroupIndices()[beliefManager->getRepresentativeState(currId)];
1016 if (pomdp().getChoiceLabeling().getLabelsOfChoice(rowIndex + action).size() > 0) {
1017 overApproximation->addChoiceLabelToCurrentState(action, *(pomdp().getChoiceLabeling().getLabelsOfChoice(rowIndex + action).begin()));
1029 if (!statistics.overApproximationStates) {
1030 statistics.overApproximationBuildAborted =
true;
1031 statistics.overApproximationStates = overApproximation->getCurrentNumberOfMdpStates();
1033 statistics.overApproximationBuildTime.stop();
1037 overApproximation->finishExploration();
1038 statistics.overApproximationBuildTime.stop();
1040 statistics.overApproximationCheckTime.start();
1041 overApproximation->computeValuesOfExploredMdp(env, min ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize);
1042 statistics.overApproximationCheckTime.stop();
1046 statistics.overApproximationStates = overApproximation->getExploredMdp()->getNumberOfStates();
1051template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
1052bool BeliefExplorationPomdpModelChecker<PomdpModelType, BeliefValueType, BeliefMDPType>::buildUnderApproximation(
1053 storm::Environment
const& env, std::set<uint32_t>
const& targetObservations,
bool min,
bool computeRewards,
bool refine,
1054 HeuristicParameters
const& heuristicParameters, std::shared_ptr<BeliefManagerType>& beliefManager, std::shared_ptr<ExplorerType>& underApproximation,
1055 bool firstIteration) {
1056 statistics.underApproximationBuildTime.start();
1058 unfoldingStatus = Status::Exploring;
1059 if (options.useClipping) {
1061 statistics.nrClippingAttempts = 0;
1062 statistics.nrClippedStates = 0;
1065 uint64_t nrCutoffStrategies =
min ? underApproximation->getNrSchedulersForUpperBounds() : underApproximation->getNrSchedulersForLowerBounds();
1067 bool fixPoint =
true;
1068 if (heuristicParameters.sizeThreshold != std::numeric_limits<uint64_t>::max()) {
1069 statistics.underApproximationStateLimit = heuristicParameters.sizeThreshold;
1072 if (options.interactiveUnfolding && !firstIteration) {
1073 underApproximation->restoreExplorationState();
1074 }
else if (computeRewards) {
1082 underApproximation->restartExploration();
1086 storm::utility::Stopwatch explorationTime;
1087 storm::utility::Stopwatch printUpdateStopwatch;
1088 printUpdateStopwatch.
start();
1089 if (options.explorationTimeLimit != 0) {
1090 explorationTime.
start();
1092 bool timeLimitExceeded =
false;
1093 bool stateStored =
false;
1094 while (underApproximation->hasUnexploredState()) {
1095 if (!timeLimitExceeded && options.explorationTimeLimit != 0 &&
1096 static_cast<uint64_t
>(explorationTime.
getTimeInSeconds()) > options.explorationTimeLimit) {
1098 timeLimitExceeded =
true;
1101 printUpdateStopwatch.
restart();
1102 STORM_LOG_INFO(
"### " << underApproximation->getCurrentNumberOfMdpStates() <<
" beliefs in underapproximation MDP"
1103 <<
" ##### " << underApproximation->getUnexploredStates().size() <<
" beliefs queued\n");
1104 if (underApproximation->getCurrentNumberOfMdpStates() > heuristicParameters.sizeThreshold && options.useClipping) {
1105 STORM_LOG_INFO(
"##### Clipping Attempts: " << statistics.nrClippingAttempts.value() <<
" ##### "
1106 <<
"Clipped States: " << statistics.nrClippedStates.value() <<
"\n");
1109 if (unfoldingControl == UnfoldingControl::Pause && !stateStored) {
1110 underApproximation->storeExplorationState();
1113 uint64_t currId = underApproximation->exploreNextState();
1114 uint32_t currObservation = beliefManager->getBeliefObservation(currId);
1115 uint64_t addedActions = 0;
1116 bool stateAlreadyExplored = refine && underApproximation->currentStateHasOldBehavior() && !underApproximation->getCurrentStateWasTruncated();
1117 if (!stateAlreadyExplored || timeLimitExceeded) {
1120 if (targetObservations.count(beliefManager->getBeliefObservation(currId)) != 0) {
1121 underApproximation->setCurrentStateIsTarget();
1122 underApproximation->addSelfloopTransition();
1123 underApproximation->addChoiceLabelToCurrentState(0,
"loop");
1125 bool stopExploration =
false;
1126 bool clipBelief =
false;
1127 if (timeLimitExceeded || (options.interactiveUnfolding && unfoldingControl != UnfoldingControl::Run)) {
1128 clipBelief = options.useClipping;
1129 stopExploration = !underApproximation->isMarkedAsGridBelief(currId);
1130 }
else if (!stateAlreadyExplored) {
1132 ValueType gap =
getGap(underApproximation->getLowerValueBoundAtCurrentState(), underApproximation->getUpperValueBoundAtCurrentState());
1133 if ((gap < heuristicParameters.gapThreshold) || (gap == 0 && options.cutZeroGap)) {
1134 stopExploration =
true;
1135 }
else if (underApproximation->getCurrentNumberOfMdpStates() >=
1136 heuristicParameters.sizeThreshold ) {
1137 clipBelief = options.useClipping;
1138 stopExploration = !underApproximation->isMarkedAsGridBelief(currId);
1142 if (clipBelief && !underApproximation->isMarkedAsGridBelief(currId)) {
1144 if (!options.useStateEliminationCutoff) {
1145 bool successfulClip = clipToGridExplicitly(env, currId, computeRewards, beliefManager, underApproximation, 0);
1147 stopExploration = !underApproximation->isMarkedAsGridBelief(currId);
1148 if (successfulClip) {
1152 clipToGrid(env, currId, computeRewards, min, beliefManager, underApproximation);
1153 addedActions += beliefManager->getBeliefNumberOfChoices(currId);
1157 if (stopExploration) {
1158 underApproximation->setCurrentStateIsTruncated();
1160 if (options.useStateEliminationCutoff || !stopExploration) {
1162 uint64_t numActions = beliefManager->getBeliefNumberOfChoices(currId);
1163 if (underApproximation->needsActionAdjustment(numActions)) {
1164 underApproximation->adjustActions(numActions);
1166 for (uint64_t action = 0; action < numActions; ++action) {
1168 if (pomdp().hasChoiceLabeling()) {
1169 auto rowIndex = pomdp().getTransitionMatrix().getRowGroupIndices()[beliefManager->getRepresentativeState(currId)];
1170 if (pomdp().getChoiceLabeling().getLabelsOfChoice(rowIndex + action).size() > 0) {
1171 underApproximation->addChoiceLabelToCurrentState(addedActions + action,
1172 *(pomdp().getChoiceLabeling().getLabelsOfChoice(rowIndex + action).begin()));
1175 if (stateAlreadyExplored) {
1176 underApproximation->restoreOldBehaviorAtCurrentState(action);
1180 auto successors = beliefManager->expand(env, currId, action);
1181 for (
auto const& successor : successors) {
1182 bool added = underApproximation->addTransitionToBelief(addedActions + action, successor.first, successor.second, stopExploration);
1184 STORM_LOG_ASSERT(stopExploration,
"Didn't add a transition although exploration shouldn't be stopped.");
1186 truncationProbability += successor.second;
1191 truncationValueBound += successor.second * (
min ? underApproximation->computeUpperValueBoundAtBelief(successor.first)
1192 : underApproximation->computeLowerValueBoundAtBelief(successor.first));
1195 if (stopExploration) {
1196 if (computeRewards) {
1199 truncationProbability);
1201 underApproximation->addTransitionsToExtraStates(addedActions + action, truncationProbability);
1204 underApproximation->addTransitionsToExtraStates(addedActions + action, truncationValueBound,
1205 truncationProbability - truncationValueBound);
1208 if (computeRewards) {
1212 underApproximation->computeRewardAtCurrentState(action, truncationValueBound);
1214 underApproximation->addRewardToCurrentState(addedActions + action,
1215 beliefManager->getBeliefActionReward(currId, action) + truncationValueBound);
1222 for (uint64_t i = 0;
i < nrCutoffStrategies && !options.skipHeuristicSchedulers; ++
i) {
1223 auto cutOffValue =
min ? underApproximation->computeUpperValueBoundForScheduler(currId, i)
1224 : underApproximation->computeLowerValueBoundForScheduler(currId, i);
1225 if (computeRewards) {
1228 underApproximation->addRewardToCurrentState(addedActions, cutOffValue);
1235 if (pomdp().hasChoiceLabeling()) {
1236 underApproximation->addChoiceLabelToCurrentState(addedActions,
"sched_" + std::to_string(i));
1240 if (underApproximation->hasFMSchedulerValues()) {
1241 uint64_t transitionNr = 0;
1242 for (uint64_t i = 0;
i < underApproximation->getNrOfMemoryNodesForObservation(currObservation); ++
i) {
1243 auto resPair = underApproximation->computeFMSchedulerValueForMemoryNode(currId, i);
1245 if (resPair.first) {
1246 cutOffValue = resPair.second;
1248 STORM_LOG_DEBUG(
"Skipped cut-off of belief with ID " << currId <<
" with finite memory scheduler in memory node " << i
1249 <<
". Missing values.");
1252 if (computeRewards) {
1255 underApproximation->addRewardToCurrentState(addedActions + transitionNr, cutOffValue);
1261 underApproximation->addTransitionsToExtraStates(addedActions + transitionNr, cutOffValue,
1264 if (pomdp().hasChoiceLabeling()) {
1265 underApproximation->addChoiceLabelToCurrentState(addedActions + transitionNr,
"mem_node_" + std::to_string(i));
1279 if (!statistics.underApproximationStates) {
1280 statistics.underApproximationBuildAborted =
true;
1281 statistics.underApproximationStates = underApproximation->getCurrentNumberOfMdpStates();
1283 statistics.underApproximationBuildTime.stop();
1287 underApproximation->finishExploration();
1288 statistics.underApproximationBuildTime.stop();
1289 printUpdateStopwatch.
stop();
1290 STORM_LOG_INFO(
"Finished exploring under-approximation MDP.\nStart analysis...\n");
1291 unfoldingStatus = Status::ModelExplorationFinished;
1292 statistics.underApproximationCheckTime.start();
1293 underApproximation->computeValuesOfExploredMdp(env, min ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize);
1294 statistics.underApproximationCheckTime.stop();
1295 if (underApproximation->getExploredMdp()->getStateLabeling().getStates(
"truncated").getNumberOfSetBits() > 0) {
1296 statistics.nrTruncatedStates = underApproximation->getExploredMdp()->getStateLabeling().getStates(
"truncated").getNumberOfSetBits();
1300 statistics.underApproximationStates = underApproximation->getExploredMdp()->getNumberOfStates();
1305template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
1306void BeliefExplorationPomdpModelChecker<PomdpModelType, BeliefValueType, BeliefMDPType>::clipToGrid(storm::Environment
const& env, uint64_t clippingStateId,
1307 bool computeRewards,
bool min,
1308 std::shared_ptr<BeliefManagerType>& beliefManager,
1309 std::shared_ptr<ExplorerType>& beliefExplorer) {
1312 for (uint64_t action = 0, numActions = beliefManager->getBeliefNumberOfChoices(clippingStateId); action < numActions; ++action) {
1314 auto successors = beliefManager->expand(env, clippingStateId, action);
1316 for (
auto const& successor : successors) {
1320 bool added = beliefExplorer->addTransitionToBelief(action, successor.first, successor.second,
true);
1323 statistics.nrClippingAttempts = statistics.nrClippingAttempts.value() + 1;
1325 beliefManager->clipBeliefToGrid(env, successor.first, options.clippingGridRes,
1326 computeRewards ? beliefExplorer->getStateExtremeBoundIsInfinite() : storm::storage::BitVector());
1327 if (clipping.isClippable) {
1329 statistics.nrClippedStates = statistics.nrClippedStates.value() + 1;
1331 BeliefValueType transitionProb =
1336 if (computeRewards) {
1339 for (
auto const& deltaValue : clipping.deltaValues) {
1340 localRew += deltaValue.second *
1348 }
else if (clipping.onGrid) {
1350 beliefExplorer->addTransitionToBelief(action, successor.first, successor.second,
false);
1356 : beliefExplorer->computeLowerValueBoundAtBelief(successor.first));
1362 if (computeRewards) {
1368 BeliefValueType totalRewardVal = rewardBound / absDelta;
1375 if (computeRewards) {
1376 beliefExplorer->computeRewardAtCurrentState(action);
1381template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
1382bool BeliefExplorationPomdpModelChecker<PomdpModelType, BeliefValueType, BeliefMDPType>::clipToGridExplicitly(storm::Environment
const& env,
1383 uint64_t clippingStateId,
bool computeRewards,
1384 std::shared_ptr<BeliefManagerType>& beliefManager,
1385 std::shared_ptr<ExplorerType>& beliefExplorer,
1386 uint64_t localActionIndex) {
1387 statistics.nrClippingAttempts = statistics.nrClippingAttempts.value() + 1;
1388 auto clipping = beliefManager->clipBeliefToGrid(env, clippingStateId, options.clippingGridRes,
1389 computeRewards ? beliefExplorer->getStateExtremeBoundIsInfinite() : storm::storage::BitVector());
1390 if (clipping.isClippable) {
1392 statistics.nrClippedStates = statistics.nrClippedStates.value() + 1;
1396 beliefExplorer->markAsGridBelief(clipping.targetBelief);
1397 if (computeRewards) {
1400 for (
auto const& deltaValue : clipping.deltaValues) {
1410 BeliefValueType totalRewardVal = reward / clipping.delta;
1417 beliefExplorer->addChoiceLabelToCurrentState(localActionIndex,
"clip");
1420 if (clipping.onGrid) {
1422 beliefExplorer->markAsGridBelief(clippingStateId);
1428template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
1429void BeliefExplorationPomdpModelChecker<PomdpModelType, BeliefValueType, BeliefMDPType>::setUnfoldingControl(
1430 storm::pomdp::modelchecker::BeliefExplorationPomdpModelChecker<PomdpModelType, BeliefValueType, BeliefMDPType>::UnfoldingControl newUnfoldingControl) {
1431 unfoldingControl = newUnfoldingControl;
1434template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
1437 setUnfoldingControl(UnfoldingControl::Pause);
1440template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
1443 setUnfoldingControl(UnfoldingControl::Run);
1446template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
1449 setUnfoldingControl(UnfoldingControl::Terminate);
1452template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
1457template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
1462template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
1467template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
1468std::shared_ptr<storm::builder::BeliefMdpExplorer<PomdpModelType, BeliefValueType>>
1470 return interactiveUnderApproximationExplorer;
1473template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
1475 std::vector<std::vector<std::unordered_map<uint64_t, ValueType>>> valueList) {
1476 interactiveUnderApproximationExplorer->setFMSchedValueList(valueList);
1479template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
1480BeliefValueType BeliefExplorationPomdpModelChecker<PomdpModelType, BeliefValueType, BeliefMDPType>::rateObservation(
1481 typename ExplorerType::SuccessorObservationInformation
const& info, BeliefValueType
const& observationResolution, BeliefValueType
const& maxResolution) {
1495 obsChoiceRating = (obsChoiceRating * n - one) / (n - one);
1497 obsChoiceRating *= observationResolution / maxResolution;
1498 return obsChoiceRating;
1502template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
1503std::vector<BeliefValueType> BeliefExplorationPomdpModelChecker<PomdpModelType, BeliefValueType, BeliefMDPType>::getObservationRatings(
1504 std::shared_ptr<ExplorerType>
const& overApproximation, std::vector<BeliefValueType>
const& observationResolutionVector) {
1505 uint64_t numMdpStates = overApproximation->getExploredMdp()->getNumberOfStates();
1506 auto const& choiceIndices = overApproximation->getExploredMdp()->getNondeterministicChoiceIndices();
1507 BeliefValueType maxResolution = *std::max_element(observationResolutionVector.begin(), observationResolutionVector.end());
1511 std::map<uint32_t, typename ExplorerType::SuccessorObservationInformation> gatheredSuccessorObservations;
1512 for (uint64_t mdpState = 0; mdpState < numMdpStates; ++mdpState) {
1515 if (overApproximation->stateIsOptimalSchedulerReachable(mdpState)) {
1516 for (uint64_t mdpChoice = choiceIndices[mdpState]; mdpChoice < choiceIndices[mdpState + 1]; ++mdpChoice) {
1518 if (overApproximation->actionIsOptimal(mdpChoice)) {
1520 gatheredSuccessorObservations.clear();
1521 overApproximation->gatherSuccessorObservationInformationAtMdpChoice(mdpChoice, gatheredSuccessorObservations);
1522 for (
auto const& obsInfo : gatheredSuccessorObservations) {
1523 auto const& obs = obsInfo.first;
1524 BeliefValueType obsChoiceRating = rateObservation(obsInfo.second, observationResolutionVector[obs], maxResolution);
1527 resultingRatings[obs] = std::min(resultingRatings[obs], obsChoiceRating);
1533 return resultingRatings;
1536template<
typename PomdpModelType,
typename BeliefValueType,
typename BeliefMDPType>
1537typename PomdpModelType::ValueType BeliefExplorationPomdpModelChecker<PomdpModelType, BeliefValueType, BeliefMDPType>::getGap(
1538 typename PomdpModelType::ValueType
const& l,
typename PomdpModelType::ValueType
const& u) {
1540 "Gap computation currently does not handle negative values.");
void addLabel(std::string const &label)
Adds a new label to the labelings.
bool containsLabel(std::string const &label) const
Checks whether a label is registered within this labeling.
void removeLabel(std::string const &label)
Removes a label from the labelings.
This class represents a (discrete-time) Markov decision process.
std::shared_ptr< storm::models::sparse::Model< ValueType, RewardModelType > > applyScheduler(storm::storage::Scheduler< ValueType > const &scheduler, bool dropUnreachableStates=true, bool preserveModelType=false) const
Applies the given scheduler to this model.
This class manages the labeling of the state space with a number of (atomic) labels.
bool getStateHasLabel(std::string const &label, storm::storage::sparse::state_type state) const
Checks whether a given state is labeled with the given label.
void addLabelToState(std::string const &label, storm::storage::sparse::state_type state)
Adds a label to a given state.
Model checker for checking reachability queries on POMDPs using approximations based on exploration o...
BeliefExplorationPomdpModelChecker(std::shared_ptr< PomdpModelType > pomdp, Options options=Options())
Constructor.
int64_t getStatus()
Get the current status of the interactive unfolding.
bool hasConverged()
Indicates whether the interactive unfolding has coonverged, i.e.
bool isExploring()
Indicates whether the interactive unfolding is currently in the process of exploring the belief MDP.
std::shared_ptr< ExplorerType > getInteractiveBeliefExplorer()
Get a pointer to the belief explorer used in the interactive unfolding.
@ ModelExplorationFinished
BeliefExplorationPomdpModelCheckerOptions< ValueType > Options
void setFMSchedValueList(std::vector< std::vector< std::unordered_map< uint64_t, ValueType > > > valueList)
void unfoldInteractively(storm::Environment const &env, std::set< uint32_t > const &targetObservations, bool min, std::optional< std::string > rewardModelName, storm::pomdp::modelchecker::POMDPValueBounds< ValueType > const &valueBounds, Result &result)
Allows to generate an under-approximation using a controllable unfolding.
bool isResultReady()
Indicates whether there is a result after an interactive unfolding was paused.
Result check(storm::Environment const &env, storm::logic::Formula const &formula, storm::Environment const &preProcEnv, std::vector< std::vector< std::unordered_map< uint64_t, ValueType > > > const &additionalUnderApproximationBounds=std::vector< std::vector< std::unordered_map< uint64_t, ValueType > > >())
Performs model checking of the given POMDP with regards to a formula using the previously specified o...
Result getInteractiveResult()
Get the latest saved result obtained by the interactive unfolding.
void printStatisticsToStream(std::ostream &stream) const
Prints statistics of the process to a given output stream.
void pauseUnfolding()
Pauses a running interactive unfolding.
void precomputeValueBounds(const logic::Formula &formula, storm::Environment const &preProcEnv)
Uses model checking on the underlying MDP to generate values used for cut-offs and for clipping compe...
void continueUnfolding()
Continues a previously paused interactive unfolding.
void terminateUnfolding()
Terminates a running interactive unfolding.
A bit vector that is internally represented as a vector of 64-bit values.
uint64_t getNumberOfSetBits() const
Returns the number of bits that are set to true in this bit vector.
size_t size() const
Retrieves the number of bits this bit vector can store.
void start()
Start stopwatch (again) and start measuring time.
void restart()
Reset the stopwatch and immediately start it.
SecondType getTimeInSeconds() const
Gets the measured time in seconds.
void stop()
Stop stopwatch and add measured time to total time.
#define STORM_LOG_INFO(message)
#define STORM_LOG_WARN(message)
#define STORM_LOG_DEBUG(message)
#define STORM_LOG_TRACE(message)
#define STORM_LOG_ASSERT(cond, message)
#define STORM_LOG_WARN_COND(cond, message)
#define STORM_LOG_ERROR_COND(cond, message)
#define STORM_LOG_THROW(cond, exception, message)
#define STORM_LOG_INFO_COND(cond, message)
SFTBDDChecker::ValueType ValueType
FormulaInformation getFormulaInformation(PomdpType const &pomdp, storm::logic::ProbabilityOperatorFormula const &formula)
ValueType getGap(ValueType const &l, ValueType const &u)
bool detectFiniteBeliefMdp(storm::models::sparse::Pomdp< ValueType > const &pomdp, std::optional< storm::storage::BitVector > const &targetStates)
This method tries to detect that the beliefmdp is finite.
storm::storage::BitVector getReachableStates(storm::storage::SparseMatrix< T > const &transitionMatrix, storm::storage::BitVector const &initialStates, storm::storage::BitVector const &constraintStates, storm::storage::BitVector const &targetStates, bool useStepBound, uint_fast64_t maximalSteps, boost::optional< storm::storage::BitVector > const &choiceFilter)
Performs a forward depth-first search through the underlying graph structure to identify the states t...
bool isTerminate()
Check whether the program should terminate (due to some abort signal).
storm::storage::BitVector filter(std::vector< T > const &values, std::function< bool(T const &value)> const &function)
Retrieves a bit vector containing all the indices for which the value at this position makes the give...
bool isOne(ValueType const &a)
ValueType min(ValueType const &first, ValueType const &second)
bool isZero(ValueType const &a)
ValueType ceil(ValueType const &number)
ValueType abs(ValueType const &number)
bool isInfinity(ValueType const &a)
TargetType convertNumber(SourceType const &number)
static const bool IsExact
Struct used to store the results of the model checker.
Result(ValueType lower, ValueType upper)
ValueType diff(bool relative=false) const
bool updateUpperBound(ValueType const &value)
bool updateLowerBound(ValueType const &value)
Structure for storing values on the POMDP used for cut-offs and clipping.
std::vector< std::vector< std::unordered_map< uint64_t, ValueType > > > fmSchedulerValueList
storm::pomdp::storage::ExtremePOMDPValueBound< ValueType > extremePomdpValueBound
storm::pomdp::storage::PreprocessingPomdpValueBounds< ValueType > trivialPomdpValueBounds
std::unordered_map< std::string, RewardModelType > rewardModels
storm::storage::SparseMatrix< ValueType > transitionMatrix
std::optional< storm::storage::sparse::Valuations > observationValuations
std::optional< storm::models::sparse::ChoiceLabeling > choiceLabeling
storm::models::sparse::StateLabeling stateLabeling
std::optional< std::vector< uint32_t > > observabilityClasses