Storm
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
MILPPermissiveSchedulers.h
Go to the documentation of this file.
1#pragma once
2
3#include <fstream>
4#include <memory>
5#include <unordered_map>
6
9#include "storm/io/file.h"
16
17namespace storm {
18namespace ps {
19
20template<typename RM>
22 private:
23 bool mCalledOptimizer = false;
25 std::unordered_map<storm::storage::StateActionPair, storm::expressions::Variable> multistrategyVariables;
26 std::unordered_map<uint_fast64_t, storm::expressions::Variable> mProbVariables;
27 std::unordered_map<uint_fast64_t, storm::expressions::Variable> mAlphaVariables;
28 std::unordered_map<storm::storage::StateActionTarget, storm::expressions::Variable> mBetaVariables;
29 std::unordered_map<uint_fast64_t, storm::expressions::Variable> mGammaVariables;
30
31 public:
33 storm::storage::BitVector const& goalstates, storm::storage::BitVector const& sinkstates)
34 : PermissiveSchedulerComputation<RM>(mdp, goalstates, sinkstates), solver(milpsolver) {}
35
36 void calculatePermissiveScheduler(bool lowerBound, double boundary) override {
37 createMILP(lowerBound, boundary, this->mPenalties);
38 // STORM_LOG_DEBUG("Calling optimizer");
39 solver.optimize();
40 // STORM_LOG_DEBUG("Done optimizing.")
41 mCalledOptimizer = true;
42 }
43
44 bool foundSolution() const override {
45 STORM_LOG_ASSERT(mCalledOptimizer, "Optimizer not called.");
46 return !solver.isInfeasible();
47 }
48
50 STORM_LOG_ASSERT(mCalledOptimizer, "Optimizer not called.");
51 STORM_LOG_ASSERT(foundSolution(), "Solution not found.");
52
53 SubMDPPermissiveScheduler<RM> result(this->mdp, true);
54 for (auto const& entry : multistrategyVariables) {
55 if (!solver.getBinaryValue(entry.second)) {
56 result.disable(this->mdp.getChoiceIndex(entry.first));
57 }
58 }
59 return result;
60 }
61
62 void dumpLpSolutionToFile(std::string const& filename) {
63 std::ofstream filestream;
64 storm::io::openFile(filename, filestream);
65 for (auto const& pVar : mProbVariables) {
66 filestream << pVar.second.getName() << "->" << solver.getContinuousValue(pVar.second) << '\n';
67 }
68 for (auto const& yVar : multistrategyVariables) {
69 filestream << yVar.second.getName() << "->" << solver.getBinaryValue(yVar.second) << '\n';
70 }
71 for (auto const& aVar : mAlphaVariables) {
72 filestream << aVar.second.getName() << "->" << solver.getBinaryValue(aVar.second) << '\n';
73 }
74 for (auto const& betaVar : mBetaVariables) {
75 filestream << betaVar.second.getName() << "->" << solver.getBinaryValue(betaVar.second) << '\n';
76 }
77 for (auto const& gammaVar : mGammaVariables) {
78 filestream << gammaVar.second.getName() << "->" << solver.getContinuousValue(gammaVar.second) << '\n';
79 }
80 storm::io::closeFile(filestream);
81 }
82
83 void dumpLpToFile(std::string const& filename) {
84 solver.writeModelToFile(filename);
85 }
86
87 private:
91 void createVariables(PermissiveSchedulerPenalties const& penalties, storm::storage::BitVector const& relevantStates) {
92 // We need the unique initial state later, so we get that one before looping.
93 STORM_LOG_ASSERT(this->mdp.getInitialStates().getNumberOfSetBits() == 1, "No unique initial state.");
94 uint_fast64_t initialStateIndex = this->mdp.getInitialStates().getNextSetIndex(0);
95
97 for (uint_fast64_t s : relevantStates) {
98 // Create x_s variables
99 // Notice that only the initial probability appears in the objective.
100 if (s == initialStateIndex) {
101 var = solver.addLowerBoundedContinuousVariable("x_" + std::to_string(s), 0.0, -1.0);
102 } else {
103 var = solver.addLowerBoundedContinuousVariable("x_" + std::to_string(s), 0.0);
104 }
105 mProbVariables[s] = var;
106 // Create alpha_s variables
107 var = solver.addBinaryVariable("alp_" + std::to_string(s));
108 mAlphaVariables[s] = var;
109 // Create gamma_s variables
110 var = solver.addBoundedContinuousVariable("gam_" + std::to_string(s), 0.0, 1.0);
111 mGammaVariables[s] = var;
112 for (uint_fast64_t a = 0; a < this->mdp.getNumberOfChoices(s); ++a) {
113 auto stateAndAction = storage::StateActionPair(s, a);
114
115 // Create y_(s,a) variables
116 double penalty = penalties.get(stateAndAction);
117 var = solver.addBinaryVariable("y_" + std::to_string(s) + "_" + std::to_string(a), -penalty);
118 multistrategyVariables[stateAndAction] = var;
119
120 // Create beta_(s,a,t) variables
121 // Iterate over successors of s via a.
122 for (auto const& entry : this->mdp.getTransitionMatrix().getRow(this->mdp.getNondeterministicChoiceIndices()[s] + a)) {
123 if (entry.getValue() != 0) {
124 storage::StateActionTarget sat = {s, a, entry.getColumn()};
125 var = solver.addBinaryVariable("beta_" + to_string(sat));
126 mBetaVariables[sat] = var;
127 }
128 }
129 }
130 }
131 solver.update();
132 }
133
137 void createConstraints(bool lowerBound, double boundary, storm::storage::BitVector const& relevantStates) {
138 // (5) and (7) are omitted on purpose (-- we currenty do not support controllability of actions -- )
139
140 // (1)
141 STORM_LOG_ASSERT(this->mdp.getInitialStates().getNumberOfSetBits() == 1, "No unique initial state.");
142 uint_fast64_t initialStateIndex = this->mdp.getInitialStates().getNextSetIndex(0);
143 STORM_LOG_ASSERT(relevantStates[initialStateIndex], "Initial state not relevant.");
144 if (lowerBound) {
145 solver.addConstraint("c1", mProbVariables[initialStateIndex] >= solver.getConstant(boundary));
146 } else {
147 solver.addConstraint("c1", mProbVariables[initialStateIndex] <= solver.getConstant(boundary));
148 }
149 for (uint_fast64_t s : relevantStates) {
150 std::string stateString = std::to_string(s);
152 // (2)
153 for (uint_fast64_t a = 0; a < this->mdp.getNumberOfChoices(s); ++a) {
154 expr = expr + multistrategyVariables[storage::StateActionPair(s, a)];
155 }
156 solver.addConstraint("c2-" + stateString, solver.getConstant(1) <= expr);
157 // (5)
158 solver.addConstraint("c5-" + std::to_string(s), mProbVariables[s] <= mAlphaVariables[s]);
159
160 // (3) For the relevant states.
161 for (uint_fast64_t a = 0; a < this->mdp.getNumberOfChoices(s); ++a) {
162 std::string sastring(stateString + "_" + std::to_string(a));
163 expr = solver.getConstant(0.0);
164 for (auto const& entry : this->mdp.getTransitionMatrix().getRow(this->mdp.getNondeterministicChoiceIndices()[s] + a)) {
165 if (entry.getValue() != 0 && relevantStates.get(entry.getColumn())) {
166 expr = expr + solver.getConstant(entry.getValue()) * mProbVariables[entry.getColumn()];
167 } else if (entry.getValue() != 0 && this->mGoals.get(entry.getColumn())) {
168 expr = expr + solver.getConstant(entry.getValue());
169 }
170 }
171 if (lowerBound) {
172 solver.addConstraint("c3-" + sastring,
173 mProbVariables[s] <= (solver.getConstant(1) - multistrategyVariables[storage::StateActionPair(s, a)]) + expr);
174 } else {
175 solver.addConstraint("c3-" + sastring,
176 mProbVariables[s] >= (solver.getConstant(1) - multistrategyVariables[storage::StateActionPair(s, a)]) + expr);
177 }
178 }
179
180 for (uint_fast64_t a = 0; a < this->mdp.getNumberOfChoices(s); ++a) {
181 // (6)
182 std::string sastring(stateString + "_" + std::to_string(a));
183 expr = solver.getConstant(0.0);
184 for (auto const& entry : this->mdp.getTransitionMatrix().getRow(this->mdp.getNondeterministicChoiceIndices()[s] + a)) {
185 if (entry.getValue() != 0) {
186 storage::StateActionTarget sat = {s, a, entry.getColumn()};
187 expr = expr + mBetaVariables[sat];
188 }
189 }
190 solver.addConstraint("c6-" + sastring,
191 multistrategyVariables[storage::StateActionPair(s, a)] == (solver.getConstant(1) - mAlphaVariables[s]) + expr);
192
193 for (auto const& entry : this->mdp.getTransitionMatrix().getRow(this->mdp.getNondeterministicChoiceIndices()[s] + a)) {
194 if (entry.getValue() != 0) {
195 storage::StateActionTarget sat = {s, a, entry.getColumn()};
196 std::string satstring = to_string(sat);
197 // (8)
198 if (relevantStates[entry.getColumn()]) {
199 STORM_LOG_ASSERT(mGammaVariables.count(entry.getColumn()) > 0, "Entry not found.");
200 STORM_LOG_ASSERT(mGammaVariables.count(s) > 0, "Entry not found.");
201 STORM_LOG_ASSERT(mBetaVariables.count(sat) > 0, "Entry not found.");
202 solver.addConstraint("c8-" + satstring,
203 mGammaVariables[entry.getColumn()] < mGammaVariables[s] + (solver.getConstant(1) - mBetaVariables[sat]));
204 }
205 }
206 }
207 }
208 }
209 }
210
214 void createMILP(bool lowerBound, double boundary, PermissiveSchedulerPenalties const& penalties) {
215 storm::storage::BitVector irrelevant = this->mGoals | this->mSinks;
216 storm::storage::BitVector relevantStates = ~irrelevant;
217 // Notice that the separated construction of variables and
218 // constraints slows down the construction of the MILP.
219 // In the future, we might want to merge this.
220 createVariables(penalties, relevantStates);
221 createConstraints(lowerBound, boundary, relevantStates);
222
223 solver.setOptimizationDirection(storm::OptimizationDirection::Minimize);
224 }
225};
226} // namespace ps
227} // namespace storm
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.
void dumpLpSolutionToFile(std::string const &filename)
MilpPermissiveSchedulerComputation(storm::solver::LpSolver< double > &milpsolver, 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
storm::models::sparse::Mdp< double, RM > const & mdp
double get(uint_fast64_t state, uint_fast64_t action) const
void disable(uint_fast64_t choiceIndex)
An interface that captures the functionality of an LP solver.
Definition LpSolver.h:51
virtual bool getBinaryValue(Variable const &variable) const =0
Retrieves the value of the binary variable with the given name.
virtual void update() const =0
Updates the model to make the variables that have been declared since the last call to update usable.
virtual void addConstraint(std::string const &name, Constraint const &constraint)=0
Adds a the given constraint to the LP problem.
virtual void writeModelToFile(std::string const &filename) const =0
Writes the current LP problem to the given file.
void setOptimizationDirection(OptimizationDirection const &optimizationDirection)
Sets whether the objective function of this model is to be minimized or maximized.
Definition LpSolver.cpp:118
Variable addLowerBoundedContinuousVariable(std::string const &name, ValueType lowerBound, ValueType objectiveFunctionCoefficient=0)
Registers a lower-bounded continuous variable, i.e.
Definition LpSolver.cpp:45
Constant getConstant(ValueType value) const
Retrieves an expression that characterizes the given constant value.
Definition LpSolver.cpp:109
virtual bool isInfeasible() const =0
Retrieves whether the model was found to be infeasible.
Variable addBoundedContinuousVariable(std::string const &name, ValueType lowerBound, ValueType upperBound, ValueType objectiveFunctionCoefficient=0)
Registers an upper- and lower-bounded continuous variable, i.e.
Definition LpSolver.cpp:38
virtual ValueType getContinuousValue(Variable const &variable) const =0
Retrieves the value of the continuous variable with the given name.
virtual void optimize() const =0
Optimizes the LP problem previously constructed.
Variable addBinaryVariable(std::string const &name, ValueType objectiveFunctionCoefficient=0)
Registers a boolean variable, i.e.
Definition LpSolver.cpp:103
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
void closeFile(std::ofstream &stream)
Close the given file after writing.
Definition file.h:47
void openFile(std::string const &filepath, std::ofstream &filestream, bool append=false, bool silent=false)
Open the given file for writing.
Definition file.h:18
std::string to_string(ModelType const &type)
Definition ModelType.cpp:10
LabParser.cpp.
Definition cli.cpp:18