96std::pair<std::shared_ptr<models::sparse::Mdp<BeliefMdpValueType>>, std::unordered_map<uint64_t, BeliefId>>
buildBeliefMdp(
99 std::function<std::unordered_map<std::string, BeliefMdpValueType>(BeliefType
const&)>
const& computeCutOffValueMap) {
103 STORM_LOG_ASSERT(isReachProb || isTotRew || isRewBndReachProb,
"Unexpected kind of property.");
105 bool constexpr extraDataCompatibleWithRewardAwareness =
106 sizeof...(ExtraTransitionData) == 1 && (std::is_same_v<std::vector<BeliefMdpValueType>, ExtraTransitionData> || ...);
115 STORM_LOG_THROW(!(isRewBndReachProb && propertyInformation.
dir == storm::OptimizationDirection::Minimize &&
117 [](
RewardBound const& bound) { return bound.lowerBound.has_value() && bound.upperBound.has_value(); })),
118 storm::exceptions::NotSupportedException,
119 "Reward-bounded probability minimisation does not support dimensions with both lower and upper reward bounds.");
124 uint64_t nrCutOffChoices = 0ull;
125 std::unordered_map<BeliefId, std::unordered_map<std::string, BeliefMdpValueType>> cutOffInformationMap;
126 for (
auto const& frontierBeliefId : frontierBeliefs) {
127 auto const& frontierBelief = explorationInformation.
discoveredBeliefs.getBeliefFromId(frontierBeliefId);
128 cutOffInformationMap[frontierBeliefId] = computeCutOffValueMap(frontierBelief);
129 nrCutOffChoices += cutOffInformationMap[frontierBeliefId].size();
132 constexpr bool clippingUsed = std::is_same_v<
ExplorationInformation<BeliefMdpValueType, BeliefType, ExtraTransitionData...>,
138 uint64_t
const numBottomTargetStates = isReachProb || isRewBndReachProb || clippingUsed ? 2ull : 1ull;
139 uint64_t
const numInitialTerminalStates = initialBeliefIsTerminal ? 1ull : 0ull;
140 uint64_t
const numExtraStates = numBottomTargetStates + frontierBeliefs.size() + numInitialTerminalStates;
141 uint64_t
const numStates = explorationInformation.
matrix.
groups() + numExtraStates;
142 uint64_t
const numChoices = explorationInformation.
matrix.
rows() + numBottomTargetStates + nrCutOffChoices + numInitialTerminalStates;
143 uint64_t
const targetState = numStates - numBottomTargetStates;
144 uint64_t
const bottomState = numStates - 1;
145 std::optional<models::sparse::ChoiceLabeling> optionalChoiceLabeling;
150 std::vector<BeliefMdpValueType> actionRewards;
152 actionRewards.reserve(numChoices);
153 actionRewards.insert(actionRewards.end(), explorationInformation.
actionRewards.begin(), explorationInformation.
actionRewards.end());
155 actionRewards.insert(actionRewards.end(), nrCutOffChoices + numInitialTerminalStates + numBottomTargetStates,
158 "Unexpected size of action rewards: Expected " << numChoices <<
" got " << actionRewards.size() <<
".");
161 std::unordered_map<BeliefId, uint64_t> frontierBeliefToStateMap;
162 std::unordered_map<uint64_t, BeliefId> stateToFrontierBeliefMap;
163 uint64_t nextStateId = numStates - numExtraStates;
164 std::optional<uint64_t> initialTerminalState;
165 if (initialBeliefIsTerminal) {
166 initialTerminalState = nextStateId++;
168 for (
auto const& frontierBeliefId : frontierBeliefs) {
169 frontierBeliefToStateMap.emplace(frontierBeliefId, nextStateId);
170 stateToFrontierBeliefMap.emplace(nextStateId, frontierBeliefId);
175 std::vector<storm::storage::SparseMatrixBuilder<BeliefMdpValueType>> transitionRewardBuilderVector;
177 if constexpr (extraDataCompatibleWithRewardAwareness) {
178 if (isRewBndReachProb) {
179 for (uint64_t i = 0; i < propertyInformation.
rewardBounds.size(); ++i) {
180 transitionRewardBuilderVector.emplace_back(numChoices, numStates, 0,
true,
true, numStates);
187 std::vector<std::pair<uint64_t, uint64_t>> rowSuccessors;
189 for (uint64_t state = 0; state < numStates - numExtraStates; ++state) {
192 for (
auto& transitionRewardBuilder : transitionRewardBuilderVector) {
193 transitionRewardBuilder.newRowGroup(choice);
195 for (uint64_t
const groupEnd = explorationInformation.
matrix.
rowGroupIndices[state + 1]; choice < groupEnd; ++choice) {
196 if constexpr (extraDataCompatibleWithRewardAwareness) {
199 "Duplicate transition reward in choice " << choice <<
".");
203 rowSuccessors.clear();
209 rowSuccessors.emplace_back(explIt->second, entryIndex);
212 BeliefMdpValueType successorValue;
218 successorValue = entry.probability * terminalIt->second;
220 probabilityToTarget += successorValue;
221 probabilityToBottom += entry.probability - successorValue;
224 "Terminal reward accumulation requires an expected total reachability reward property.");
225 probabilityToTarget += entry.probability;
226 actionRewards[choice] += successorValue;
230 auto const frontierIt = frontierBeliefToStateMap.find(entry.targetBelief);
231 STORM_LOG_ASSERT(frontierIt != frontierBeliefToStateMap.end(),
"Unknown frontier belief.");
232 rowSuccessors.emplace_back(frontierIt->second, entryIndex);
235 if constexpr (clippingUsed) {
237 auto const& clippingProbability = std::get<0>(entry.data);
238 auto const& rewardPenalty = std::get<1>(entry.data);
240 if (clippingProbability) {
243 probabilityToTarget += *clippingProbability;
245 probabilityToBottom += *clippingProbability;
247 }
else if (rewardPenalty) {
252 probabilityToBottom += *clippingProbability;
254 actionRewards[choice] += *rewardPenalty;
255 probabilityToTarget += *clippingProbability;
258 probabilityToTarget += *clippingProbability;
264 if (!std::is_sorted(rowSuccessors.begin(), rowSuccessors.end())) {
265 std::sort(rowSuccessors.begin(), rowSuccessors.end());
267 for (
auto const& [successorState, entryIndex] : rowSuccessors) {
269 transitionBuilder.
addNextValue(choice, successorState, entry.probability);
270 if constexpr (extraDataCompatibleWithRewardAwareness) {
271 if (isRewBndReachProb) {
272 for (uint64_t i = 0; i < propertyInformation.
rewardBounds.size(); ++i) {
274 transitionRewardBuilderVector.at(i).addNextValue(choice, successorState, entry.data[i]);
282 transitionBuilder.
addNextValue(choice, targetState, probabilityToTarget);
285 transitionBuilder.
addNextValue(choice, bottomState, probabilityToBottom);
287 if (optionalChoiceLabeling.has_value()) {
289 if (!optionalChoiceLabeling.value().containsLabel(label)) {
290 optionalChoiceLabeling.value().addLabel(label);
292 optionalChoiceLabeling.value().addLabelToChoice(label, choice);
298 uint64_t choice = explorationInformation.
matrix.
rows();
299 uint64_t
const firstFrontierState = numStates - numExtraStates + numInitialTerminalStates;
300 for (uint64_t state = firstFrontierState; state < numStates - numBottomTargetStates; ++state) {
302 for (
auto& transitionRewardBuilder : transitionRewardBuilderVector) {
303 transitionRewardBuilder.newRowGroup(choice);
305 std::unordered_map<std::string, BeliefMdpValueType> cutOffInformationForBelief = cutOffInformationMap.at(stateToFrontierBeliefMap.at(state));
306 for (
auto const& entry : cutOffInformationForBelief) {
307 if (isReachProb || isRewBndReachProb) {
309 transitionBuilder.
addNextValue(choice, targetState, entry.second);
313 transitionBuilder.
addNextValue(choice, bottomState, probabilityToBottom);
318 actionRewards[choice] += entry.second;
321 if (optionalChoiceLabeling.has_value()) {
322 if (!optionalChoiceLabeling.value().containsLabel(entry.first)) {
323 optionalChoiceLabeling.value().addLabel(entry.first);
325 optionalChoiceLabeling.value().addLabelToChoice(entry.first, choice);
330 if (initialTerminalState.has_value()) {
335 transitionBuilder.
addNextValue(choice, targetState, terminalValue);
339 transitionBuilder.
addNextValue(choice, bottomState, probabilityToBottom);
342 STORM_LOG_ASSERT(isTotRew,
"Unexpected terminal initial belief for this property type.");
344 actionRewards[choice] += terminalValue;
348 STORM_LOG_ASSERT(choice == numChoices - numBottomTargetStates,
"Unexpected choice layout.");
351 transitionBuilder.
newRowGroup(numChoices - numBottomTargetStates);
352 if (optionalChoiceLabeling.has_value()) {
353 if (!optionalChoiceLabeling.value().containsLabel(
"__loop__")) {
354 optionalChoiceLabeling.value().addLabel(
"__loop__");
356 optionalChoiceLabeling.value().addLabelToChoice(
"__loop__", numChoices - numBottomTargetStates);
359 for (uint64_t i = 0; i < transitionRewardBuilderVector.size(); ++i) {
360 auto& transitionRewardBuilder = transitionRewardBuilderVector[i];
361 transitionRewardBuilder.newRowGroup(numChoices - numBottomTargetStates);
363 if (propertyInformation.
dir == storm::OptimizationDirection::Minimize && propertyInformation.
rewardBounds[i].lowerBound.has_value()) {
367 if (isReachProb || isRewBndReachProb || clippingUsed) {
370 for (
auto& transitionRewardBuilder : transitionRewardBuilderVector) {
371 transitionRewardBuilder.newRowGroup(numChoices - 1);
373 if (optionalChoiceLabeling.has_value()) {
374 if (!optionalChoiceLabeling.value().containsLabel(
"__loop__")) {
375 optionalChoiceLabeling.value().addLabel(
"__loop__");
377 optionalChoiceLabeling.value().addLabelToChoice(
"__loop__", numChoices - 1);
383 if (isRewBndReachProb) {
388 for (
auto const& [belId, state] : explorationInformation.
exploredBeliefs) {
394 for (
auto const& belId : frontierBeliefs) {
397 stateLabeling.
addLabelToState(
"target", frontierBeliefToStateMap.at(belId));
407 }
else if (initialTerminalState.has_value()) {
412 stateLabeling.
addLabel(
"truncated");
413 for (uint64_t state = firstFrontierState; state < numStates - numBottomTargetStates; ++state) {
417 if (isReachProb || isRewBndReachProb || clippingUsed) {
426 }
else if (isRewBndReachProb) {
428 for (
auto& transitionRewardBuilder : transitionRewardBuilderVector) {
434 if (optionalChoiceLabeling.has_value()) {
439 std::unordered_map<uint64_t, BeliefId> stateToBeliefMap;
441 for (
auto const& [beliefId, stateIndex] : explorationInformation.
exploredBeliefs) {
442 stateToBeliefMap[stateIndex] = beliefId;
445 for (
auto const& [stateIndex, beliefId] : stateToFrontierBeliefMap) {
446 stateToBeliefMap[stateIndex] = beliefId;
448 if (initialTerminalState.has_value()) {
449 stateToBeliefMap[initialTerminalState.value()] = explorationInformation.
initialBeliefId;