Storm 1.13.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
GlobalPomdpMecChoiceEliminator.cpp
Go to the documentation of this file.
2
3#include <vector>
4
10#include "storm/utility/graph.h"
12
13namespace storm {
14namespace transformer {
15
16template<typename ValueType>
18
19template<typename ValueType>
20std::shared_ptr<storm::models::sparse::Pomdp<ValueType>> GlobalPomdpMecChoiceEliminator<ValueType>::transform(storm::logic::Formula const& formula) const {
21 // check whether the property is minimizing or maximizing
22 STORM_LOG_THROW(formula.isOperatorFormula(), storm::exceptions::InvalidPropertyException, "Expected an operator formula");
23 STORM_LOG_THROW(formula.asOperatorFormula().hasOptimalityType() || formula.asOperatorFormula().hasBound(), storm::exceptions::InvalidPropertyException,
24 "The formula " << formula << " does not specify whether to minimize or maximize.");
25 bool minimizes = (formula.asOperatorFormula().hasOptimalityType() && storm::solver::minimize(formula.asOperatorFormula().getOptimalityType())) ||
27
28 std::shared_ptr<storm::logic::Formula const> subformula = formula.asOperatorFormula().getSubformula().asSharedPointer();
29 // If necessary, convert the subformula to a more general case
30 if (subformula->isEventuallyFormula() && formula.isProbabilityOperatorFormula()) {
31 subformula = std::make_shared<storm::logic::UntilFormula>(storm::logic::Formula::getTrueFormula(),
32 subformula->asEventuallyFormula().getSubformula().asSharedPointer());
33 }
34
35 if (formula.isProbabilityOperatorFormula() && subformula->isUntilFormula()) {
36 if (!minimizes) {
37 return transformMax(subformula->asUntilFormula());
38 }
39 } else if (formula.isRewardOperatorFormula() && subformula->isEventuallyFormula()) {
40 if (minimizes) {
41 return transformMinReward(subformula->asEventuallyFormula());
42 }
43 }
44 STORM_LOG_THROW(false, storm::exceptions::InvalidPropertyException, "Mec elimination is not supported for the property " << formula);
45 return nullptr;
46}
47
48template<typename ValueType>
49std::shared_ptr<storm::models::sparse::Pomdp<ValueType>> GlobalPomdpMecChoiceEliminator<ValueType>::transformMinReward(
50 storm::logic::EventuallyFormula const& formula) const {
51 assert(formula.isRewardPathFormula());
52 auto backwardTransitions = pomdp.getBackwardTransitions();
53 storm::storage::BitVector allStates(pomdp.getNumberOfStates(), true);
54 auto prob1EStates = storm::utility::graph::performProb1E(pomdp.getTransitionMatrix(), pomdp.getTransitionMatrix().getRowGroupIndices(), backwardTransitions,
55 allStates, checkPropositionalFormula(formula.getSubformula()));
56 STORM_LOG_THROW(prob1EStates.full(), storm::exceptions::InvalidPropertyException,
57 "There are states from which the set of target states is not reachable. This is not supported.");
58 auto prob1AStates = storm::utility::graph::performProb1A(pomdp.getTransitionMatrix(), pomdp.getTransitionMatrix().getRowGroupIndices(), backwardTransitions,
59 allStates, checkPropositionalFormula(formula.getSubformula()));
60
61 auto mecs = decomposeEndComponents(~prob1AStates, ~allStates);
62
63 // Get the 'out' state for every MEC with just a single out state
64 storm::storage::BitVector uniqueOutStates = getEndComponentsWithSingleOutStates(mecs);
65
66 // For each observation of some 'out' state get the intersection of the choices that lead to the corresponding MEC
67 std::vector<storm::storage::BitVector> mecChoicesPerObservation = getEndComponentChoicesPerObservation(mecs, uniqueOutStates);
68
69 // Filter the observations that have a state that is not an out state
70 storm::storage::BitVector stateFilter = ~uniqueOutStates;
71 for (auto const state : stateFilter) {
72 mecChoicesPerObservation[pomdp.getObservation(state)].clear();
73 }
74
75 // It should not be possible to clear all choices for an observation since we only consider states that lead outside of its MEC.
76 for (auto& clearedChoices : mecChoicesPerObservation) {
77 STORM_LOG_ASSERT(clearedChoices.size() == 0 || !clearedChoices.full(), "Tried to clear all choices for an observation.");
78 }
79
80 // transform the set of selected choices to global choice indices
81 storm::storage::BitVector choiceFilter(pomdp.getNumberOfChoices(), true);
82 stateFilter.complement();
83 for (auto const state : stateFilter) {
84 uint64_t offset = pomdp.getTransitionMatrix().getRowGroupIndices()[state];
85 for (auto const& choice : mecChoicesPerObservation[pomdp.getObservation(state)]) {
86 choiceFilter.set(offset + choice, false);
87 }
88 }
89
91 auto res = cs.transform(choiceFilter)->template as<storm::models::sparse::Pomdp<ValueType>>();
92 res->setIsCanonic();
93 return res;
94}
95
96template<typename ValueType>
97std::shared_ptr<storm::models::sparse::Pomdp<ValueType>> GlobalPomdpMecChoiceEliminator<ValueType>::transformMax(
98 storm::logic::UntilFormula const& formula) const {
99 auto backwardTransitions = pomdp.getBackwardTransitions();
100 auto prob01States = storm::utility::graph::performProb01Max(pomdp.getTransitionMatrix(), pomdp.getTransitionMatrix().getRowGroupIndices(),
101 backwardTransitions, checkPropositionalFormula(formula.getLeftSubformula()),
102 checkPropositionalFormula(formula.getRightSubformula()));
103 auto mecs = decomposeEndComponents(~(prob01States.first | prob01States.second), prob01States.first);
104
105 // Get the 'out' state for every MEC with just a single out state
106 storm::storage::BitVector uniqueOutStates = getEndComponentsWithSingleOutStates(mecs);
107
108 // For each observation of some 'out' state get the intersection of the choices that lead to the corresponding MEC
109 std::vector<storm::storage::BitVector> mecChoicesPerObservation = getEndComponentChoicesPerObservation(mecs, uniqueOutStates);
110
111 // Filter the observations that have a state that is neither an out state, nor a prob0A state
112 storm::storage::BitVector stateFilter = ~(uniqueOutStates | prob01States.first);
113 for (auto const state : stateFilter) {
114 mecChoicesPerObservation[pomdp.getObservation(state)].clear();
115 }
116
117 // It should not be possible to clear all choices for an observation since we only consider states that lead outside of its MEC.
118 for (auto& clearedChoices : mecChoicesPerObservation) {
119 STORM_LOG_ASSERT(clearedChoices.size() == 0 || !clearedChoices.full(), "Tried to clear all choices for an observation.");
120 }
121
122 // transform the set of selected choices to global choice indices
123 storm::storage::BitVector choiceFilter(pomdp.getNumberOfChoices(), true);
124 stateFilter.complement();
125 for (auto const state : stateFilter) {
126 uint64_t offset = pomdp.getTransitionMatrix().getRowGroupIndices()[state];
127 for (auto const& choice : mecChoicesPerObservation[pomdp.getObservation(state)]) {
128 choiceFilter.set(offset + choice, false);
129 }
130 }
131
133 auto res = cs.transform(choiceFilter)->template as<storm::models::sparse::Pomdp<ValueType>>();
134 res->setIsCanonic();
135 return res;
136}
137
138template<typename ValueType>
139storm::storage::BitVector GlobalPomdpMecChoiceEliminator<ValueType>::getEndComponentsWithSingleOutStates(
140 storm::storage::MaximalEndComponentDecomposition<ValueType> const& mecs) const {
141 storm::storage::BitVector result(pomdp.getNumberOfStates(), false);
142 for (auto const& mec : mecs) {
143 std::optional<uint64_t> uniqueOutState = std::nullopt;
144 for (auto const& stateActionsPair : mec) {
145 // Check whether this is an 'out' state, i.e., not all actions stay inside the MEC
146 if (stateActionsPair.second.size() != pomdp.getNumberOfChoices(stateActionsPair.first)) {
147 if (uniqueOutState) {
148 // we already found one out state, so this mec is invalid
149 uniqueOutState = std::nullopt;
150 break;
151 } else {
152 uniqueOutState = stateActionsPair.first;
153 }
154 }
155 }
156 if (uniqueOutState) {
157 result.set(uniqueOutState.value(), true);
158 }
159 }
160 return result;
161}
162
163template<typename ValueType>
164std::vector<storm::storage::BitVector> GlobalPomdpMecChoiceEliminator<ValueType>::getEndComponentChoicesPerObservation(
165 storm::storage::MaximalEndComponentDecomposition<ValueType> const& mecs, storm::storage::BitVector const& consideredStates) const {
166 std::vector<storm::storage::BitVector> result(pomdp.getNrObservations());
167 for (auto const& mec : mecs) {
168 for (auto const& stateActions : mec) {
169 if (consideredStates.get(stateActions.first)) {
170 storm::storage::BitVector localChoiceIndices(pomdp.getNumberOfChoices(stateActions.first), false);
171 uint64_t offset = pomdp.getTransitionMatrix().getRowGroupIndices()[stateActions.first];
172 for (auto const& choice : stateActions.second) {
173 assert(choice >= offset);
174 localChoiceIndices.set(choice - offset, true);
175 }
176
177 auto& mecChoices = result[pomdp.getObservation(stateActions.first)];
178 if (mecChoices.size() == 0) {
179 mecChoices = localChoiceIndices;
180 } else {
181 STORM_LOG_ASSERT(mecChoices.size() == localChoiceIndices.size(),
182 "Observation action count does not match for two states with the same observation");
183 mecChoices &= localChoiceIndices;
184 }
185 }
186 }
187 }
188 return result;
189}
190
191template<typename ValueType>
192storm::storage::MaximalEndComponentDecomposition<ValueType> GlobalPomdpMecChoiceEliminator<ValueType>::decomposeEndComponents(
193 storm::storage::BitVector const& subsystem, storm::storage::BitVector const& redirectingStates) const {
194 if (redirectingStates.empty()) {
195 return storm::storage::MaximalEndComponentDecomposition<ValueType>(pomdp.getTransitionMatrix(), pomdp.getBackwardTransitions(), subsystem);
196 } else {
197 // Redirect all incoming transitions of a redirictingState back to the origin of the transition.
198 storm::storage::SparseMatrixBuilder<ValueType> builder(pomdp.getTransitionMatrix().getRowCount(), pomdp.getTransitionMatrix().getColumnCount(), 0, true,
199 true, pomdp.getTransitionMatrix().getRowGroupCount());
200 uint64_t row = 0;
201 for (uint64_t rowGroup = 0; rowGroup < pomdp.getTransitionMatrix().getRowGroupCount(); ++rowGroup) {
202 assert(row == pomdp.getTransitionMatrix().getRowGroupIndices()[rowGroup]);
203 builder.newRowGroup(row);
204 for (; row < pomdp.getTransitionMatrix().getRowGroupIndices()[rowGroup + 1]; ++row) {
205 ValueType redirectedProbabilityMass = pomdp.getTransitionMatrix().getConstrainedRowSum(row, redirectingStates);
206 bool insertSelfloop = !storm::utility::isZero(redirectedProbabilityMass);
207 for (auto const& entry : pomdp.getTransitionMatrix().getRow(row)) {
208 if (!redirectingStates.get(entry.getColumn())) {
209 if (insertSelfloop && entry.getColumn() >= rowGroup) {
210 // insert selfloop now
211 insertSelfloop = false;
212 if (entry.getColumn() == rowGroup) {
213 builder.addNextValue(row, rowGroup, entry.getValue() + redirectedProbabilityMass);
214 continue;
215 } else {
216 builder.addNextValue(row, rowGroup, redirectedProbabilityMass);
217 }
218 }
219 builder.addNextValue(row, entry.getColumn(), entry.getValue());
220 }
221 }
222 if (insertSelfloop) {
223 builder.addNextValue(row, rowGroup, redirectedProbabilityMass);
224 }
225 }
226 }
227 storm::storage::SparseMatrix<ValueType> transitionMatrix = builder.build();
228 return storm::storage::MaximalEndComponentDecomposition<ValueType>(transitionMatrix, transitionMatrix.transpose(true), subsystem);
229 }
230}
231
232template<typename ValueType>
233storm::storage::BitVector GlobalPomdpMecChoiceEliminator<ValueType>::checkPropositionalFormula(storm::logic::Formula const& propositionalFormula) const {
234 storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Mdp<ValueType>> mc(pomdp);
235 STORM_LOG_THROW(mc.canHandle(propositionalFormula), storm::exceptions::InvalidPropertyException,
236 "Propositional model checker can not handle formula " << propositionalFormula);
237 return mc.check(propositionalFormula)->template asExplicitQualitativeCheckResult<ValueType>().getTruthValuesVector();
238}
239
242} // namespace transformer
243} // namespace storm
Formula const & getRightSubformula() const
Formula const & getLeftSubformula() const
virtual bool isRewardPathFormula() const override
static std::shared_ptr< Formula const > getTrueFormula()
Definition Formula.cpp:213
virtual bool isOperatorFormula() const
Definition Formula.cpp:188
virtual bool isProbabilityOperatorFormula() const
Definition Formula.cpp:180
OperatorFormula & asOperatorFormula()
Definition Formula.cpp:492
virtual bool isRewardOperatorFormula() const
Definition Formula.cpp:184
std::shared_ptr< Formula const > asSharedPointer()
Definition Formula.cpp:571
Bound const & getBound() const
storm::solver::OptimizationDirection const & getOptimalityType() const
Formula const & getSubformula() const
Formula const & getSubformula() const
This class represents a partially observable Markov decision process.
Definition Pomdp.h:13
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:16
bool empty() const
Retrieves whether no bits are set to true in this bit vector.
bool get(uint64_t index) const
Retrieves the truth value of the bit at the given index and performs a bound check.
storm::storage::SparseMatrix< value_type > transpose(bool joinGroups=false, bool keepZeros=false) const
Transposes the matrix.
GlobalPomdpMecChoiceEliminator(storm::models::sparse::Pomdp< ValueType > const &pomdp)
std::shared_ptr< storm::models::sparse::Pomdp< ValueType > > transform(storm::logic::Formula const &formula) const
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:11
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
SFTBDDChecker::ValueType ValueType
bool isLowerBound(ComparisonType t)
bool constexpr minimize(OptimizationDirection d)
std::pair< storm::storage::BitVector, storm::storage::BitVector > performProb01Max(storm::storage::SparseMatrix< T > const &transitionMatrix, std::vector< uint_fast64_t > const &nondeterministicChoiceIndices, storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates)
Definition graph.cpp:819
storm::storage::BitVector performProb1A(storm::models::sparse::NondeterministicModel< T, RM > const &model, storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates)
Computes the sets of states that have probability 1 of satisfying phi until psi under all possible re...
Definition graph.cpp:981
storm::storage::BitVector performProb1E(storm::storage::SparseMatrix< T > const &transitionMatrix, std::vector< uint_fast64_t > const &nondeterministicChoiceIndices, storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates, boost::optional< storm::storage::BitVector > const &choiceConstraint)
Computes the sets of states that have probability 1 of satisfying phi until psi under at least one po...
Definition graph.cpp:741
bool isZero(ValueType const &a)
Definition constants.cpp:39
ComparisonType comparisonType
Definition Bound.h:18