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