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