Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
MonotonicityHelper.cpp
Go to the documentation of this file.
2
5
10
13
16
17namespace storm {
18namespace analysis {
19/*** Constructor ***/
20template<typename ValueType, typename ConstantType>
22 std::vector<std::shared_ptr<logic::Formula const>> formulas,
23 std::vector<storage::ParameterRegion<ValueType>> regions, uint_fast64_t numberOfSamples,
24 double const& precision, bool dotOutput)
25 : assumptionMaker(model->getTransitionMatrix()) {
26 STORM_LOG_ASSERT(model != nullptr, "Model should not be null.");
27
28 this->model = model;
29 this->formulas = formulas;
30 this->precision = utility::convertNumber<ConstantType>(precision);
31 this->matrix = model->getTransitionMatrix();
32 this->dotOutput = dotOutput;
33
34 if (regions.size() == 1) {
35 this->region = *(regions.begin());
36 } else {
39 std::set<VariableType> vars;
41 for (auto var : vars) {
44 lowerBoundaries.insert(std::make_pair(var, lb));
45 upperBoundaries.insert(std::make_pair(var, ub));
46 }
47 this->region = storage::ParameterRegion<ValueType>(std::move(lowerBoundaries), std::move(upperBoundaries));
48 }
49
50 if (numberOfSamples > 2) {
51 // sampling
52 if (model->isOfType(models::ModelType::Dtmc)) {
53 checkMonotonicityOnSamples(model->template as<models::sparse::Dtmc<ValueType>>(), numberOfSamples);
54 } else if (model->isOfType(models::ModelType::Mdp)) {
55 checkMonotonicityOnSamples(model->template as<models::sparse::Mdp<ValueType>>(), numberOfSamples);
56 }
57 checkSamples = true;
58 } else {
59 if (numberOfSamples > 0) {
60 STORM_LOG_WARN("At least 3 sample points are needed to check for monotonicity on samples, not using samples for now");
61 }
62 checkSamples = false;
63 }
64
65 this->extender = new analysis::OrderExtender<ValueType, ConstantType>(model, formulas[0]);
66
67 for (uint_fast64_t i = 0; i < matrix.getRowCount(); ++i) {
68 std::set<VariableType> occurringVariables;
69
70 for (auto& entry : matrix.getRow(i)) {
71 storm::utility::parametric::gatherOccurringVariables(entry.getValue(), occurringVariables);
72 }
73 for (auto& var : occurringVariables) {
74 occuringStatesAtVariable[var].push_back(i);
75 }
76 }
77}
78
79/*** Public methods ***/
80template<typename ValueType, typename ConstantType>
81std::map<std::shared_ptr<Order>, std::pair<std::shared_ptr<MonotonicityResult<typename MonotonicityHelper<ValueType, ConstantType>::VariableType>>,
82 std::vector<std::shared_ptr<expressions::BinaryRelationExpression>>>>
83MonotonicityHelper<ValueType, ConstantType>::checkMonotonicityInBuild(std::ostream& outfile, bool usePLA, std::string dotOutfileName) {
84 if (usePLA) {
85 storm::utility::Stopwatch plaWatch(true);
86 this->extender->initializeMinMaxValues(region);
87 plaWatch.stop();
88 STORM_LOG_STATISTICS("\nTotal time for pla checking: " << plaWatch << ".\n\n");
89 }
90 createOrder();
91
92 // output of results
93 for (auto itr : monResults) {
94 if (itr.first != nullptr) {
95 std::cout << "Number of done states: " << itr.first->getNumberOfDoneStates() << '\n';
96 }
97 if (checkSamples) {
98 for (auto& entry : resultCheckOnSamples.getMonotonicityResult()) {
99 if (entry.second == Monotonicity::Not) {
100 itr.second.first->updateMonotonicityResult(entry.first, entry.second, true);
101 }
102 }
103 }
104 std::string temp = itr.second.first->toString();
105 bool first = true;
106 for (auto& assumption : itr.second.second) {
107 if (!first) {
108 outfile << " & ";
109 } else {
110 outfile << "Assumptions: \n"
111 << " ";
112 first = false;
113 }
114 outfile << *assumption;
115 }
116 if (!first) {
117 outfile << '\n';
118 } else {
119 outfile << "No Assumptions\n";
120 }
121 outfile << "Monotonicity Result: \n"
122 << " " << temp << "\n\n";
123 }
124
125 if (monResults.size() == 0) {
126 outfile << "No monotonicity found, as the order is insufficient\n";
127 if (checkSamples) {
128 outfile << "Monotonicity Result on samples: " << resultCheckOnSamples.toString() << '\n';
129 }
130 }
131
132 // dotoutput
133 if (dotOutput) {
134 STORM_LOG_WARN_COND(monResults.size() <= 10, "Too many Reachability Orders. Dot Output will only be created for 10.");
135 int i = 0;
136 auto orderItr = monResults.begin();
137 while (i < 10 && orderItr != monResults.end()) {
138 std::ofstream dotOutfile;
139 std::string name = dotOutfileName + std::to_string(i);
140 storm::io::openFile(name, dotOutfile);
141 dotOutfile << "Assumptions:\n";
142 auto assumptionItr = orderItr->second.second.begin();
143 while (assumptionItr != orderItr->second.second.end()) {
144 dotOutfile << *assumptionItr << '\n';
145 dotOutfile << '\n';
146 assumptionItr++;
147 }
148 dotOutfile << '\n';
149 orderItr->first->dotOutputToFile(dotOutfile);
150 storm::io::closeFile(dotOutfile);
151 i++;
152 orderItr++;
153 }
154 }
155 return monResults;
156}
157
158template<typename ValueType, typename ConstantType>
159std::shared_ptr<LocalMonotonicityResult<typename MonotonicityHelper<ValueType, ConstantType>::VariableType>>
161 LocalMonotonicityResult<VariableType> localMonRes(model->getNumberOfStates());
162 for (uint_fast64_t state = 0; state < model->getNumberOfStates(); ++state) {
163 for (auto& var : extender->getVariablesOccuringAtState()[state]) {
164 localMonRes.setMonotonicity(state, var, extender->getMonotoncityChecker().checkLocalMonotonicity(order, state, var, region));
165 }
166 }
167 localMonRes.setDone(order->getDoneBuilding());
168 return std::make_shared<LocalMonotonicityResult<VariableType>>(localMonRes);
169}
170
171/*** Private methods ***/
172template<typename ValueType, typename ConstantType>
173void MonotonicityHelper<ValueType, ConstantType>::createOrder() {
174 // Transform to Orders
175 std::tuple<std::shared_ptr<Order>, uint_fast64_t, uint_fast64_t> criticalTuple;
176
177 // Create initial order
178 auto monRes = std::make_shared<MonotonicityResult<VariableType>>(MonotonicityResult<VariableType>());
179 criticalTuple = extender->toOrder(region, monRes);
180 // Continue based on not (yet) sorted states
181 std::map<std::shared_ptr<Order>, std::vector<std::shared_ptr<expressions::BinaryRelationExpression>>> result;
182
183 auto val1 = std::get<1>(criticalTuple);
184 auto val2 = std::get<2>(criticalTuple);
185 auto numberOfStates = model->getNumberOfStates();
186 std::vector<std::shared_ptr<expressions::BinaryRelationExpression>> assumptions;
187
188 if (val1 == numberOfStates && val2 == numberOfStates) {
189 auto resAssumptionPair =
190 std::pair<std::shared_ptr<MonotonicityResult<VariableType>>, std::vector<std::shared_ptr<expressions::BinaryRelationExpression>>>(monRes,
191 assumptions);
192 monResults.insert(
193 std::pair<std::shared_ptr<Order>,
194 std::pair<std::shared_ptr<MonotonicityResult<VariableType>>, std::vector<std::shared_ptr<expressions::BinaryRelationExpression>>>>(
195 std::get<0>(criticalTuple), resAssumptionPair));
196 } else if (val1 != numberOfStates && val2 != numberOfStates) {
197 extendOrderWithAssumptions(std::get<0>(criticalTuple), val1, val2, assumptions, monRes);
198 } else {
199 STORM_LOG_ASSERT(false, "Unreachable code reached.");
200 }
201}
202
203template<typename ValueType, typename ConstantType>
204void MonotonicityHelper<ValueType, ConstantType>::extendOrderWithAssumptions(std::shared_ptr<Order> order, uint_fast64_t val1, uint_fast64_t val2,
205 std::vector<std::shared_ptr<expressions::BinaryRelationExpression>> assumptions,
206 std::shared_ptr<MonotonicityResult<VariableType>> monRes) {
207 std::map<std::shared_ptr<Order>, std::vector<std::shared_ptr<expressions::BinaryRelationExpression>>> result;
208 if (order->isInvalid()) {
209 // We don't add anything as the order we created with assumptions turns out to be invalid
210 STORM_LOG_INFO(" The order was invalid, so we stop here");
211 return;
212 }
213 auto numberOfStates = model->getNumberOfStates();
214 if (val1 == numberOfStates || val2 == numberOfStates) {
215 STORM_LOG_ASSERT(val1 == val2, "Values should be equal when reaching numberOfStates.");
216 STORM_LOG_ASSERT(order->getNumberOfAddedStates() == order->getNumberOfStates(), "Added states count mismatch.");
217 auto resAssumptionPair =
218 std::pair<std::shared_ptr<MonotonicityResult<VariableType>>, std::vector<std::shared_ptr<expressions::BinaryRelationExpression>>>(monRes,
219 assumptions);
220 monResults.insert(
221 std::pair<std::shared_ptr<Order>,
222 std::pair<std::shared_ptr<MonotonicityResult<VariableType>>, std::vector<std::shared_ptr<expressions::BinaryRelationExpression>>>>(
223 std::move(order), std::move(resAssumptionPair)));
224 } else {
225 // Make the three assumptions
226 STORM_LOG_INFO("Creating assumptions for " << val1 << " and " << val2 << ". ");
227 auto newAssumptions = assumptionMaker.createAndCheckAssumptions(val1, val2, order, region);
228 STORM_LOG_ASSERT(newAssumptions.size() <= 3, "Expected at most 3 assumptions.");
229 auto itr = newAssumptions.begin();
230 if (newAssumptions.size() == 0) {
231 monRes = std::make_shared<MonotonicityResult<VariableType>>(MonotonicityResult<VariableType>());
232 for (auto& entry : occuringStatesAtVariable) {
233 for (auto& state : entry.second) {
234 extender->checkParOnStateMonRes(state, order, entry.first, monRes);
235 if (monRes->getMonotonicity(entry.first) == Monotonicity::Unknown) {
236 break;
237 }
238 }
239 monRes->setDoneForVar(entry.first);
240 }
241 monResults.insert({order, {monRes, assumptions}});
242 STORM_LOG_INFO(" None of the assumptions were valid, we stop exploring the current order");
243 } else {
244 STORM_LOG_INFO(" Created " << newAssumptions.size() << " assumptions, we continue extending the current order");
245 }
246
247 while (itr != newAssumptions.end()) {
248 auto assumption = *itr;
249 ++itr;
250 if (assumption.second != AssumptionStatus::INVALID) {
251 if (itr != newAssumptions.end()) {
252 // We make a copy of the order and the assumptions
253 auto orderCopy = order->copy();
254 auto assumptionsCopy = std::vector<std::shared_ptr<expressions::BinaryRelationExpression>>(assumptions);
255 auto monResCopy = monRes->copy();
256
257 if (assumption.second == AssumptionStatus::UNKNOWN) {
258 // only add assumption to the set of assumptions if it is unknown whether it holds or not
259 assumptionsCopy.push_back(std::move(assumption.first));
260 }
261 auto criticalTuple = extender->extendOrder(orderCopy, region, monResCopy, assumption.first);
262 extendOrderWithAssumptions(std::get<0>(criticalTuple), std::get<1>(criticalTuple), std::get<2>(criticalTuple), assumptionsCopy, monResCopy);
263 } else {
264 // It is the last one, so we don't need to create a copy.
265 if (assumption.second == AssumptionStatus::UNKNOWN) {
266 // only add assumption to the set of assumptions if it is unknown whether it holds or not
267 assumptions.push_back(std::move(assumption.first));
268 }
269 auto criticalTuple = extender->extendOrder(order, region, monRes, assumption.first);
270 extendOrderWithAssumptions(std::get<0>(criticalTuple), std::get<1>(criticalTuple), std::get<2>(criticalTuple), assumptions, monRes);
271 }
272 }
273 }
274 }
275}
276
277template<typename ValueType, typename ConstantType>
278void MonotonicityHelper<ValueType, ConstantType>::checkMonotonicityOnSamples(std::shared_ptr<models::sparse::Dtmc<ValueType>> model,
279 uint_fast64_t numberOfSamples) {
280 STORM_LOG_ASSERT(numberOfSamples > 2, "Expected at least 3 samples.");
281
282 auto instantiator = utility::ModelInstantiator<models::sparse::Dtmc<ValueType>, models::sparse::Dtmc<ConstantType>>(*model);
283 std::set<VariableType> variables = models::sparse::getProbabilityParameters(*model);
284 std::vector<std::vector<ConstantType>> samples;
285 // For each of the variables create a model in which we only change the value for this specific variable
286 for (auto itr = variables.begin(); itr != variables.end(); ++itr) {
287 ConstantType previous = -1;
288 bool monDecr = true;
289 bool monIncr = true;
290
291 // Check monotonicity in variable (*itr) by instantiating the model
292 // all other variables fixed on lb, only increasing (*itr)
293 for (uint_fast64_t i = 0; (monDecr || monIncr) && i < numberOfSamples; ++i) {
294 // Create valuation
296 for (auto itr2 = variables.begin(); itr2 != variables.end(); ++itr2) {
297 // Only change value for current variable
298 if ((*itr) == (*itr2)) {
299 auto lb = region.getLowerBoundary(itr->name());
300 auto ub = region.getUpperBoundary(itr->name());
301 // Creates samples between lb and ub, that is: lb, lb + (ub-lb)/(#samples -1), lb + 2* (ub-lb)/(#samples -1), ..., ub
302 valuation[*itr2] = (lb + utility::convertNumber<CoefficientType>(i / (numberOfSamples - 1)) * (ub - lb));
303 } else {
304 auto lb = region.getLowerBoundary(itr2->name());
305 valuation[*itr2] = utility::convertNumber<typename utility::parametric::CoefficientType<ValueType>::type>(lb);
306 }
307 }
308
309 // Instantiate model and get result
310 models::sparse::Dtmc<ConstantType> sampleModel = instantiator.instantiate(valuation);
311 auto checker = modelchecker::SparseDtmcPrctlModelChecker<models::sparse::Dtmc<ConstantType>>(sampleModel);
312 std::unique_ptr<modelchecker::CheckResult> checkResult;
313 auto formula = formulas[0];
314 if (formula->isProbabilityOperatorFormula() && formula->asProbabilityOperatorFormula().getSubformula().isUntilFormula()) {
315 const modelchecker::CheckTask<logic::UntilFormula, ConstantType> checkTask =
316 modelchecker::CheckTask<logic::UntilFormula, ConstantType>(formula->asProbabilityOperatorFormula().getSubformula().asUntilFormula());
317 checkResult = checker.computeUntilProbabilities(Environment(), checkTask);
318 } else if (formula->isProbabilityOperatorFormula() && formula->asProbabilityOperatorFormula().getSubformula().isEventuallyFormula()) {
319 const modelchecker::CheckTask<logic::EventuallyFormula, ConstantType> checkTask =
320 modelchecker::CheckTask<logic::EventuallyFormula, ConstantType>(
321 formula->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula());
322 checkResult = checker.computeReachabilityProbabilities(Environment(), checkTask);
323 } else {
324 STORM_LOG_THROW(false, exceptions::NotSupportedException, "Expecting until or eventually formula.");
325 }
326
327 auto quantitativeResult = checkResult->asExplicitQuantitativeCheckResult<ConstantType>();
328 std::vector<ConstantType> values = quantitativeResult.getValueVector();
329 auto initialStates = model->getInitialStates();
330 ConstantType initial = 0;
331 // Get total probability from initial states
332 for (auto j = initialStates.getNextSetIndex(0); j < model->getNumberOfStates(); j = initialStates.getNextSetIndex(j + 1)) {
333 initial += values[j];
334 }
335 // Calculate difference with result for previous valuation
336 STORM_LOG_ASSERT(initial >= 0 - precision && initial <= 1 + precision, "Initial value out of [0,1] range.");
337 ConstantType diff = previous - initial;
338 STORM_LOG_ASSERT(previous == -1 || (diff >= -1 - precision && diff <= 1 + precision), "Diff out of [-1,1] range.");
339
340 if (previous != -1 && (diff > precision || diff < -precision)) {
341 monDecr &= diff > precision; // then previous value is larger than the current value from the initial states
342 monIncr &= diff < -precision;
343 }
344 previous = initial;
345 samples.push_back(std::move(values));
346 }
348 resultCheckOnSamples.addMonotonicityResult(*itr, res);
349 }
350 assumptionMaker.setSampleValues(std::move(samples));
351}
352
353template<typename ValueType, typename ConstantType>
354void MonotonicityHelper<ValueType, ConstantType>::checkMonotonicityOnSamples(std::shared_ptr<models::sparse::Mdp<ValueType>> model,
355 uint_fast64_t numberOfSamples) {
356 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Checking monotonicity on samples not implemented for mdps.");
357}
358
361} // namespace analysis
362} // namespace storm
void setMonotonicity(uint_fast64_t state, VariableType var, Monotonicity mon)
Sets the local Monotonicity of a parameter at a given state.
std::map< std::shared_ptr< Order >, std::pair< std::shared_ptr< MonotonicityResult< VariableType > >, std::vector< std::shared_ptr< expressions::BinaryRelationExpression > > > > checkMonotonicityInBuild(std::ostream &outfile, bool usePLA=false, std::string dotOutfileName="dotOutput")
Builds Reachability Orders for the given model and simultaneously uses them to check for Monotonicity...
std::shared_ptr< LocalMonotonicityResult< VariableType > > createLocalMonotonicityResult(std::shared_ptr< Order > order, storage::ParameterRegion< ValueType > region)
Builds Reachability Orders for the given model and simultaneously uses them to check for Monotonicity...
MonotonicityHelper(std::shared_ptr< models::sparse::Model< ValueType > > model, std::vector< std::shared_ptr< logic::Formula const > > formulas, std::vector< storage::ParameterRegion< ValueType > > regions, uint_fast64_t numberOfSamples=0, double const &precision=0.000001, bool dotOutput=false)
Constructor of MonotonicityHelper.
This class represents a discrete-time Markov chain.
Definition Dtmc.h:13
This class represents a (discrete-time) Markov decision process.
Definition Mdp.h:13
Base class for all sparse models.
Definition Model.h:30
storm::utility::parametric::CoefficientType< ParametricType >::type CoefficientType
storm::utility::parametric::Valuation< ParametricType > Valuation
A class that provides convenience operations to display run times.
Definition Stopwatch.h:13
void stop()
Stop stopwatch and add measured time to total time.
Definition Stopwatch.cpp:42
#define STORM_LOG_INFO(message)
Definition logging.h:27
#define STORM_LOG_WARN(message)
Definition logging.h:28
#define STORM_LOG_STATISTICS(message)
Definition logging.h:41
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_WARN_COND(cond, message)
Definition macros.h:36
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:28
@ Unknown
the monotonicity result is unknown
typename utility::parametric::VariableType< FunctionType >::type VariableType
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::set< storm::RationalFunctionVariable > getProbabilityParameters(Model< storm::RationalFunction > const &model)
Get all probability parameters occurring on transitions.
Definition Model.cpp:694
std::map< typename VariableType< FunctionType >::type, typename CoefficientType< FunctionType >::type > Valuation
Definition parametric.h:43
void gatherOccurringVariables(FunctionType const &function, std::set< typename VariableType< FunctionType >::type > &variableSet)
Add all variables that occur in the given function to the the given set.
TargetType convertNumber(SourceType const &number)