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