42 if (lhBelief.size() != rhBelief.size()) {
46 auto lhIt = lhBelief.begin();
47 auto rhIt = rhBelief.begin();
48 while (lhIt != lhBelief.end() || rhIt != rhBelief.end()) {
50 if (lhIt->first != rhIt->first || std::fabs(lhIt->second - rhIt->second) > 1e-15) {
56 return lhIt == lhBelief.end() && rhIt == rhBelief.end();
59template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
60std::size_t BeliefManager<PomdpType, BeliefValueType, StateType>::BeliefHash::operator()(
const BeliefType &belief)
const {
63 for (
auto const &entry : belief) {
64 boost::hash_combine(seed, entry.first);
65 boost::hash_combine(seed, entry.second);
74 for (
auto const &entry : belief) {
75 boost::hash_combine(seed, entry.first);
81template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
84 : pomdp(pomdp), cc(precision, false), triangulationMode(triangulationMode) {
85 beliefToIdMap.resize(pomdp.getNrObservations());
86 initialBeliefId = computeInitialBelief();
89template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
91 if (rewardModelName) {
92 auto const &rewardModel = pomdp.getRewardModel(rewardModelName.value());
93 pomdpActionRewardVector = rewardModel.getTotalRewardVector(pomdp.getTransitionMatrix());
99template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
101 pomdpActionRewardVector.clear();
104template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
106 return std::numeric_limits<BeliefId>::max();
109template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
111 return isEqual(getBelief(first), getBelief(second));
114template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
116 return toString(getBelief(beliefId));
119template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
121 std::stringstream str;
123 for (uint64_t
i = 0;
i < t.
size(); ++
i) {
130template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
132 BeliefId const &beliefId, std::vector<ValueType>
const &summands) {
134 for (
auto const &entry : getBelief(beliefId)) {
140template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
142 BeliefId const &beliefId, std::unordered_map<StateType, ValueType>
const &summands) {
143 bool successful =
true;
145 for (
auto const &entry : getBelief(beliefId)) {
146 auto probIter = summands.find(entry.first);
147 if (probIter != summands.end()) {
154 return {successful, result};
157template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
159 return initialBeliefId;
162template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
164 BeliefId const &beliefId, uint64_t
const &localActionIndex)
const {
165 auto const &belief = getBelief(beliefId);
166 STORM_LOG_ASSERT(!pomdpActionRewardVector.empty(),
"Requested a reward although no reward model was specified.");
168 auto const &choiceIndices = pomdp.getTransitionMatrix().getRowGroupIndices();
169 for (
auto const &entry : belief) {
170 uint64_t choiceIndex = choiceIndices[entry.first] + localActionIndex;
171 STORM_LOG_ASSERT(choiceIndex < choiceIndices[entry.first + 1],
"Invalid local action index.");
172 STORM_LOG_ASSERT(choiceIndex < pomdpActionRewardVector.size(),
"Invalid choice index.");
178template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
183template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
185 auto const &belief = getBelief(beliefId);
186 return pomdp.getNumberOfChoices(belief.begin()->first);
189template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
191 BeliefId beliefId, BeliefValueType resolution) {
195template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
196template<
typename DistributionType>
198 auto insertionRes = distr.emplace(state, value);
199 if (!insertionRes.second) {
200 insertionRes.first->second += value;
204template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
205template<
typename DistributionType>
206void BeliefManager<PomdpType, BeliefValueType, StateType>::adjustDistribution(DistributionType &distr) {
213template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
215 auto const &belief = getBelief(beliefId);
216 for (
auto const &entry : belief) {
217 support.insert(entry.first);
221template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
223 return beliefs.size();
226template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
227std::vector<std::pair<typename BeliefManager<PomdpType, BeliefValueType, StateType>::BeliefId,
230 std::vector<BeliefValueType>
const &observationResolutions) {
231 return expandInternal(beliefId, actionIndex, observationResolutions);
234template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
235std::vector<std::pair<typename BeliefManager<PomdpType, BeliefValueType, StateType>::BeliefId,
238 std::vector<uint64_t>
const &observationResolutions) {
239 return expandInternal(beliefId, actionIndex, std::nullopt, observationResolutions);
242template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
243std::vector<std::pair<typename BeliefManager<PomdpType, BeliefValueType, StateType>::BeliefId,
246 return expandInternal(beliefId, actionIndex);
249template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
251 BeliefId
const &
id)
const {
253 STORM_LOG_ASSERT(
id < getNumberOfBeliefIds(),
"Belief index " <<
id <<
" is out of range.");
257template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
259 BeliefType
const &belief)
const {
260 uint32_t obs = getBeliefObservation(belief);
261 STORM_LOG_ASSERT(obs < beliefToIdMap.size(),
"Belief has unknown observation.");
262 auto idIt = beliefToIdMap[obs].find(belief);
267template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
269 std::stringstream str;
272 for (
auto const &entry : belief) {
278 str << entry.first <<
": " << std::setprecision(std::numeric_limits<double>::max_digits10 + 1) << entry.second;
284template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
286 if (first.size() != second.size()) {
289 auto secondIt = second.begin();
290 for (
auto const &firstEntry : first) {
291 if (firstEntry.first != secondIt->first) {
294 if (!cc.isEqual(firstEntry.second, secondIt->second)) {
302template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
303bool BeliefManager<PomdpType, BeliefValueType, StateType>::assertBelief(BeliefType
const &belief)
const {
305 std::optional<uint32_t> observation;
306 for (
auto const &entry : belief) {
307 if (entry.first >= pomdp.getNumberOfStates()) {
308 STORM_LOG_ERROR(
"Belief does refer to non-existing pomdp state " << entry.first <<
".");
311 uint64_t entryObservation = pomdp.getObservation(entry.first);
313 if (observation.value() != entryObservation) {
318 observation = entryObservation;
336 if (!cc.isOne(sum)) {
337 STORM_LOG_ERROR(
"Belief does not sum up to one. (" << sum <<
" instead).");
343template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
344bool BeliefManager<PomdpType, BeliefValueType, StateType>::assertTriangulation(BeliefType
const &belief, Triangulation
const &triangulation)
const {
345 if (triangulation.weights.size() != triangulation.gridPoints.size()) {
346 STORM_LOG_ERROR(
"Number of weights and points in triangulation does not match.");
349 if (triangulation.size() == 0) {
353 BeliefType triangulatedBelief;
355 for (uint64_t
i = 0;
i < triangulation.weights.size(); ++
i) {
356 if (cc.isZero(triangulation.weights[
i])) {
367 weightSum += triangulation.weights[
i];
368 BeliefType
const &gridPoint = getBelief(triangulation.gridPoints[
i]);
369 for (
auto const &pointEntry : gridPoint) {
371 triangulatedValue += triangulation.weights[
i] * pointEntry.second;
374 if (!cc.isOne(weightSum)) {
378 if (!assertBelief(triangulatedBelief)) {
381 if (!isEqual(belief, triangulatedBelief)) {
388template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
391 return pomdp.getObservation(belief.begin()->first);
394template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
395void BeliefManager<PomdpType, BeliefValueType, StateType>::triangulateBeliefFreudenthal(BeliefType
const &belief, BeliefValueType
const &resolution,
396 Triangulation &result) {
399 StateType numEntries = belief.size();
405 std::set<FreudenthalDiff, std::greater<>> sorted_diffs;
406 std::vector<BeliefValueType> qsRow;
407 qsRow.reserve(numEntries);
408 std::vector<StateType> toOriginalIndicesMap;
409 toOriginalIndicesMap.reserve(numEntries);
410 BeliefValueType x = resolution;
411 for (
auto const &entry : belief) {
413 sorted_diffs.emplace(toOriginalIndicesMap.size(), x - qsRow.back());
414 toOriginalIndicesMap.push_back(entry.first);
415 x -= entry.second * resolution;
420 result.weights.reserve(numEntries);
421 result.gridPoints.reserve(numEntries);
422 auto currentSortedDiff = sorted_diffs.begin();
423 auto previousSortedDiff = sorted_diffs.end();
424 --previousSortedDiff;
425 for (StateType
i = 0;
i < numEntries; ++
i) {
427 BeliefValueType weight = previousSortedDiff->diff - currentSortedDiff->diff;
435 if (!cc.isZero(weight)) {
436 result.weights.push_back(weight);
438 BeliefType gridPoint;
439 for (StateType j = 0; j < numEntries; ++j) {
440 BeliefValueType gridPointEntry = qsRow[j] - qsRow[j + 1];
441 if (!cc.isZero(gridPointEntry)) {
442 gridPoint[toOriginalIndicesMap[j]] = gridPointEntry / resolution;
445 result.gridPoints.push_back(getOrAddBeliefId(gridPoint));
447 previousSortedDiff = currentSortedDiff++;
451template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
452void BeliefManager<PomdpType, BeliefValueType, StateType>::triangulateBeliefDynamic(BeliefType
const &belief, BeliefValueType
const &resolution,
453 Triangulation &result) {
457 BeliefValueType finalResolution = resolution;
458 uint64_t finalResolutionMisses = belief.size() + 1;
460 for (BeliefValueType currResolution = resolution; currResolution > resolution / 2; --currResolution) {
461 uint64_t currResMisses = 0;
462 bool continueWithNextResolution =
false;
463 for (
auto const &belEntry : belief) {
464 BeliefValueType product = belEntry.second * currResolution;
467 if (currResMisses >= finalResolutionMisses) {
469 continueWithNextResolution =
true;
474 if (!continueWithNextResolution) {
475 STORM_LOG_ASSERT(currResMisses < finalResolutionMisses,
"Distance for this resolution should not be larger than a previously checked one.");
476 finalResolution = currResolution;
477 finalResolutionMisses = currResMisses;
478 if (currResMisses == 0) {
487 triangulateBeliefFreudenthal(belief, finalResolution, result);
490template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
492 BeliefType
const &belief, BeliefValueType
const &resolution) {
493 STORM_LOG_ASSERT(assertBelief(belief),
"Input belief for triangulation is not valid.");
494 Triangulation result;
496 if (belief.size() == 1u) {
498 result.gridPoints.push_back(getOrAddBeliefId(belief));
501 switch (triangulationMode) {
502 case TriangulationMode::Static:
503 triangulateBeliefFreudenthal(belief, ceiledResolution, result);
505 case TriangulationMode::Dynamic:
506 triangulateBeliefDynamic(belief, ceiledResolution, result);
516template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
517std::vector<std::pair<typename BeliefManager<PomdpType, BeliefValueType, StateType>::BeliefId,
519BeliefManager<PomdpType, BeliefValueType, StateType>::expandInternal(BeliefId
const &beliefId, uint64_t actionIndex,
520 std::optional<std::vector<BeliefValueType>>
const &observationTriangulationResolutions,
521 std::optional<std::vector<uint64_t>>
const &observationGridClippingResolutions) {
522 std::vector<std::pair<BeliefId, ValueType>> destinations;
524 BeliefType belief = getBelief(beliefId);
527 BeliefType successorObs;
528 for (
auto const &pointEntry : belief) {
529 uint64_t state = pointEntry.first;
530 for (
auto const &pomdpTransition : pomdp.getTransitionMatrix().getRow(state, actionIndex)) {
532 auto obs = pomdp.getObservation(pomdpTransition.getColumn());
537 adjustDistribution(successorObs);
540 for (
auto const &successor : successorObs) {
541 BeliefType successorBelief;
542 for (
auto const &pointEntry : belief) {
543 uint64_t state = pointEntry.first;
544 for (
auto const &pomdpTransition : pomdp.getTransitionMatrix().getRow(state, actionIndex)) {
545 if (pomdp.getObservation(pomdpTransition.getColumn()) == successor.first) {
547 addToDistribution(successorBelief, pomdpTransition.getColumn(), prob);
551 adjustDistribution(successorBelief);
552 STORM_LOG_ASSERT(assertBelief(successorBelief),
"Invalid successor belief.");
555 if (observationTriangulationResolutions) {
556 Triangulation triangulation = triangulateBelief(successorBelief, observationTriangulationResolutions.value()[successor.first]);
557 for (
size_t j = 0; j < triangulation.size(); ++j) {
559 BeliefValueType a = triangulation.weights[j] * successor.second;
562 }
else if (observationGridClippingResolutions) {
563 BeliefClipping clipping = clipBeliefToGrid(successorBelief, observationGridClippingResolutions.value()[successor.first],
564 storm::storage::BitVector(pomdp.getNumberOfStates()));
565 if (clipping.isClippable) {
580template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
584 res.startingBelief = beliefId;
588template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
591 uint32_t obs = getBeliefObservation(belief);
592 STORM_LOG_ASSERT(obs < beliefToIdMap.size(),
"Belief has unknown observation.");
604 std::vector<storm::expressions::Expression> decisionVariables;
609 std::vector<storm::expressions::Expression> deltas;
611 for (
auto const &state : belief) {
614 if (isInfinite[state.first]) {
617 deltas.push_back(deltaExpr);
621 BeliefValueType bound = state.second;
622 if (!isInfinite.
empty()) {
626 deltas.push_back(storm::expressions::Expression(localDelta));
631 std::vector<BeliefType> gridCandidates;
633 BeliefType candidate;
634 auto belIter = belief.begin();
635 for (uint64_t j = 0; j < belief.size() - 1; ++j) {
644 if (isEqual(candidate, belief)) {
648 gridCandidates.push_back(candidate);
651 auto decisionVar = lpSolver->addBinaryVariable(
"a_" + std::to_string(gridCandidates.size() - 1));
652 decisionVariables.push_back(storm::expressions::Expression(decisionVar));
656 for (
auto const &state : belief) {
659 storm::expressions::Expression leftSide = deltas[
i];
660 storm::expressions::Expression targetValue = lpSolver->getConstant(candidate[
i]);
661 if (candidate.find(state.first) != candidate.end()) {
662 targetValue = lpSolver->getConstant(candidate.at(state.first));
668 storm::expressions::Expression rightSide =
669 lpSolver->getConstant(state.second) - targetValue + storm::expressions::Expression(bigDelta) * targetValue -
673 lpSolver->addConstraint(
"state_eq_" + std::to_string(
i) +
"_" + std::to_string(gridCandidates.size() - 1), leftSide >= rightSide);
683 auto helperIt = helper.end() - 1;
684 while (*helperIt == *(helperIt - 1)) {
687 STORM_LOG_ASSERT(helperIt != helper.begin(),
"Error in grid clipping - index wrong");
692 while (helperIt != helper.end()) {
708 lpSolver->optimize();
710 BeliefId targetBelief = noId();
712 BeliefType deltaValues;
715 if (lpSolver->isOptimal()) {
716 optDelta = lpSolver->getObjectiveValue();
717 for (uint64_t dist = 0; dist < gridCandidates.size(); ++dist) {
718 if (lpSolver->getBinaryValue(lpSolver->getManager().getVariable(
"a_" + std::to_string(dist)))) {
719 targetBelief = getOrAddBeliefId(gridCandidates[dist]);
724 for (
auto const &state : belief) {
725 auto val = lpSolver->getContinuousValue(lpSolver->getManager().getVariable(
"d_" + std::to_string(
i)));
727 deltaValues.emplace(state.first, val);
736 STORM_LOG_WARN(
"LP solver returned an optimal value of 0. This should definitely not happen when using a grid");
743 STORM_LOG_WARN(
"LP solver returned an optimal value of 1. Sum of state clipping values is " << deltaSum);
754 return BeliefClipping{lpSolver->isOptimal(), noId(), targetBelief, optDelta, deltaValues,
false};
757template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
759 STORM_LOG_ASSERT(pomdp.getInitialStates().getNumberOfSetBits() < 2,
"POMDP contains more than one initial state");
760 STORM_LOG_ASSERT(pomdp.getInitialStates().getNumberOfSetBits() == 1,
"POMDP does not contain an initial state");
765 return getOrAddBeliefId(belief);
768template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
770 BeliefType
const &belief) {
771 uint32_t obs = getBeliefObservation(belief);
772 STORM_LOG_ASSERT(obs < beliefToIdMap.size(),
"Belief has unknown observation.");
773 auto insertioRes = beliefToIdMap[obs].emplace(belief, beliefs.size());
774 if (insertioRes.second) {
777 beliefs.push_back(belief);
780 return insertioRes.first->second;
782template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
784 return getBelief(beliefId).begin()->first;
787template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
789 if (pomdp.hasObservationValuations()) {
792 STORM_LOG_TRACE(
"Cannot get observation labels as no observation valuation has been defined for the POMDP. Return empty label instead.");
797template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
798std::vector<BeliefValueType> BeliefManager<PomdpType, BeliefValueType, StateType>::getBeliefAsVector(BeliefId
const &beliefId) {
799 return getBeliefAsVector(getBelief(beliefId));
802template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
803std::vector<BeliefValueType> BeliefManager<PomdpType, BeliefValueType, StateType>::getBeliefAsVector(
const BeliefType &belief) {
805 for (
auto const &stateprob : belief) {
806 res[stateprob.first] = stateprob.second;
811template<
typename PomdpType,
typename BeliefValueType,
typename StateType>
814 std::vector<BeliefValueType> beliefAsVector = getBeliefAsVector(beliefId);
815 std::vector<BeliefValueType> res(matrix.
getRowCount());