Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
SmtBasedPermissiveSchedulers.h
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <cstdint>
5#include <string>
6#include <unordered_map>
7#include <vector>
8
14
15namespace storm {
16namespace ps {
17
18template<typename RM>
20 private:
21 bool mPerformedSmtLoop = false;
22 bool mFoundSolution = false;
25 std::unordered_map<storm::storage::StateActionPair, storm::expressions::Variable> multistrategyVariables;
26 std::unordered_map<storm::expressions::Variable, bool> multistrategyVariablesToTakenMap;
27 std::unordered_map<uint_fast64_t, storm::expressions::Variable> mProbVariables;
28 std::unordered_map<uint_fast64_t, storm::expressions::Variable> mAlphaVariables;
29 std::unordered_map<storm::storage::StateActionTarget, storm::expressions::Variable> mBetaVariables;
30 std::unordered_map<uint_fast64_t, storm::expressions::Variable> mGammaVariables;
31
32 public:
34 storm::storage::BitVector const& goalstates, storm::storage::BitVector const& sinkstates)
35 : PermissiveSchedulerComputation<RM>(mdp, goalstates, sinkstates),
36 mPerformedSmtLoop(false),
37 mFoundSolution(false),
38 solver(smtSolver),
39 manager(solver.getManager()) {}
40
41 void calculatePermissiveScheduler(bool lowerBound, double boundary) override {
42 performSmtLoop(lowerBound, boundary, this->mPenalties);
43 mPerformedSmtLoop = true;
44 }
45
46 bool foundSolution() const override {
47 STORM_LOG_ASSERT(mPerformedSmtLoop, "SMT loop not performed.");
48 return mFoundSolution;
49 }
50
52 STORM_LOG_ASSERT(foundSolution(), "Solution not found.");
53 SubMDPPermissiveScheduler<RM> result(this->mdp, true);
54 for (auto const& entry : multistrategyVariables) {
55 if (!multistrategyVariablesToTakenMap.at(entry.second)) {
56 result.disable(this->mdp.getChoiceIndex(entry.first));
57 }
58 }
59 return result;
60 }
61
62 private:
66 void createVariables(storm::storage::BitVector const& relevantStates) {
68 for (uint_fast64_t s : relevantStates) {
69 // Create x_s variables
70 var = manager.declareRationalVariable("x_" + std::to_string(s));
71 solver.add(var >= manager.rational(0));
72 solver.add(var <= manager.rational(1));
73 mProbVariables[s] = var;
74 // Create alpha_s variables
75 var = manager.declareBooleanVariable("alp_" + std::to_string(s));
76 mAlphaVariables[s] = var;
77 // Create gamma_s variables
78 var = manager.declareRationalVariable("gam_" + std::to_string(s));
79 solver.add(var >= manager.rational(0));
80 solver.add(var <= manager.rational(1));
81 mGammaVariables[s] = var;
82 for (uint_fast64_t a = 0; a < this->mdp.getNumberOfChoices(s); ++a) {
83 auto stateAndAction = storage::StateActionPair(s, a);
84
85 // Create y_(s,a) variables
86 var = manager.declareBooleanVariable("y_" + std::to_string(s) + "_" + std::to_string(a));
87 multistrategyVariables[stateAndAction] = var;
88 multistrategyVariablesToTakenMap[var] = false;
89
90 // Create beta_(s,a,t) variables
91 // Iterate over successors of s via a.
92 for (auto const& entry : this->mdp.getTransitionMatrix().getRow(this->mdp.getNondeterministicChoiceIndices()[s] + a)) {
93 if (entry.getValue() != 0) {
94 storage::StateActionTarget sat = {s, a, entry.getColumn()};
95 var = manager.declareBooleanVariable("beta_" + to_string(sat));
96 mBetaVariables[sat] = var;
97 }
98 }
99 }
100 }
101 }
102
106 void createConstraints(bool lowerBound, double boundary, storm::storage::BitVector const& relevantStates) {
107 // (4) and (7) are omitted on purpose (-- we currenty do not support controllability of actions -- )
108
109 // (1)
110 STORM_LOG_ASSERT(this->mdp.getInitialStates().getNumberOfSetBits() == 1, "No unique initial state.");
111 uint_fast64_t initialStateIndex = this->mdp.getInitialStates().getNextSetIndex(0);
112 STORM_LOG_ASSERT(relevantStates[initialStateIndex], "Initial state not relevant.");
113 if (lowerBound) {
114 solver.add(mProbVariables[initialStateIndex] >= manager.rational(boundary));
115 } else {
116 solver.add(mProbVariables[initialStateIndex] <= manager.rational(boundary));
117 }
118 for (uint_fast64_t s : relevantStates) {
119 std::vector<storm::expressions::Expression> expressions;
120 // (2)
121 for (uint_fast64_t a = 0; a < this->mdp.getNumberOfChoices(s); ++a) {
122 expressions.push_back(multistrategyVariables[storage::StateActionPair(s, a)]);
123 }
124 solver.add(storm::expressions::disjunction(expressions));
125 expressions.clear();
126
127 // (5) These constraints are only necessary for lower-bounded properties.
128 if (lowerBound) {
129 // TODO
130 // solver.addConstraint("c5-" + std::to_string(s), mProbVariables[s] <= mAlphaVariables[s]);
131 }
132
133 // (3) For the relevant states.
134 for (uint_fast64_t a = 0; a < this->mdp.getNumberOfChoices(s); ++a) {
135 for (auto const& entry : this->mdp.getTransitionMatrix().getRow(this->mdp.getNondeterministicChoiceIndices()[s] + a)) {
136 if (entry.getValue() != 0 && relevantStates.get(entry.getColumn())) {
137 expressions.push_back(manager.rational(entry.getValue()) * mProbVariables[entry.getColumn()]);
138 } else if (entry.getValue() != 0 && this->mGoals.get(entry.getColumn())) {
139 expressions.push_back(manager.rational(entry.getValue()));
140 }
141 }
142 if (lowerBound) {
143 solver.add(storm::expressions::implies(multistrategyVariables[storage::StateActionPair(s, a)],
144 mProbVariables[s] <= storm::expressions::sum(expressions)));
145 } else {
146 solver.add(storm::expressions::implies(multistrategyVariables[storage::StateActionPair(s, a)],
147 mProbVariables[s] >= storm::expressions::sum(expressions)));
148 }
149 expressions.clear();
150 }
151
152 // (6) and (8) are only necessary for lower-bounded properties.
153 if (lowerBound) {
154 // TODO
155 // for(uint_fast64_t a = 0; a < this->mdp.getNumberOfChoices(s); ++a) {
156 // // (6)
157 // std::string sastring(stateString + "_" + std::to_string(a));
158 // expr = solver.getConstant(0.0);
159 // for(auto const& entry : this->mdp.getTransitionMatrix().getRow(this->mdp.getNondeterministicChoiceIndices()[s]+a))
160 // {
161 // if(entry.getValue() != 0) {
162 // storage::StateActionTarget sat = {s,a,entry.getColumn()};
163 // expr = expr + mBetaVariables[sat];
164 // }
165 // }
166 // solver.addConstraint("c6-" + sastring, multistrategyVariables[storage::StateActionPair(s,a)] ==
167 // (solver.getConstant(1) - mAlphaVariables[s]) + expr);
168 //
169 // for(auto const& entry : this->mdp.getTransitionMatrix().getRow(this->mdp.getNondeterministicChoiceIndices()[s]+a))
170 // {
171 // if(entry.getValue() != 0) {
172 // storage::StateActionTarget sat = {s,a,entry.getColumn()};
173 // std::string satstring = to_string(sat);
174 // // (8)
175 // if(relevantStates[entry.getColumn()]) {
176 // STORM_LOG_ASSERT(mGammaVariables.count(entry.getColumn()) > 0, "Entry not found.");
177 // STORM_LOG_ASSERT(mGammaVariables.count(s) > 0, "Entry not found.");
178 // STORM_LOG_ASSERT(mBetaVariables.count(sat) > 0, "Entry not found.");
179 // solver.addConstraint("c8-" + satstring, mGammaVariables[entry.getColumn()] < mGammaVariables[s] +
180 // (solver.getConstant(1) - mBetaVariables[sat]) + mProbVariables[s]); // With rewards, we have to change
181 // this.
182 // }
183 // }
184 // }
185 // }
186 }
187 }
188 }
189
193 void performSmtLoop(bool lowerBound, double boundary, PermissiveSchedulerPenalties const& penalties) {
194 storm::storage::BitVector irrelevant = this->mGoals | this->mSinks;
195 storm::storage::BitVector relevantStates = ~irrelevant;
196 createVariables(relevantStates);
197 createConstraints(lowerBound, boundary, relevantStates);
198
199 // Find the initial solution (if possible).
200 storm::solver::SmtSolver::CheckResult result = solver.check();
201
203 // Extract the solution from the multi-strategy variables and track all state-action pairs that were
204 // not taken. Also, we assert all decided choices, so they are not altered anymore.
205 std::shared_ptr<storm::solver::SmtSolver::ModelReference> model = solver.getModel();
206
207 std::vector<storage::StateActionPair> availableStateActionPairs;
208 for (uint_fast64_t s : relevantStates) {
209 for (uint_fast64_t a = 0; a < this->mdp.getNumberOfChoices(s); ++a) {
210 auto stateAndAction = storage::StateActionPair(s, a);
211
212 auto multistrategyVariable = multistrategyVariables.at(stateAndAction);
213 if (model->getBooleanValue(multistrategyVariable)) {
214 multistrategyVariablesToTakenMap[multistrategyVariable] = true;
215 solver.add(multistrategyVariable);
216 } else {
217 availableStateActionPairs.push_back(stateAndAction);
218 }
219 }
220 }
221
222 // Now we sort the available state-action pairs in decending penalty order, so we can try taking more
223 // and more actions from the back (and discard them if not).
224 std::sort(availableStateActionPairs.begin(), availableStateActionPairs.end(),
225 [&penalties](storage::StateActionPair const& first, storage::StateActionPair const& second) {
226 return penalties.get(first) < penalties.get(second);
227 });
228
229 do {
230 auto multistrategyVariable = multistrategyVariables.at(availableStateActionPairs.back());
231
232 result = solver.checkWithAssumptions({multistrategyVariable});
233
235 model = solver.getModel();
236 if (model->getBooleanValue(multistrategyVariable)) {
237 solver.add(multistrategyVariable);
238 multistrategyVariablesToTakenMap[multistrategyVariable] = true;
239 }
240 }
241 availableStateActionPairs.pop_back();
242
243 } while (!availableStateActionPairs.empty());
244
245 mFoundSolution = true;
246 } else {
247 mFoundSolution = false;
248 }
249 }
250};
251
252} // namespace ps
253} // namespace storm
This class is responsible for managing a set of typed variables and all expressions using these varia...
This class represents a (discrete-time) Markov decision process.
Definition Mdp.h:13
storm::storage::SparseMatrix< ValueType > const & getTransitionMatrix() const
Retrieves the matrix representing the transitions of the model.
Definition Model.cpp:198
uint_fast64_t getNumberOfChoices(uint_fast64_t state) const
storm::models::sparse::Mdp< double, RM > const & mdp
PermissiveSchedulerComputation(storm::models::sparse::Mdp< double, RM > const &mdp, storm::storage::BitVector const &goalstates, storm::storage::BitVector const &sinkstates)
SubMDPPermissiveScheduler< RM > getScheduler() const override
void calculatePermissiveScheduler(bool lowerBound, double boundary) override
SmtPermissiveSchedulerComputation(storm::solver::SmtSolver &smtSolver, storm::models::sparse::Mdp< double, RM > const &mdp, storm::storage::BitVector const &goalstates, storm::storage::BitVector const &sinkstates)
void disable(uint_fast64_t choiceIndex)
An interface that captures the functionality of an SMT solver.
Definition SmtSolver.h:21
CheckResult
possible check results
Definition SmtSolver.h:24
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:16
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
Expression sum(std::vector< storm::expressions::Expression > const &expressions)
Expression disjunction(std::vector< storm::expressions::Expression > const &expressions)
Expression implies(Expression const &first, Expression const &second)