20template<
typename ValueType,
typename ConstantType>
22 std::vector<std::shared_ptr<logic::Formula const>> formulas,
24 double const& precision,
bool dotOutput)
25 : env(env), assumptionMaker(model->getTransitionMatrix()) {
29 this->formulas = formulas;
31 this->matrix = model->getTransitionMatrix();
32 this->dotOutput = dotOutput;
34 if (regions.size() == 1) {
35 this->region = *(regions.begin());
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));
50 if (numberOfSamples > 2) {
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");
65 this->extender = std::make_unique<analysis::OrderExtender<ValueType, ConstantType>>(model, formulas[0]);
67 for (uint_fast64_t i = 0; i < matrix.getRowCount(); ++i) {
68 std::set<VariableType> occurringVariables;
70 for (
auto& entry : matrix.getRow(i)) {
73 for (
auto& var : occurringVariables) {
74 occuringStatesAtVariable[var].push_back(i);
80template<
typename ValueType,
typename ConstantType>
81std::vector<typename MonotonicityHelper<ValueType, ConstantType>::ExpectedMonotonicityResult>
85 this->extender->initializeMinMaxValues(env, region);
92 for (
auto itr : monResults) {
93 if (itr.first !=
nullptr) {
94 std::cout <<
"Number of done states: " << itr.first->getNumberOfDoneStates() <<
'\n';
97 for (
auto& entry : resultCheckOnSamples.getMonotonicityResult()) {
98 if (entry.second == Monotonicity::Not) {
99 itr.second.first->updateMonotonicityResult(entry.first, entry.second,
true);
103 std::string temp = itr.second.first->toString();
105 for (
auto& assumption : itr.second.second) {
109 outfile <<
"Assumptions: \n"
113 outfile << assumption;
118 outfile <<
"No Assumptions\n";
120 outfile <<
"Monotonicity Result: \n"
121 <<
" " << temp <<
"\n\n";
124 if (monResults.size() == 0) {
125 outfile <<
"No monotonicity found, as the order is insufficient\n";
127 outfile <<
"Monotonicity Result on samples: " << resultCheckOnSamples.toString() <<
'\n';
133 STORM_LOG_WARN_COND(monResults.size() <= 10,
"Too many Reachability Orders. Dot Output will only be created for 10.");
135 auto orderItr = monResults.begin();
136 while (i < 10 && orderItr != monResults.end()) {
137 std::ofstream dotOutfile;
138 std::string name = dotOutfileName + std::to_string(i);
140 dotOutfile <<
"Assumptions:\n";
141 auto assumptionItr = orderItr->second.second.begin();
142 while (assumptionItr != orderItr->second.second.end()) {
143 dotOutfile << *assumptionItr <<
'\n';
148 orderItr->first->dotOutputToFile(dotOutfile);
154 std::vector<ExpectedMonotonicityResult> result;
155 result.reserve(monResults.size());
156 for (
auto const& [order, entry] : monResults) {
157 result.push_back({order, entry.first, entry.second});
162template<
typename ValueType,
typename ConstantType>
163std::shared_ptr<LocalMonotonicityResult<typename MonotonicityHelper<ValueType, ConstantType>::VariableType>>
166 for (uint_fast64_t state = 0; state < model->getNumberOfStates(); ++state) {
167 for (
auto& var : extender->getVariablesOccuringAtState()[state]) {
168 localMonRes.
setMonotonicity(state, var, extender->getMonotoncityChecker().checkLocalMonotonicity(order, state, var, region));
171 localMonRes.
setDone(order->getDoneBuilding());
172 return std::make_shared<LocalMonotonicityResult<VariableType>>(localMonRes);
176template<
typename ValueType,
typename ConstantType>
177void MonotonicityHelper<ValueType, ConstantType>::createOrder() {
179 std::tuple<std::shared_ptr<Order>, uint_fast64_t, uint_fast64_t> criticalTuple;
183 criticalTuple = extender->toOrder(region, monRes);
184 auto order = std::get<0>(criticalTuple);
185 if (order->isInvalid()) {
189 STORM_LOG_INFO(
" The initial order is invalid, so no monotonicity results are produced");
193 auto val1 = std::get<1>(criticalTuple);
194 auto val2 = std::get<2>(criticalTuple);
195 auto numberOfStates = model->getNumberOfStates();
196 std::vector<Assumption> assumptions;
198 if (val1 == numberOfStates && val2 == numberOfStates) {
199 monResults.insert({order, {monRes, assumptions}});
200 }
else if (val1 != numberOfStates && val2 != numberOfStates) {
201 extendOrderWithAssumptions(order, val1, val2, assumptions, monRes);
207template<
typename ValueType,
typename ConstantType>
208void MonotonicityHelper<ValueType, ConstantType>::extendOrderWithAssumptions(std::shared_ptr<Order> order, uint_fast64_t val1, uint_fast64_t val2,
209 std::vector<Assumption> assumptions,
215 struct PendingBranch {
216 std::shared_ptr<Order> order;
219 std::vector<Assumption> assumptions;
220 std::shared_ptr<MonotonicityResult<VariableType>> monRes;
222 std::vector<PendingBranch> worklist;
223 worklist.push_back({std::move(order), val1, val2, std::move(assumptions), std::move(monRes)});
225 auto const numberOfStates = model->getNumberOfStates();
226 while (!worklist.empty()) {
227 PendingBranch current = std::move(worklist.back());
230 if (current.order->isInvalid()) {
235 if (current.val1 == numberOfStates || current.val2 == numberOfStates) {
236 STORM_LOG_ASSERT(current.val1 == current.val2,
"The order is done, so both states must be unset.");
237 STORM_LOG_ASSERT(current.order->getNumberOfAddedStates() == current.order->getNumberOfStates(),
"All states must have been added.");
238 monResults.insert({current.order, {current.monRes, current.assumptions}});
243 STORM_LOG_INFO(
"Creating assumptions for " << current.val1 <<
" and " << current.val2 <<
". ");
244 auto newAssumptions = assumptionMaker.createAndCheckAssumptions(current.val1, current.val2, current.order, region);
245 STORM_LOG_ASSERT(newAssumptions.size() <= 3,
"At most three candidate assumptions can be created.");
247 if (newAssumptions.empty()) {
249 for (
auto& entry : occuringStatesAtVariable) {
250 for (
auto& state : entry.second) {
251 extender->checkParOnStateMonRes(state, current.order, entry.first, fallbackMonRes);
252 if (fallbackMonRes->getMonotonicity(entry.first) == Monotonicity::Unknown) {
256 fallbackMonRes->setDoneForVar(entry.first);
258 monResults.insert({current.order, {fallbackMonRes, current.assumptions}});
259 STORM_LOG_INFO(
" None of the assumptions were valid, we stop exploring the current order");
262 STORM_LOG_INFO(
" Created " << newAssumptions.size() <<
" assumptions, we continue extending the current order");
268 std::vector<PendingBranch> preparedBranches;
269 preparedBranches.reserve(newAssumptions.size());
270 for (
size_t i = 0;
i < newAssumptions.size(); ++
i) {
271 auto const& assumption = newAssumptions[
i];
272 bool const isLastCandidate = (
i == newAssumptions.size() - 1);
274 std::shared_ptr<Order> branchOrder;
275 if (isLastCandidate) {
276 branchOrder = current.order;
278 branchOrder = current.order->copy();
282 extender->copyContext(current.order, branchOrder);
284 std::shared_ptr<MonotonicityResult<VariableType>> branchMonRes = isLastCandidate ? current.monRes : current.monRes->copy();
285 std::vector<Assumption> branchAssumptions = current.assumptions;
289 branchAssumptions.push_back(assumption.first);
291 auto criticalTuple = extender->extendOrder(branchOrder, region, branchMonRes, assumption.first);
292 preparedBranches.push_back(
293 {std::get<0>(criticalTuple), std::get<1>(criticalTuple), std::get<2>(criticalTuple), std::move(branchAssumptions), branchMonRes});
297 for (
auto it = preparedBranches.rbegin(); it != preparedBranches.rend(); ++it) {
298 worklist.push_back(std::move(*it));
303template<
typename ValueType,
typename ConstantType>
304void MonotonicityHelper<ValueType, ConstantType>::checkMonotonicityOnSamples(Environment
const& env, std::shared_ptr<models::sparse::Dtmc<ValueType>> model,
305 uint_fast64_t numberOfSamples) {
308 auto instantiator = utility::ModelInstantiator<models::sparse::Dtmc<ValueType>, models::sparse::Dtmc<ConstantType>>(*model);
310 std::vector<std::vector<ConstantType>> samples;
312 for (
auto itr = variables.begin(); itr != variables.end(); ++itr) {
313 ConstantType previous = -1;
319 for (uint_fast64_t i = 0; (monDecr || monIncr) &&
i < numberOfSamples; ++
i) {
322 for (
auto itr2 = variables.begin(); itr2 != variables.end(); ++itr2) {
324 if ((*itr) == (*itr2)) {
325 auto lb = region.getLowerBoundary(itr->name());
326 auto ub = region.getUpperBoundary(itr->name());
330 auto lb = region.getLowerBoundary(itr2->name());
331 valuation[*itr2] = utility::convertNumber<typename utility::parametric::CoefficientType<ValueType>::type>(lb);
336 models::sparse::Dtmc<ConstantType> sampleModel = instantiator.instantiate(valuation);
337 auto checker = modelchecker::SparseDtmcPrctlModelChecker<models::sparse::Dtmc<ConstantType>>(sampleModel);
338 std::unique_ptr<modelchecker::CheckResult> checkResult;
339 auto formula = formulas[0];
340 if (formula->isProbabilityOperatorFormula() && formula->asProbabilityOperatorFormula().getSubformula().isUntilFormula()) {
341 const modelchecker::CheckTask<logic::UntilFormula, ConstantType> checkTask =
342 modelchecker::CheckTask<logic::UntilFormula, ConstantType>(formula->asProbabilityOperatorFormula().getSubformula().asUntilFormula());
343 checkResult = checker.computeUntilProbabilities(env, checkTask);
344 }
else if (formula->isProbabilityOperatorFormula() && formula->asProbabilityOperatorFormula().getSubformula().isEventuallyFormula()) {
345 const modelchecker::CheckTask<logic::EventuallyFormula, ConstantType> checkTask =
346 modelchecker::CheckTask<logic::EventuallyFormula, ConstantType>(
347 formula->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula());
348 checkResult = checker.computeReachabilityProbabilities(env, checkTask);
353 auto const& quantitativeResult = checkResult->asExplicitQuantitativeCheckResult<ConstantType>();
354 std::vector<ConstantType> values = quantitativeResult.getFiniteValueVector();
355 auto initialStates = model->getInitialStates();
356 ConstantType initial = 0;
358 for (
auto j = initialStates.getNextSetIndex(0); j < model->getNumberOfStates(); j = initialStates.getNextSetIndex(j + 1)) {
359 initial += values[j];
362 STORM_LOG_ASSERT(initial >= 0 - precision && initial <= 1 + precision,
"Initial value out of [0,1] range.");
363 ConstantType diff = previous - initial;
364 STORM_LOG_ASSERT(previous == -1 || (diff >= -1 - precision && diff <= 1 + precision),
"Diff out of [-1,1] range.");
366 if (previous != -1 && (diff > precision || diff < -precision)) {
367 monDecr &= diff > precision;
368 monIncr &= diff < -precision;
371 samples.push_back(std::move(values));
374 resultCheckOnSamples.addMonotonicityResult(*itr, res);
376 assumptionMaker.setSampleValues(std::move(samples));
379template<
typename ValueType,
typename ConstantType>
380void MonotonicityHelper<ValueType, ConstantType>::checkMonotonicityOnSamples(Environment
const&, std::shared_ptr<models::sparse::Mdp<ValueType>> model,
381 uint_fast64_t numberOfSamples) {
void setDone(bool done=true)
void setMonotonicity(uint_fast64_t state, VariableType var, Monotonicity mon)
Sets the local Monotonicity of a parameter at a given state.
MonotonicityHelper(Environment const &env, 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.
std::vector< ExpectedMonotonicityResult > 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...
This class represents a discrete-time Markov chain.
This class represents a (discrete-time) Markov decision process.
Base class for all sparse models.
storm::utility::parametric::CoefficientType< ParametricType >::type CoefficientType
storm::utility::parametric::Valuation< ParametricType > Valuation
A class that provides convenience operations to display run times.
void stop()
Stop stopwatch and add measured time to total time.
#define STORM_LOG_INFO(message)
#define STORM_LOG_WARN(message)
#define STORM_LOG_STATISTICS(message)
#define STORM_LOG_THROW_UNCONDITIONALLY(exception, message)
#define STORM_LOG_ASSERT(cond, message)
#define STORM_LOG_WARN_COND(cond, message)
@ Unknown
the monotonicity result is unknown
typename utility::parametric::VariableType< FunctionType >::type VariableType
void closeFile(std::ofstream &stream)
Close the given file after writing.
void openFile(std::string const &filepath, std::ofstream &filestream, bool append=false, bool silent=false)
Open the given file for writing.
std::set< storm::RationalFunctionVariable > getProbabilityParameters(Model< storm::RationalFunction > const &model)
Get all probability parameters occurring on transitions.
std::map< typename VariableType< FunctionType >::type, typename CoefficientType< FunctionType >::type > Valuation
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)