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