Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
ClippingBeliefAbstraction.cpp
Go to the documentation of this file.
2
4
14
16
17template<typename BeliefType>
18ClippingBeliefAbstraction<BeliefType>::ClippingBeliefAbstraction(storm::Environment const& env, std::vector<uint64_t>&& observationResolutions)
19 : observationResolutions(std::forward<std::vector<uint64_t>>(observationResolutions)) {
20 STORM_LOG_ASSERT(std::all_of(this->observationResolutions.begin(), this->observationResolutions.end(), [](auto o) { return o > 0; }),
21 "Expected that the resolutions are positive.");
22 lpSolver = storm::utility::solver::getLpSolver<BeliefValueType>(env, "POMDP LP Solver");
23 lpSolver->push();
24}
25
26template<typename BeliefType>
27ClippingBeliefAbstraction<BeliefType>::ClippingBeliefAbstraction(storm::Environment const& env, std::vector<uint64_t>&& observationResolutions,
28 std::vector<BeliefValueType>&& extremalRewardValues)
29 : observationResolutions(std::forward<std::vector<uint64_t>>(observationResolutions)),
30 extremalRewardValues(std::forward<std::vector<BeliefValueType>>(extremalRewardValues)) {
31 STORM_LOG_ASSERT(std::all_of(this->observationResolutions.begin(), this->observationResolutions.end(), [](auto o) { return o > 0; }),
32 "Expected that the resolutions are positive.");
33 lpSolver = storm::utility::solver::getLpSolver<BeliefValueType>(env, "POMDP LP Solver");
34 lpSolver->push();
35}
36
37template<typename BeliefType>
39 const uint64_t resolution) {
40 lpSolver->pop();
41 lpSolver->push();
42
43 auto const resolutionConverted = storm::utility::convertNumber<BeliefValueType>(resolution);
44
45 std::vector<BeliefValueType> helper(belief.size(), storm::utility::zero<BeliefValueType>());
46 helper[0] = resolutionConverted;
47 bool done = false;
48 // Set-up Variables
49 std::vector<storm::expressions::Expression> decisionVariables;
50 // Add variable for the clipping value, it is to be minimized
51 auto bigDelta = lpSolver->addBoundedContinuousVariable("D", storm::utility::zero<BeliefValueType>(), storm::utility::one<BeliefValueType>(),
53 // State clipping values
54 std::vector<storm::expressions::Expression> deltas;
55 uint64_t i = 0;
56 belief.forEach([this, &deltas, &i](BeliefStateType const& state, BeliefValueType const& beliefValue) {
57 auto localDelta = lpSolver->addBoundedContinuousVariable("d_" + std::to_string(i), storm::utility::zero<BeliefValueType>(), beliefValue);
58 deltas.push_back(storm::expressions::Expression(localDelta));
59 ++i;
60 });
61 lpSolver->update();
62 std::vector<BeliefType> gridCandidates;
63 while (!done) {
64 BeliefBuilder<BeliefType> candidateBuilder;
65 candidateBuilder.setObservation(belief.observation());
66
67 uint64_t j{0};
68 uint64_t const jMax = belief.size() - 1;
69 belief.forEach([&helper, &j, &resolutionConverted, &candidateBuilder, &jMax](BeliefStateType const& state, BeliefValueType const& beliefValue) {
70 if (j < jMax) {
71 if (!BeliefNumerics<BeliefValueType>::isZero(helper[j] - helper[j + 1])) {
72 candidateBuilder.addValue(state, (helper[j] - helper[j + 1]) / resolutionConverted);
73 }
74 } else {
75 if (!BeliefNumerics<BeliefValueType>::isZero(helper[jMax])) {
76 candidateBuilder.addValue(state, helper[jMax] / resolutionConverted);
77 }
78 }
79 ++j;
80 });
81 auto candidate = candidateBuilder.build();
82 if (candidate == belief) {
83 STORM_LOG_TRACE(belief.toString() << " on clipping grid.");
84 // TODO Improve handling of successors which are already on the grid
85 return BeliefClipping{false, std::move(candidate), storm::utility::zero<BeliefValueType>(), {}, true};
86 } else {
87 gridCandidates.push_back(candidate);
88
89 // Add variables a_j
90 auto decisionVar = lpSolver->addBinaryVariable("a_" + std::to_string(gridCandidates.size() - 1));
91 decisionVariables.push_back(storm::expressions::Expression(decisionVar));
92 lpSolver->update();
93
94 i = 0;
95 belief.forEachCombine(candidate, [&](BeliefStateType const& state, BeliefValueType const& beliefValue, BeliefValueType const& candidateValue) {
96 // Add the constraint to describe the transformation between the state values in the beliefs
97 // Add d_i >= b(s_i) - b_j(s_i) + D * b_j(s_i) - 1 + a_j
98 lpSolver->addConstraint("state_eq_" + std::to_string(i) + "_" + std::to_string(gridCandidates.size() - 1),
99 deltas.at(i) >= lpSolver->getConstant(beliefValue) - lpSolver->getConstant(candidateValue) +
100 storm::expressions::Expression(bigDelta) * lpSolver->getConstant(candidateValue) -
101 lpSolver->getConstant(storm::utility::one<BeliefValueType>()) +
102 storm::expressions::Expression(decisionVar));
103 ++i;
104 lpSolver->update();
105 });
106 }
107 if (helper.back() == storm::utility::convertNumber<BeliefValueType>(resolution)) {
108 // If the last entry of helper is the gridResolution, we have enumerated all necessary distributions
109 done = true;
110 } else {
111 // Update helper by finding the index to increment
112 auto helperIt = helper.end() - 1;
113 while (*helperIt == *(helperIt - 1)) {
114 --helperIt;
115 }
116 STORM_LOG_ASSERT(helperIt != helper.begin(), "Error in grid clipping - index wrong");
117 // Increment the value at the index
118 *helperIt += 1;
119 // Reset all indices greater than the changed one to 0
120 ++helperIt;
121 while (helperIt != helper.end()) {
122 *helperIt = 0;
123 ++helperIt;
124 }
125 }
126 }
127
128 // Only one target belief should be chosen
129 lpSolver->addConstraint("choice", storm::expressions::sum(decisionVariables) == lpSolver->getConstant(storm::utility::one<BeliefValueType>()));
130 // Link D and d_i
131 lpSolver->addConstraint("delta", storm::expressions::Expression(bigDelta) == storm::expressions::sum(deltas));
132 // Exclude D = 0 (self-loop)
133 lpSolver->addConstraint("not_zero", storm::expressions::Expression(bigDelta) > lpSolver->getConstant(storm::utility::zero<BeliefValueType>()));
134
135 lpSolver->update();
136
137 lpSolver->optimize();
138 // Get the optimal belief for clipping
139 // Not a belief but has the same type
143 if (lpSolver->isOptimal()) {
144 uint64_t targetBeliefIndex = std::numeric_limits<uint64_t>::max();
145 optDelta = lpSolver->getObjectiveValue();
146 for (uint64_t dist = 0; dist < gridCandidates.size(); ++dist) {
147 if (lpSolver->getBinaryValue(lpSolver->getManager().getVariable("a_" + std::to_string(dist)))) {
148 targetBeliefIndex = dist;
149 break;
150 }
151 }
152 STORM_LOG_ASSERT(targetBeliefIndex < gridCandidates.size(), "LP optimal but no belief selected");
153 auto targetBelief = gridCandidates.at(targetBeliefIndex);
154 i = 0;
155 belief.forEachStateInSupport([this, &i, &deltaValues, &deltaSum](BeliefStateType const& state) {
156 auto val = lpSolver->getContinuousValue(lpSolver->getManager().getVariable("d_" + std::to_string(i)));
158 deltaValues.emplace(state, val);
159 deltaSum += val;
160 }
161 ++i;
162 });
163
164 // If we get an optimal value of 0, the LP solver considers two beliefs to be equal, possibly due to numerical instability
165 // To prevent infinite expansion, we throw an exception.
166 STORM_LOG_THROW(!BeliefNumerics<BeliefValueType>::isZero(optDelta), storm::exceptions::UnexpectedException,
167 "Grid clipping LP returned a clipping value of zero for distinct beliefs. This may be due to numeric instability. Origin: "
168 << belief.toString(false, true) << ", target: " << targetBelief.toString(false, true)
169 << ", objective: " << storm::utility::to_string(optDelta) << ", delta sum: " << storm::utility::to_string(deltaSum) << ".");
170
172 STORM_LOG_THROW(!BeliefNumerics<BeliefValueType>::isOne(deltaSum), storm::exceptions::UnexpectedException,
173 "Grid clipping LP returned a clipping value of one, leaving no retained probability. Origin: "
174 << belief.toString(false, true) << ", target: " << targetBelief.toString(false, true)
175 << ", objective: " << storm::utility::to_string(optDelta) << ", delta sum: " << storm::utility::to_string(deltaSum) << ".");
176
177 // Recover from a rounded objective using the reconstructed value.
178 optDelta = deltaSum;
179 }
180 STORM_LOG_TRACE("Clip " << belief.toString() << " to " << targetBelief.toString() << " with value " << optDelta);
181 return BeliefClipping{true, std::move(targetBelief), optDelta, deltaValues, false};
182 }
183 STORM_LOG_TRACE("Clipping " << belief.toString() << " not possible. LP not optimal.");
184 return BeliefClipping{false, belief, optDelta, deltaValues, false};
185}
186
189
190} // namespace storm::pomdp::beliefs
BeliefType build()
Builds the belief and returns it.
void setObservation(BeliefObservationType const &observation)
Sets the observation of the belief.
void addValue(BeliefStateType const &state, ValueType const &value)
Adds a probability value to the given state.
Approximates a belief by clipping it to an observation-specific grid point.
BeliefClipping clipBeliefToGrid(BeliefType const &belief, uint64_t resolution)
ClippingBeliefAbstraction(storm::Environment const &env, std::vector< uint64_t > &&observationResolutions)
Creates clipping grids with one resolution per POMDP observation.
#define STORM_LOG_TRACE(message)
Definition logging.h:22
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:39
Expression sum(std::vector< storm::expressions::Expression > const &expressions)
uint64_t BeliefStateType
Shared identifiers and sparse-container types used by the belief subsystem.
Definition types.h:10
boost::container::flat_map< BeliefStateType, BeliefValueType > BeliefFlatMap
Definition types.h:17
std::unique_ptr< storm::solver::LpSolver< ValueType > > getLpSolver(storm::Environment const &env, std::string const &name, storm::solver::LpSolverTypeSelection solvType)
Definition solver.cpp:146
ValueType zero()
Definition constants.cpp:26
std::string to_string(ValueType const &value)
ValueType one()
Definition constants.cpp:21
TargetType convertNumber(SourceType const &number)
static bool isZero(ValueType const &val)
static bool isOne(ValueType const &val)
static bool lessOrEqual(ValueType const &lhs, ValueType const &rhs)