Storm
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
GlobalPomdpMecChoiceEliminator.cpp
Go to the documentation of this file.
2#include <vector>
3
10
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.");
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
90 ChoiceSelector<ValueType> cs(pomdp);
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
132 ChoiceSelector<ValueType> cs(pomdp);
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(
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(
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 {
235 STORM_LOG_THROW(mc.canHandle(propositionalFormula), storm::exceptions::InvalidPropertyException,
236 "Propositional model checker can not handle formula " << propositionalFormula);
237 return mc.check(propositionalFormula)->asExplicitQualitativeCheckResult().getTruthValuesVector();
238}
239
240template class GlobalPomdpMecChoiceEliminator<storm::RationalNumber>;
241
242template class GlobalPomdpMecChoiceEliminator<double>;
243} // namespace transformer
244} // 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:205
virtual bool isOperatorFormula() const
Definition Formula.cpp:180
virtual bool isProbabilityOperatorFormula() const
Definition Formula.cpp:172
OperatorFormula & asOperatorFormula()
Definition Formula.cpp:469
virtual bool isRewardOperatorFormula() const
Definition Formula.cpp:176
std::shared_ptr< Formula const > asSharedPointer()
Definition Formula.cpp:548
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:15
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:18
void complement()
Negates all bits in the bit vector.
bool empty() const
Retrieves whether no bits are set to true in this bit vector.
void clear()
Removes all set bits from the bit vector.
bool get(uint_fast64_t index) const
Retrieves the truth value of the bit at the given index and performs a bound check.
This class represents the decomposition of a nondeterministic model into its maximal end components.
A class that can be used to build a sparse matrix by adding value by value.
A class that holds a possibly non-square matrix in the compressed row storage format.
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:835
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:997
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:757
bool isZero(ValueType const &a)
Definition constants.cpp:41
LabParser.cpp.
Definition cli.cpp:18
ComparisonType comparisonType
Definition Bound.h:18