Storm
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
SparsePcaaQuantitativeQuery.cpp
Go to the documentation of this file.
2
12
14
15namespace storm {
16namespace modelchecker {
17namespace multiobjective {
18
19template<class SparseModelType, typename GeometryValueType>
22 : SparsePcaaQuery<SparseModelType, GeometryValueType>(preprocessorResult) {
24 "Invalid query Type");
25
26 for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
27 if (!this->objectives[objIndex].formula->hasBound()) {
28 indexOfOptimizingObjective = objIndex;
29 break;
30 }
31 }
32 initializeThresholdData();
33
34 // Set the maximum distance between lower and upper bound of the weightVectorChecker result.
35 this->weightVectorChecker->setWeightedPrecision(storm::utility::convertNumber<typename SparseModelType::ValueType>(0.1));
36}
37
38template<class SparseModelType, typename GeometryValueType>
40 thresholds.reserve(this->objectives.size());
41 strictThresholds = storm::storage::BitVector(this->objectives.size(), false);
42 std::vector<storm::storage::geometry::Halfspace<GeometryValueType>> thresholdConstraints;
43 thresholdConstraints.reserve(this->objectives.size() - 1);
44 for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
45 auto const& formula = *this->objectives[objIndex].formula;
46
47 if (formula.hasBound()) {
48 thresholds.push_back(formula.template getThresholdAs<GeometryValueType>());
49 if (storm::solver::minimize(formula.getOptimalityType())) {
50 STORM_LOG_ASSERT(!storm::logic::isLowerBound(formula.getBound().comparisonType), "Minimizing objective should not specify an upper bound.");
51 // Values for minimizing objectives will be negated in order to convert them to maximizing objectives.
52 // Hence, we also negate the threshold
53 thresholds.back() *= -storm::utility::one<GeometryValueType>();
54 }
55 strictThresholds.set(objIndex, storm::logic::isStrict(formula.getBound().comparisonType));
56 WeightVector normalVector(this->objectives.size(), storm::utility::zero<GeometryValueType>());
57 normalVector[objIndex] = -storm::utility::one<GeometryValueType>();
58 thresholdConstraints.emplace_back(std::move(normalVector), -thresholds.back());
59 } else {
60 thresholds.push_back(storm::utility::zero<GeometryValueType>());
61 }
62 }
63 // Note: If we have a single objective (i.e., no objectives with thresholds), thresholdsAsPolytope gets no constraints
64 thresholdsAsPolytope = storm::storage::geometry::Polytope<GeometryValueType>::create(thresholdConstraints);
65}
66
67template<class SparseModelType, typename GeometryValueType>
69 // First find one solution that achieves the given thresholds ...
70 if (this->checkAchievability(env)) {
71 // ... then improve it
72 GeometryValueType result = this->improveSolution(env);
73
74 // transform the obtained result for the preprocessed model to a result w.r.t. the original model and return the checkresult
75 auto const& obj = this->objectives[indexOfOptimizingObjective];
76 if (storm::solver::maximize(obj.formula->getOptimalityType())) {
77 if (obj.considersComplementaryEvent) {
78 result = storm::utility::one<GeometryValueType>() - result;
79 }
80 } else {
81 if (obj.considersComplementaryEvent) {
82 result = storm::utility::one<GeometryValueType>() + result; // 1 - (-result)
83 } else {
84 result *= -storm::utility::one<GeometryValueType>();
85 }
86 }
87 auto resultForOriginalModel = storm::utility::convertNumber<typename SparseModelType::ValueType>(result);
88
90 this->originalModel.getInitialStates().getNextSetIndex(0), resultForOriginalModel));
91 } else {
92 return std::unique_ptr<CheckResult>(new ExplicitQualitativeCheckResult(this->originalModel.getInitialStates().getNextSetIndex(0), false));
93 }
94}
95
96template<class SparseModelType, typename GeometryValueType>
98 if (this->objectives.size() > 1) {
99 // We don't care for the optimizing objective at this point
100 this->diracWeightVectorsToBeChecked.set(indexOfOptimizingObjective, false);
101
102 while (!this->maxStepsPerformed(env) && !storm::utility::resources::isTerminate()) {
103 WeightVector separatingVector = this->findSeparatingVector(thresholds);
104 this->updateWeightedPrecisionInAchievabilityPhase(separatingVector);
105 this->performRefinementStep(env, std::move(separatingVector));
106 // Pick the threshold for the optimizing objective low enough so valid solutions are not excluded
107 thresholds[indexOfOptimizingObjective] =
108 std::min(thresholds[indexOfOptimizingObjective], this->refinementSteps.back().lowerBoundPoint[indexOfOptimizingObjective]);
109 if (!checkIfThresholdsAreSatisfied(this->overApproximation)) {
110 return false;
111 }
112 if (checkIfThresholdsAreSatisfied(this->underApproximation)) {
113 return true;
114 }
115 }
116 } else {
117 // If there is only one objective than its the optimizing one. Thus the query has to be achievable.
118 return true;
119 }
120 STORM_LOG_ERROR("Could not check whether thresholds are achievable: Termination requested or maximum number of refinement steps exceeded.");
121 return false;
122}
123
124template<class SparseModelType, typename GeometryValueType>
125void SparsePcaaQuantitativeQuery<SparseModelType, GeometryValueType>::updateWeightedPrecisionInAchievabilityPhase(WeightVector const& weights) {
126 // Our heuristic considers the distance between the under- and the over approximation w.r.t. the given direction
127 std::pair<Point, bool> optimizationResOverApprox = this->overApproximation->optimize(weights);
128 if (optimizationResOverApprox.second) {
129 std::pair<Point, bool> optimizationResUnderApprox = this->underApproximation->optimize(weights);
130 if (optimizationResUnderApprox.second) {
131 GeometryValueType distance = storm::utility::vector::dotProduct(optimizationResOverApprox.first, weights) -
132 storm::utility::vector::dotProduct(optimizationResUnderApprox.first, weights);
133 STORM_LOG_ASSERT(distance >= storm::utility::zero<GeometryValueType>(), "Negative distance between under- and over approximation was not expected");
134 // Normalize the distance by dividing it with the Euclidean Norm of the weight-vector
135 distance /= storm::utility::sqrt(storm::utility::vector::dotProduct(weights, weights));
136 distance /= GeometryValueType(2);
137 this->weightVectorChecker->setWeightedPrecision(storm::utility::convertNumber<typename SparseModelType::ValueType>(distance));
138 }
139 }
140 // do not update the precision if one of the approximations is unbounded in the provided direction
141}
142
143template<class SparseModelType, typename GeometryValueType>
144GeometryValueType SparsePcaaQuantitativeQuery<SparseModelType, GeometryValueType>::improveSolution(Environment const& env) {
145 STORM_LOG_THROW(env.modelchecker().multi().getPrecisionType() == MultiObjectiveModelCheckerEnvironment::PrecisionType::Absolute,
146 storm::exceptions::IllegalArgumentException, "Unhandled multiobjective precision type.");
147 this->diracWeightVectorsToBeChecked.clear(); // Only check weight vectors that can actually improve the solution
148
149 WeightVector directionOfOptimizingObjective(this->objectives.size(), storm::utility::zero<GeometryValueType>());
150 directionOfOptimizingObjective[indexOfOptimizingObjective] = storm::utility::one<GeometryValueType>();
151
152 // Improve the found solution.
153 // Note that we do not care whether a threshold is strict anymore, because the resulting optimum should be
154 // the supremum over all strategies. Hence, one could combine a scheduler inducing the optimum value (but possibly violating strict
155 // thresholds) and (with very low probability) a scheduler that satisfies all (possibly strict) thresholds.
156 GeometryValueType result = storm::utility::zero<GeometryValueType>();
157 while (!this->maxStepsPerformed(env) && !storm::utility::resources::isTerminate()) {
158 if (this->refinementSteps.empty()) {
159 // We did not make any refinement steps during the checkAchievability phase (e.g., because there is only one objective).
160 this->weightVectorChecker->setWeightedPrecision(
161 storm::utility::convertNumber<typename SparseModelType::ValueType>(env.modelchecker().multi().getPrecision()));
162 WeightVector separatingVector = directionOfOptimizingObjective;
163 this->performRefinementStep(env, std::move(separatingVector));
164 }
165 std::pair<Point, bool> optimizationRes = this->underApproximation->intersection(thresholdsAsPolytope)->optimize(directionOfOptimizingObjective);
166 STORM_LOG_THROW(optimizationRes.second, storm::exceptions::UnexpectedException, "The underapproximation is either unbounded or empty.");
167 result = optimizationRes.first[indexOfOptimizingObjective];
168 STORM_LOG_DEBUG("Best solution found so far is ~" << storm::utility::convertNumber<double>(result) << ".");
169 // Compute an upper bound for the optimum and check for convergence
170 optimizationRes = this->overApproximation->intersection(thresholdsAsPolytope)->optimize(directionOfOptimizingObjective);
171 if (optimizationRes.second) {
172 GeometryValueType precisionOfResult = optimizationRes.first[indexOfOptimizingObjective] - result;
173 if (precisionOfResult < storm::utility::convertNumber<GeometryValueType>(env.modelchecker().multi().getPrecision())) {
174 // Goal precision reached!
175 return result;
176 } else {
177 STORM_LOG_DEBUG("Solution can be improved by at most " << storm::utility::convertNumber<double>(precisionOfResult));
178 thresholds[indexOfOptimizingObjective] = optimizationRes.first[indexOfOptimizingObjective];
179 }
180 } else {
181 thresholds[indexOfOptimizingObjective] = result + storm::utility::one<GeometryValueType>();
182 }
183 WeightVector separatingVector = this->findSeparatingVector(thresholds);
184 this->updateWeightedPrecisionInImprovingPhase(env, separatingVector);
185 this->performRefinementStep(env, std::move(separatingVector));
186 }
187 STORM_LOG_ERROR("Could not reach the desired precision: Termination requested or maximum number of refinement steps exceeded.");
188 return result;
189}
190
191template<class SparseModelType, typename GeometryValueType>
192void SparsePcaaQuantitativeQuery<SparseModelType, GeometryValueType>::updateWeightedPrecisionInImprovingPhase(Environment const& env,
193 WeightVector const& weights) {
194 STORM_LOG_THROW(!storm::utility::isZero(weights[this->indexOfOptimizingObjective]), exceptions::UnexpectedException,
195 "The chosen weight-vector gives zero weight for the objective that is to be optimized.");
196 STORM_LOG_THROW(env.modelchecker().multi().getPrecisionType() == MultiObjectiveModelCheckerEnvironment::PrecisionType::Absolute,
197 storm::exceptions::IllegalArgumentException, "Unhandled multiobjective precision type.");
198 // If weighs[indexOfOptimizingObjective] is low, the computation of the weightVectorChecker needs to be more precise.
199 // Our heuristic ensures that if p is the new vertex of the under-approximation, then max{ eps | p' = p + (0..0 eps 0..0) is in the over-approximation } <=
200 // multiobjective_precision/0.9
201 GeometryValueType weightedPrecision =
202 weights[this->indexOfOptimizingObjective] * storm::utility::convertNumber<GeometryValueType>(env.modelchecker().multi().getPrecision());
203 // Normalize by division with the Euclidean Norm of the weight-vector
204 weightedPrecision /= storm::utility::sqrt(storm::utility::vector::dotProduct(weights, weights));
205 weightedPrecision *= storm::utility::convertNumber<GeometryValueType>(0.9);
206
207 this->weightVectorChecker->setWeightedPrecision(storm::utility::convertNumber<typename SparseModelType::ValueType>(weightedPrecision));
208}
209
210template<class SparseModelType, typename GeometryValueType>
211bool SparsePcaaQuantitativeQuery<SparseModelType, GeometryValueType>::checkIfThresholdsAreSatisfied(
212 std::shared_ptr<storm::storage::geometry::Polytope<GeometryValueType>> const& polytope) {
213 std::vector<storm::storage::geometry::Halfspace<GeometryValueType>> halfspaces = polytope->getHalfspaces();
214 for (auto const& h : halfspaces) {
215 if (storm::utility::isZero(h.distance(thresholds))) {
216 // Check if the threshold point is on the boundary of the halfspace and whether this is violates strict thresholds
217 if (h.isPointOnBoundary(thresholds)) {
218 for (auto strictThreshold : strictThresholds) {
219 if (h.normalVector()[strictThreshold] > storm::utility::zero<GeometryValueType>()) {
220 return false;
221 }
222 }
223 }
224 } else {
225 return false;
226 }
227 }
228 return true;
229}
230
231#ifdef STORM_HAVE_CARL
232template class SparsePcaaQuantitativeQuery<storm::models::sparse::Mdp<double>, storm::RationalNumber>;
233template class SparsePcaaQuantitativeQuery<storm::models::sparse::MarkovAutomaton<double>, storm::RationalNumber>;
234
235template class SparsePcaaQuantitativeQuery<storm::models::sparse::Mdp<storm::RationalNumber>, storm::RationalNumber>;
236template class SparsePcaaQuantitativeQuery<storm::models::sparse::MarkovAutomaton<storm::RationalNumber>, storm::RationalNumber>;
237#endif
238} // namespace multiobjective
239} // namespace modelchecker
240} // namespace storm
SparsePcaaQuantitativeQuery(preprocessing::SparseMultiObjectivePreprocessorResult< SparseModelType > &preprocessorResult)
virtual std::unique_ptr< CheckResult > check(Environment const &env) override
std::unique_ptr< PcaaWeightVectorChecker< SparseModelType > > weightVectorChecker
std::vector< Objective< typename SparseModelType::ValueType > > objectives
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:18
static std::shared_ptr< Polytope< ValueType > > create(std::vector< Halfspace< ValueType > > const &halfspaces)
Creates a polytope from the given halfspaces.
#define STORM_LOG_DEBUG(message)
Definition logging.h:23
#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
bool isLowerBound(ComparisonType t)
bool isStrict(ComparisonType t)
bool constexpr maximize(OptimizationDirection d)
bool constexpr minimize(OptimizationDirection d)
bool isTerminate()
Check whether the program should terminate (due to some abort signal).
T dotProduct(std::vector< T > const &firstOperand, std::vector< T > const &secondOperand)
Computes the dot product (aka scalar product) and returns the result.
Definition vector.h:517
bool isZero(ValueType const &a)
Definition constants.cpp:41
ValueType sqrt(ValueType const &number)
LabParser.cpp.
Definition cli.cpp:18