Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
feasibility.cpp
Go to the documentation of this file.
2#include <optional>
3
8
11
19
20namespace storm::pars {
21
22template<typename VT1>
23void printFeasibilityResult(bool success,
24 std::pair<VT1, typename storm::storage::ParameterRegion<storm::RationalFunction>::Valuation> const& valueValuationPair,
25 storm::utility::Stopwatch const& watch) {
26 std::stringstream valuationStr;
27 bool first = true;
28 for (auto const& v : valueValuationPair.second) {
29 if (first) {
30 first = false;
31 } else {
32 valuationStr << ", ";
33 }
34 valuationStr << v.first << "=" << v.second;
35 }
36 if (success) {
37 STORM_PRINT_AND_LOG("Result at initial state: " << valueValuationPair.first << " ( approx. "
38 << storm::utility::convertNumber<double>(valueValuationPair.first) << ") at [" << valuationStr.str()
39 << "].\n");
40 } else {
41 STORM_PRINT_AND_LOG("No satisfying result found.\n");
42 }
43 STORM_PRINT_AND_LOG("Time for model checking: " << watch << ".\n");
44}
45
46std::shared_ptr<FeasibilitySynthesisTask const> createFeasibilitySynthesisTaskFromSettings(
47 std::shared_ptr<storm::logic::Formula const> const& formula, std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> const& regions) {
48 STORM_LOG_THROW(formula->isRewardOperatorFormula() || formula->isProbabilityOperatorFormula(), storm::exceptions::NotSupportedException,
49 "We only support reward- and probability operator formulas.");
50 STORM_LOG_THROW(regions.size() <= 1, storm::exceptions::NotSupportedException, "Storm only supports one or zero regions.");
51
52 std::shared_ptr<storm::logic::Formula const> formulaNoBound;
53 if (formula->asOperatorFormula().hasBound()) {
54 std::shared_ptr<storm::logic::Formula> formulaWithoutBounds = formula->clone();
55 formulaWithoutBounds->asOperatorFormula().removeBound();
56 formulaNoBound = formulaWithoutBounds->asSharedPointer();
57 } else {
58 formulaNoBound = formula;
59 }
60
61 FeasibilitySynthesisTask t(formulaNoBound);
63
64 if (formula->asOperatorFormula().hasBound()) {
65 t.setBound(formula->asOperatorFormula().getBound());
66
67 STORM_LOG_THROW(!feasibilitySettings.isParameterDirectionSet(), storm::exceptions::NotSupportedException,
68 "With a bound, the direction for the parameters is inferred from the bound.");
69 STORM_LOG_THROW(!feasibilitySettings.hasOptimalValueGuaranteeBeenSet(), storm::exceptions::NotSupportedException,
70 "When a bound is given, the guarantee is that this bound will be satisfied by a solution.");
71 } else {
72 if (feasibilitySettings.hasOptimalValueGuaranteeBeenSet()) {
73 t.setMaximalAllowedGap(storm::utility::convertNumber<storm::RationalNumber>(feasibilitySettings.getOptimalValueGuarantee()));
74 t.setMaximalAllowedGapIsRelative(!feasibilitySettings.isAbsolutePrecisionSet());
75 }
76 STORM_LOG_THROW(feasibilitySettings.isParameterDirectionSet(), storm::exceptions::NotSupportedException,
77 "Without a bound, the direction for the parameters must be explicitly given.");
78 t.setOptimizationDirection(feasibilitySettings.getParameterDirection());
79 }
80 if (regions.size() == 1) {
81 t.setRegion(regions.front());
82 }
83 return std::make_shared<FeasibilitySynthesisTask const>(std::move(t));
84}
85
86template<typename ValueType>
88 std::shared_ptr<storm::pars::FeasibilitySynthesisTask const> const& task,
89 boost::optional<std::set<RationalFunctionVariable>> omittedParameters, storm::api::MonotonicitySetting monotonicitySettings) {
91
92 STORM_PRINT_AND_LOG("Find feasible solution for " << task->getFormula());
93 if (task->isRegionSet()) {
94 STORM_PRINT_AND_LOG(" within region " << task->getRegion());
95 }
96 if (monotonicitySettings.useMonotonicity) {
97 STORM_PRINT_AND_LOG(" and using monotonicity ...");
98 }
100
101 if (feasibilitySettings.getFeasibilityMethod() == storm::pars::FeasibilityMethod::GD) {
102 runFeasibilityWithGD(env, model, task, omittedParameters, monotonicitySettings);
103 } else if (feasibilitySettings.getFeasibilityMethod() == storm::pars::FeasibilityMethod::PLA) {
104 runFeasibilityWithPLA(env, model, task, omittedParameters, monotonicitySettings);
105 } else {
106 STORM_LOG_ASSERT(feasibilitySettings.getFeasibilityMethod() == storm::pars::FeasibilityMethod::SCP, "Remaining method must be SCP.");
107 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "SCP is not yet implemented.");
108 }
109}
110
111template<typename ValueType>
113 std::shared_ptr<storm::pars::FeasibilitySynthesisTask const> const& task,
114 boost::optional<std::set<RationalFunctionVariable>> omittedParameters, storm::api::MonotonicitySetting monotonicitySettings) {
116
117 STORM_LOG_THROW(model->isOfType(storm::models::ModelType::Dtmc), storm::exceptions::NotSupportedException,
118 "Gradient descent is currently only supported for DTMCs.");
119 std::shared_ptr<storm::models::sparse::Dtmc<ValueType>> dtmc = model->template as<storm::models::sparse::Dtmc<ValueType>>();
120 STORM_LOG_THROW(task->getFormula().isProbabilityOperatorFormula() || task->getFormula().isRewardOperatorFormula(), storm::exceptions::NotSupportedException,
121 "Input formula needs to be either a probability operator formula or a reward operator formula.");
122 STORM_LOG_THROW(task->isBoundSet(), storm::exceptions::NotImplementedException, "GD (right now) requires an explicitly given bound.");
123 STORM_LOG_THROW(task->getMaximalAllowedGap() == std::nullopt, storm::exceptions::NotSupportedException,
124 "GD cannot provide guarantees on the optimality of the solution..");
125
126 if (omittedParameters && !omittedParameters->empty()) {
127 // TODO get rid of std::cout here
128 std::cout << "Parameters ";
129 for (auto const& entry : *omittedParameters) {
130 std::cout << entry << " ";
131 }
132 std::cout << "are inconsequential.";
133 if (derSettings.areInconsequentialParametersOmitted()) {
134 std::cout << " They will be omitted in the found instantiation.\n";
135 } else {
136 std::cout << " They will be set to 0.5 in the found instantiation. To omit them, set the flag --omit-inconsequential-params.\n";
137 }
138 }
139
140 boost::optional<derivative::GradientDescentConstraintMethod> constraintMethod = derSettings.getConstraintMethod();
141 if (!constraintMethod) {
142 STORM_LOG_ERROR("Unknown Gradient Descent Constraint method: " << derSettings.getConstraintMethodAsString());
143 return;
144 }
145
146 boost::optional<derivative::GradientDescentMethod> method = derSettings.getGradientDescentMethod();
147 if (!method) {
148 STORM_LOG_ERROR("Unknown Gradient Descent method: " << derSettings.getGradientDescentMethodAsString());
149 return;
150 }
151
152 std::optional<std::map<typename utility::parametric::VariableType<ValueType>::type, typename utility::parametric::CoefficientType<ValueType>::type>>
153 startPoint;
154
155 std::optional<storage::ParameterRegion<storm::RationalFunction>> region;
156 if (task->isRegionSet()) {
157 region = task->getRegion();
158 // Gradient descent requires a graph-preserving region.
159 establishGraphPreservation<ValueType>(model, {*region}, true);
160 }
161
162 STORM_PRINT("Finding an extremum using Gradient Descent\n");
163 storm::utility::Stopwatch derivativeWatch(true);
165 env, *dtmc, *method, derSettings.getLearningRate(), derSettings.getAverageDecay(), derSettings.getSquaredAverageDecay(), derSettings.getMiniBatchSize(),
166 derSettings.getTerminationEpsilon(), startPoint, *constraintMethod, region, derSettings.isPrintJsonSet());
167
168 gdsearch.setup(env, task);
169 auto instantiationAndValue = gdsearch.gradientDescent();
170 // TODO check what happens if no feasible solution is found
171 if (!derSettings.areInconsequentialParametersOmitted() && omittedParameters) {
172 for (RationalFunctionVariable const& param : *omittedParameters) {
173 if (startPoint) {
174 instantiationAndValue.first[param] = startPoint->at(param);
175 } else {
176 instantiationAndValue.first[param] = utility::convertNumber<RationalFunction::CoeffType>(0.5);
177 }
178 }
179 }
180 derivativeWatch.stop();
181
182 // TODO refactor this such that the order is globally fixed.
183 std::pair<double, typename storm::storage::ParameterRegion<ValueType>::Valuation> valueValuationPair;
184 valueValuationPair.first = instantiationAndValue.second;
185 valueValuationPair.second = instantiationAndValue.first;
186
187 if (derSettings.isPrintJsonSet()) {
188 gdsearch.printRunAsJson();
189 }
190
191 if (task->isBoundSet()) {
192 printFeasibilityResult(task->getBound().isSatisfied(valueValuationPair.first), valueValuationPair, derivativeWatch);
193 } else {
194 printFeasibilityResult(true, valueValuationPair, derivativeWatch);
195 }
196}
197
198template<typename ValueType>
200 std::shared_ptr<storm::pars::FeasibilitySynthesisTask const> const& task,
201 boost::optional<std::set<RationalFunctionVariable>> omittedParameters, storm::api::MonotonicitySetting monotonicitySettings) {
202 STORM_LOG_THROW(task->isRegionSet(), storm::exceptions::NotSupportedException, "PLA requires an explicitly given region.");
203 storm::solver::OptimizationDirection direction = task->getOptimizationDirection();
204
205 // TODO handle omittedParameterss
207 auto engine = regionVerificationSettings.getRegionCheckEngine();
208
209 auto const graphPreservationStatus =
211
212 auto regionSplittingStrategy = storm::modelchecker::RegionSplittingStrategy();
213
214 regionSplittingStrategy.heuristic = regionVerificationSettings.getRegionSplittingHeuristic();
215 regionSplittingStrategy.estimateKind = regionVerificationSettings.getRegionSplittingEstimateMethod();
216 if (regionVerificationSettings.isSplittingThresholdSet()) {
217 regionSplittingStrategy.maxSplitDimensions = regionVerificationSettings.getSplittingThreshold();
218 }
219
220 if (task->isBoundSet()) {
221 storm::utility::Stopwatch watch(true);
223 storm::api::createTask<ValueType>(task->getFormula().asSharedPointer(), true),
224 engine,
225 regionSplittingStrategy,
227 {},
228 true,
229 graphPreservationStatus};
230 auto valueValuation = storm::api::computeExtremalValue<ValueType>(env, settings, task->getRegion(), direction, storm::utility::zero<ValueType>(),
231 !task->isMaxGapRelative(), task->getBound().getInvertedBound());
232 watch.stop();
233
234 printFeasibilityResult(task->getBound().isSatisfied(valueValuation.first), valueValuation, watch);
235 } else {
236 STORM_LOG_THROW(task->getMaximalAllowedGap() != std::nullopt, storm::exceptions::NotSupportedException,
237 "Without a bound, PLA requires an explicit target in form of a guarantee.");
238
239 ValueType precision = storm::utility::convertNumber<ValueType>(task->getMaximalAllowedGap().value());
240 storm::utility::Stopwatch watch(true);
242 storm::api::createTask<ValueType>(task->getFormula().asSharedPointer(), true),
243 engine,
244 regionSplittingStrategy,
246 {},
247 true,
248 graphPreservationStatus};
249 auto valueValuation = storm::api::computeExtremalValue<ValueType>(env, settings, task->getRegion(), direction, storm::utility::zero<ValueType>(),
250 !task->isMaxGapRelative(), std::nullopt);
251 watch.stop();
252
253 printFeasibilityResult(true, valueValuation, watch);
254 }
255}
256
258 std::shared_ptr<storm::pars::FeasibilitySynthesisTask const> const& task,
259 boost::optional<std::set<RationalFunctionVariable>> omittedParameters, storm::api::MonotonicitySetting monotonicitySettings);
260
262 std::shared_ptr<storm::pars::FeasibilitySynthesisTask const> const& task,
263 boost::optional<std::set<RationalFunctionVariable>> omittedParameters, storm::api::MonotonicitySetting monotonicitySettings);
264
266 std::shared_ptr<storm::pars::FeasibilitySynthesisTask const> const& task,
267 boost::optional<std::set<RationalFunctionVariable>> omittedParameters,
268 storm::api::MonotonicitySetting monotonicitySettings);
269
270} // namespace storm::pars
std::pair< std::map< typename utility::parametric::VariableType< FunctionType >::type, typename utility::parametric::CoefficientType< FunctionType >::type >, ExtendedConstantType > gradientDescent()
Perform Gradient Descent.
void setup(Environment const &env, std::shared_ptr< storm::pars::FeasibilitySynthesisTask const > const &task)
This will setup the matrices used for computing the derivatives by constructing the SparseDerivativeI...
Base class for all sparse models.
Definition Model.h:34
void setOptimizationDirection(storm::solver::OptimizationDirection const &dir)
void setMaximalAllowedGap(storm::RationalNumber const &maxGap)
void setBound(storm::logic::Bound const &bound)
void setRegion(storm::storage::ParameterRegion< storm::RationalFunction > const &region)
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_ERROR(message)
Definition logging.h:36
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:39
storm::modelchecker::CheckTask< storm::logic::Formula, ValueType > createTask(std::shared_ptr< const storm::logic::Formula > const &formula, bool onlyInitialStatesRelevant=false)
std::pair< storm::ExtendedRationalNumber, typename storm::storage::ParameterRegion< ValueType >::Valuation > computeExtremalValue(Environment const &env, RefinementOptions< ValueType > settings, storm::storage::ParameterRegion< ValueType > const &region, storm::solver::OptimizationDirection const &dir, std::optional< ValueType > const &precision, bool absolutePrecision, std::optional< storm::logic::Bound > const &boundInvariant)
Finds the extremal value in the given region.
Definition region.h:333
storm::pars::modelchecker::MonotonicityOptions MonotonicitySetting
Definition region.h:44
storm::pars::modelchecker::RegionRefinementOptions< ValueType > RefinementOptions
Definition region.h:47
@ RobustParameterLifting
Parameter lifting approach based on robust markov models instead of generating nondeterminism.
storm::pars::analysis::GraphPreservationStatus establishGraphPreservation(std::shared_ptr< storm::models::ModelBase > const &model, std::vector< storm::storage::ParameterRegion< ValueType > > const &regions, bool graphPreservationRequired)
Determines to what extent the regions are known to be well-defined and graph-preserving.
void runFeasibilityWithGD(storm::Environment const &env, std::shared_ptr< storm::models::sparse::Model< ValueType > > model, std::shared_ptr< storm::pars::FeasibilitySynthesisTask const > const &task, boost::optional< std::set< RationalFunctionVariable > > omittedParameters, storm::api::MonotonicitySetting monotonicitySettings)
void printFeasibilityResult(bool success, std::pair< VT1, typename storm::storage::ParameterRegion< storm::RationalFunction >::Valuation > const &valueValuationPair, storm::utility::Stopwatch const &watch)
void performFeasibility(storm::Environment const &env, std::shared_ptr< storm::models::sparse::Model< ValueType > > model, std::shared_ptr< storm::pars::FeasibilitySynthesisTask const > const &task, boost::optional< std::set< RationalFunctionVariable > > omittedParameters, storm::api::MonotonicitySetting monotonicitySettings)
void runFeasibilityWithPLA(storm::Environment const &env, std::shared_ptr< storm::models::sparse::Model< ValueType > > const &model, std::shared_ptr< storm::pars::FeasibilitySynthesisTask const > const &task, boost::optional< std::set< RationalFunctionVariable > > omittedParameters, storm::api::MonotonicitySetting monotonicitySettings)
std::shared_ptr< FeasibilitySynthesisTask const > createFeasibilitySynthesisTaskFromSettings(std::shared_ptr< storm::logic::Formula const > const &formula, std::vector< storm::storage::ParameterRegion< storm::RationalFunction > > const &regions)
SettingsType const & getModule()
Get module.
ValueType zero()
Definition constants.cpp:26
TargetType convertNumber(SourceType const &number)
carl::Variable RationalFunctionVariable
#define STORM_PRINT_AND_LOG(message)
Definition print.h:20
#define STORM_PRINT(message)
Define the macros that print information to stdout and optionally also log it.
Definition print.h:14