Storm 1.13.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
SparsePcaaQuery.cpp
Go to the documentation of this file.
2
8#include "storm/io/export.h"
23
25
26template<class SparseModelType, typename GeometryValueType>
28 : initialStateOfOriginalModel(preprocessorResult.originalModel.getInitialStates().getNextSetIndex(0)), objectives(preprocessorResult.objectives) {
29 STORM_LOG_THROW(preprocessorResult.originalModel.getInitialStates().hasUniqueSetBit(), storm::exceptions::NotSupportedException,
30 "The input model does not have a unique initial state.");
31 this->weightVectorChecker = createWeightVectorChecker(preprocessorResult);
32}
33
34template<class SparseModelType, typename GeometryValueType>
35std::unique_ptr<CheckResult> SparsePcaaQuery<SparseModelType, GeometryValueType>::check(Environment const& env, bool produceScheduler) {
36 // Following Algorithm 3.1 of https://doi.org/10.18154/RWTH-2023-09669
37
38 // Ensure that we can handle the input
40 storm::exceptions::IllegalArgumentException, "Unhandled multiobjective precision type.");
41
42 // Auxiliary helper functions for better readability
43 auto abortIterations = [&env](uint64_t numRefinementSteps) {
44 if (env.modelchecker().multi().isMaxStepsSet() && numRefinementSteps >= env.modelchecker().multi().getMaxSteps()) {
45 STORM_LOG_WARN("Aborting multi-objective computation as the maximum number of refinement steps (" << env.modelchecker().multi().getMaxSteps()
46 << ") has been reached.");
47 return true;
49 STORM_LOG_WARN("Aborting multi-objective computation after " << numRefinementSteps << " refinement steps as termination has been requested.");
50 return true;
51 }
52 return false;
53 };
54 auto isMinimizingObjective = [this](uint64_t objIndex) { return storm::solver::minimize(this->objectives[objIndex].formula->getOptimalityType()); };
55
56 // Data maintained through the iterations
57 // The results in each iteration of the algorithm (including achievable points)
58 std::vector<RefinementStep> refinementSteps;
59 // Over-approximation of the set of achievable points
61
62 // Start iterative refinement
63 while (!abortIterations(refinementSteps.size())) {
64 auto answerOrWeights = tryAnswerOrNextWeights(env, refinementSteps, overApproximation, produceScheduler);
65 if (answerOrWeights.index() == 0) {
66 if (env.modelchecker().multi().isExportPlotSet()) {
67 exportPlotOfCurrentApproximation(env, refinementSteps, overApproximation);
68 }
70 STORM_PRINT_AND_LOG("Multi-objective Pareto Curve Approximation algorithm terminated after " << refinementSteps.size()
71 << " refinement steps.\n");
72 }
73 return std::move(std::get<0>(answerOrWeights));
74 };
75 auto [weightVector, epsilonWso] = std::get<1>(answerOrWeights);
76 // Normalize the weight vector to make sure that its magnitude does not influence the accuracy of the weighted sum optimization
77 GeometryValueType normalizationFactor =
79 storm::utility::vector::scaleVectorInPlace(weightVector, normalizationFactor);
80 STORM_LOG_INFO("Iteration #" << refinementSteps.size() << ": Processing new WSO instance with weight vector "
82 << " and precision " << storm::utility::convertNumber<double>(epsilonWso) << ".");
83
84 // Solve WSO instance
85 this->weightVectorChecker->setWeightedPrecision(storm::utility::convertNumber<ModelValueType>(epsilonWso));
86 weightVectorChecker->check(env, storm::utility::vector::convertNumericVector<ModelValueType>(weightVector));
87 GeometryValueType optimalWeightedSum = storm::utility::convertNumber<GeometryValueType>(weightVectorChecker->getOptimalWeightedSum());
88 // Due to numerical issues, it might be that the found optimal weighted sum is smaller than the actual weighted sum of one of the achievable points.
89 // To avoid that our over-approximation does not contain all achievable points, we correct this here.
90 for (auto const& step : refinementSteps) {
91 optimalWeightedSum = std::max(optimalWeightedSum, storm::utility::vector::dotProduct(weightVector, step.achievablePoint));
92 }
93 if (GeometryValueType const diff = optimalWeightedSum - storm::utility::convertNumber<GeometryValueType>(weightVectorChecker->getOptimalWeightedSum());
94 diff > epsilonWso / 10) {
95 STORM_LOG_WARN("Numerical issues: The overapproximation would not contain the underapproximation. Hence, a halfspace is shifted by "
97 }
98
99 // Store result of iteration
100 refinementSteps.push_back(
101 RefinementStep{.weightVector{std::move(weightVector)},
102 .achievablePoint{storm::utility::vector::convertNumericVector<GeometryValueType>(weightVectorChecker->getAchievablePoint())},
103 .optimalWeightedSum{optimalWeightedSum},
104 .scheduler{}});
105 auto& currentStep = refinementSteps.back();
107 "WSO found point " << storm::utility::vector::toString(storm::utility::vector::convertNumericVector<double>(currentStep.achievablePoint)));
108 // For the minimizing objectives, we need to scale the corresponding entries with -1 as we want to consider the downward closure
109 for (uint64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
110 if (isMinimizingObjective(objIndex)) {
111 currentStep.achievablePoint[objIndex] *= -storm::utility::one<GeometryValueType>();
112 }
113 }
114 if (produceScheduler) {
115 currentStep.scheduler = weightVectorChecker->computeScheduler();
116 }
117 overApproximation =
118 overApproximation->intersection(storm::storage::geometry::Halfspace<GeometryValueType>(currentStep.weightVector, currentStep.optimalWeightedSum));
119 }
120 // Reaching this means that we aborted the iterations
121 // Return a best-effort solution
122 std::vector<std::vector<ModelValueType>> achievablePoints;
123 achievablePoints.reserve(refinementSteps.size());
124 for (auto const& step : refinementSteps) {
125 achievablePoints.push_back(
127 }
128 return std::unique_ptr<CheckResult>(new ExplicitParetoCurveCheckResult<ModelValueType>(initialStateOfOriginalModel, std::move(achievablePoints)));
129}
130
131template<class SparseModelType, typename GeometryValueType>
132typename SparsePcaaQuery<SparseModelType, GeometryValueType>::AnswerOrWeights SparsePcaaQuery<SparseModelType, GeometryValueType>::tryAnswerOrNextWeights(
133 Environment const& env, std::vector<RefinementStep> const& refinementSteps, PolytopePtr overApproximation, bool produceScheduler) {
134 if (refinementSteps.size() < objectives.size()) {
135 // At least optimize each objective once
136 WeightVector weightVector(objectives.size(), storm::utility::zero<GeometryValueType>());
137 weightVector[refinementSteps.size()] = storm::utility::one<GeometryValueType>();
138
139 return WeightedSumOptimizationInput{
140 .weightVector{std::move(weightVector)},
141 .epsilonWso{getEpsilonWso(env)},
142 };
143 }
144 storm::storage::BitVector objectivesWithThreshold(objectives.size(), false);
145 std::vector<GeometryValueType> thresholds(objectives.size(), storm::utility::zero<GeometryValueType>());
146 for (uint64_t objIndex = 0; objIndex < objectives.size(); ++objIndex) {
147 auto const& formula = *objectives[objIndex].formula;
148 if (formula.hasBound()) {
149 objectivesWithThreshold.set(objIndex);
150 thresholds[objIndex] = formula.template getThresholdAs<GeometryValueType>();
151 if (storm::solver::minimize(formula.getOptimalityType())) {
152 // Values for minimizing objectives will be negated in order to convert them to maximizing objectives.
153 thresholds[objIndex] *= -storm::utility::one<GeometryValueType>();
154 }
156 !storm::logic::isStrict(formula.getBound().comparisonType),
157 "Strict bound in objective " << objectives[objIndex].originalFormula << " is not supported and will be treated as non-strict bound.");
158 }
159 }
160 if (objectivesWithThreshold.empty() && objectives.size() > 1) {
161 return tryAnswerOrNextWeightsPareto(env, refinementSteps, overApproximation, produceScheduler);
162 } else {
163 uint64_t const numObjectivesWithoutBound = objectives.size() - objectivesWithThreshold.getNumberOfSetBits();
164 std::optional<uint64_t> optObjIndex;
165 if (numObjectivesWithoutBound == 1) {
166 optObjIndex = objectivesWithThreshold.getNextUnsetIndex(0);
167 } else {
168 STORM_LOG_THROW(numObjectivesWithoutBound == 0, storm::exceptions::NotSupportedException,
169 "The type of query is not supported: There are multiple objectives with and without a value bound.");
170 }
171 return tryAnswerOrNextWeightsAchievability(env, optObjIndex, thresholds, refinementSteps, overApproximation, produceScheduler);
172 }
173}
174
175template<typename GeometryValueType>
176auto findSeparatingHalfspace(auto const& refinementSteps, std::vector<GeometryValueType> const& point) {
177 // Build the LP from Figure 3.9 of https://doi.org/10.18154/RWTH-2023-09669
178 uint64_t const dim = point.size();
179 STORM_LOG_ASSERT(dim > 0, "Expected at least one dimension for separating halfspace computation.");
180 STORM_LOG_ASSERT(!refinementSteps.empty(), "Expected at least one refinement step for separating halfspace computation.");
181 auto const zero = storm::utility::zero<GeometryValueType>();
183
184 storm::solver::Z3LpSolver<GeometryValueType> solver(storm::solver::OptimizationDirection::Maximize);
185 std::vector<storm::expressions::Expression> weightVariableExpressions;
186 weightVariableExpressions.reserve(dim);
187 for (uint64_t i = 0; i < dim; ++i) {
188 weightVariableExpressions.push_back(solver.addBoundedContinuousVariable("w" + std::to_string(i), zero, one));
189 }
190 solver.addConstraint("", storm::expressions::sum(weightVariableExpressions) <= solver.getManager().rational(one));
191 auto distVar = solver.addUnboundedContinuousVariable("d", one);
192 for (auto const& step : refinementSteps) {
193 std::vector<storm::expressions::Expression> sum;
194 sum.reserve(dim);
195 for (uint64_t i = 0; i < dim; ++i) {
196 sum.push_back(solver.getManager().rational(point[i] - step.achievablePoint[i]) * weightVariableExpressions[i]);
197 }
198 solver.addConstraint("", distVar <= storm::expressions::sum(sum));
199 }
200 solver.update();
201 solver.optimize();
202 std::optional<storm::storage::geometry::Halfspace<GeometryValueType>> result;
203 if (solver.isOptimal()) {
204 std::vector<GeometryValueType> normalVector;
205 for (auto const& w_i : weightVariableExpressions) {
206 normalVector.push_back(solver.getContinuousValue(w_i.getBaseExpression().asVariableExpression().getVariable()));
207 }
208 GeometryValueType offset = storm::utility::vector::dotProduct(normalVector, point) - solver.getContinuousValue(distVar);
209 result.emplace(std::move(normalVector), std::move(offset));
210 } else {
211 STORM_LOG_THROW(solver.isInfeasible(), storm::exceptions::UnexpectedException, "Unexpected result of LP solver in separating halfspace computation.");
212 }
213 return result;
214}
215
216template<class SparseModelType, typename GeometryValueType>
217typename SparsePcaaQuery<SparseModelType, GeometryValueType>::AnswerOrWeights
218SparsePcaaQuery<SparseModelType, GeometryValueType>::tryAnswerOrNextWeightsAchievability(Environment const& env, std::optional<uint64_t> const optObjIndex,
219 std::vector<GeometryValueType> const& thresholds,
220 std::vector<RefinementStep> const& refinementSteps,
221 PolytopePtr overApproximation, bool produceScheduler) {
222 // First use the overapproximation to either
223 // (1) decide that the thresholds are not achievable or
224 // (2) obtain a reference point that is in the over-approximation, respects the thresholds, and is epsilon-optimal for the objective without threshold (if
225 // any)
226 Point referencePoint;
227 if (!optObjIndex.has_value()) {
228 if (!overApproximation->contains(thresholds)) {
229 // The thresholds are not achievable
230 return std::unique_ptr<CheckResult>(new ExplicitQualitativeCheckResult<ModelValueType>(initialStateOfOriginalModel, false));
231 }
232 referencePoint = thresholds;
233 } else {
234 // Get the best value we can hope to achieve
235 std::vector<Halfspace> thresholdsHalfspaces;
236 for (uint64_t objIndex = 0; objIndex < objectives.size(); ++objIndex) {
237 if (objIndex == optObjIndex.value()) {
238 continue;
239 }
240 thresholdsHalfspaces.push_back(Halfspace(WeightVector(objectives.size(), storm::utility::zero<GeometryValueType>()), -thresholds[objIndex]));
241 thresholdsHalfspaces.back().normalVector()[objIndex] = -storm::utility::one<GeometryValueType>();
242 }
243 auto thresholdPolytope = Polytope::create(thresholdsHalfspaces);
244 auto intersection = overApproximation->intersection(thresholdPolytope);
245 WeightVector optDirVector(objectives.size(), storm::utility::zero<GeometryValueType>());
246 optDirVector[optObjIndex.value()] = storm::utility::one<GeometryValueType>();
247 auto optRes = overApproximation->intersection(thresholdPolytope)->optimize(optDirVector);
248 if (!optRes.second) {
249 // The thresholds are not achievable
250 return std::unique_ptr<CheckResult>(new ExplicitQualitativeCheckResult<ModelValueType>(initialStateOfOriginalModel, false));
251 }
252 referencePoint = thresholds;
253 referencePoint[optObjIndex.value()] =
254 optRes.first[optObjIndex.value()] - storm::utility::convertNumber<GeometryValueType>(env.modelchecker().multi().getPrecision());
255 // The following assertion holds because optRes.first is in the over-approximation and satisfies all thresholds and the over-approximation is
256 // downward closed
257 STORM_LOG_ASSERT(overApproximation->contains(referencePoint), "Expected reference point to be contained in the over-approximation");
258 }
259
260 // Second, find a separating halfspace between the under-approximation and the reference point with maximal L1 distance (not Euclidean!) to the latter
261 auto separatingHalfspace = findSeparatingHalfspace(refinementSteps, referencePoint);
262 bool const referencePointInUnderApproximation = !separatingHalfspace.has_value() || separatingHalfspace->contains(referencePoint);
263 if (referencePointInUnderApproximation) {
264 // The reference point is achievable. We can assemble a result
265 STORM_LOG_THROW(!produceScheduler, storm::exceptions::NotSupportedException,
266 "Producing schedulers is currently not supported for (numerical) achievability queries.");
267 // TODO: to get a scheduler, we need to find a convex combination of the achievable points that yields the reference point (using LP)
268 if (optObjIndex.has_value()) {
269 // Return the middle-value of the result interval [ referencePoint[optObjIndex], referencePoint[optObjIndex] + multiPrecisoin ]
270 GeometryValueType result =
271 referencePoint[optObjIndex.value()] + (storm::utility::convertNumber<GeometryValueType>(env.modelchecker().multi().getPrecision()) /
273 auto resultForOriginalModel =
275
276 return std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ModelValueType>(initialStateOfOriginalModel, resultForOriginalModel));
277 } else {
278 return std::unique_ptr<CheckResult>(new ExplicitQualitativeCheckResult<ModelValueType>(initialStateOfOriginalModel, true));
279 }
280 }
281
282 // We found a separating halfspace that we can use to refine the approximation
283 GeometryValueType eps_wso = getEpsilonWso(env);
284 // Check if there is a need to increase the weighted sum optimization precision
285 if (separatingHalfspace->distance(referencePoint) < eps_wso) {
286 // The reference point is close to the under-approximation. We also check if the boundary of the over-approximation is close
287 auto optResPair = overApproximation->optimize(separatingHalfspace->normalVector());
288 STORM_LOG_ASSERT(optResPair.second, "Expected optimization to be successful as the over-approximation is non-empty.");
289 eps_wso = getEpsilonWso(env, separatingHalfspace->distance(optResPair.first));
290 }
291 return WeightedSumOptimizationInput{
292 .weightVector{separatingHalfspace->normalVector()},
293 .epsilonWso{eps_wso},
294 };
295}
296
297template<class SparseModelType, typename GeometryValueType>
298typename SparsePcaaQuery<SparseModelType, GeometryValueType>::AnswerOrWeights SparsePcaaQuery<SparseModelType, GeometryValueType>::tryAnswerOrNextWeightsPareto(
299 Environment const& env, std::vector<RefinementStep> const& refinementSteps, PolytopePtr overApproximation, bool produceScheduler) {
300 // First get the halfspaces whose intersection underapproximates the set of achievable points
301 std::vector<Point> achievablePoints;
302 achievablePoints.reserve(refinementSteps.size());
303 for (auto const& step : refinementSteps) {
304 achievablePoints.push_back(step.achievablePoint);
305 }
306 PolytopePtr underApproximation = Polytope::createDownwardClosure(achievablePoints);
307 auto achievableHalfspaces = underApproximation->getHalfspaces();
308 // Now check whether the over-approximation contains a point that is not close enough to the under-approximation
309 GeometryValueType delta = storm::utility::convertNumber<GeometryValueType>(env.modelchecker().multi().getPrecision()) /
310 storm::utility::convertNumber<GeometryValueType>(std::sqrt(objectives.size()));
311 for (auto const& halfspace : achievableHalfspaces) {
312 GeometryValueType const sumOfWeights =
313 std::accumulate(halfspace.normalVector().begin(), halfspace.normalVector().end(), storm::utility::zero<GeometryValueType>());
314 auto invertedShiftedHalfspace = halfspace.invert();
315 invertedShiftedHalfspace.offset() -= delta * sumOfWeights;
316 auto intersection = overApproximation->intersection(invertedShiftedHalfspace);
317 if (!intersection->isEmpty()) {
318 return WeightedSumOptimizationInput{
319 .weightVector{halfspace.normalVector()},
320 .epsilonWso{getEpsilonWso(env)},
321 };
322 }
323 }
324 // If we reach this point, the over-approximation is close enough to the under-approximation
325 // obtain the data for the checkresult
326 // We take the paretoOptimalPoints as the vertices of the underApproximation.
327 // This is to filter out points found in a refinement step that are dominated by another point.
328 std::vector<std::vector<ModelValueType>> paretoOptimalPoints;
329 std::vector<storm::storage::Scheduler<ModelValueType>> paretoOptimalSchedulers;
330 std::vector<Point> vertices = underApproximation->getVertices();
331 paretoOptimalPoints.reserve(vertices.size());
332 for (auto const& vertex : vertices) {
333 paretoOptimalPoints.push_back(
335 if (produceScheduler) {
336 // Find the refinement step in which we found the vertex
337 // This is guaranteed to work as long as GeometryValueType is exact, i.e.,
338 // there as long as there are no rounding errors when converting from set of points into a (H-)polytope and then back to a vertex set.
340 auto stepIt = std::find_if(refinementSteps.begin(), refinementSteps.end(), [&vertex](auto const& step) { return step.achievablePoint == vertex; });
341 STORM_LOG_ASSERT(stepIt != refinementSteps.end(),
342 "Scheduler for point " << storm::utility::vector::toString(paretoOptimalPoints.back()) << " not found.");
343 STORM_LOG_ASSERT(stepIt->scheduler.has_value(),
344 "Scheduler for point " << storm::utility::vector::toString(paretoOptimalPoints.back()) << " not generated.");
345 paretoOptimalSchedulers.push_back(std::move(stepIt->scheduler.value()));
346 }
347 }
348 return std::unique_ptr<CheckResult>(new ExplicitParetoCurveCheckResult<ModelValueType>(
349 initialStateOfOriginalModel, std::move(paretoOptimalPoints), std::move(paretoOptimalSchedulers),
350 transformObjectivePolytopeToOriginal(this->objectives, underApproximation)->template convertNumberRepresentation<ModelValueType>(),
351 transformObjectivePolytopeToOriginal(this->objectives, overApproximation)->template convertNumberRepresentation<ModelValueType>()));
352}
353
354template<typename SparseModelType, typename GeometryValueType>
355GeometryValueType SparsePcaaQuery<SparseModelType, GeometryValueType>::getEpsilonWso(Environment const& env, std::optional<GeometryValueType> approxDistance) {
356 // Determine heuristic parameter gamma for approximation tradeoff. We should have 0 < gamma < 1, where small values mean that weighted sum optimization
357 // needs to be done with high accuracy.
358 GeometryValueType gamma;
359 if (env.modelchecker().multi().isApproximationTradeoffSet()) {
360 // A value was set explicitly, so we use that.
361 gamma = storm::utility::convertNumber<GeometryValueType>(env.modelchecker().multi().getApproximationTradeoff());
362 } else {
363 // No value was set explicitly. We pick one heuristically
364 if (env.solver().isForceExact()) {
365 gamma = storm::utility::zero<GeometryValueType>(); // In exact mode, we don't expect any inaccuracies in the WSO solver
366 } else if (env.solver().isForceSoundness() || weightVectorChecker->smallPrecisionsAreChallenging()) {
367 // in sound mode and/or when WSO calls are challenging, we pick a middle-ground value
369 } else {
370 // In unsound mode with non-challenging WSO calls, we don't want too inaccurate precisions (e.g. standard value iteration with large epsilon becomes
371 // very unreliable). Hence, we pick a rather small value.
373 }
374 }
375
376 // Get the precision for multiobjective model checking. Further decrease it if the approximation is close.
377 GeometryValueType eps_multi = storm::utility::convertNumber<GeometryValueType>(env.modelchecker().multi().getPrecision());
378 if (approxDistance.has_value()) {
379 eps_multi = std::min<GeometryValueType>(eps_multi, approxDistance.value());
380 }
381
382 // We divide by sqrt(objectives.size()) to ensure that even for values of gamma close to 1, we can still achieve enough precision
383 // See Example 3.5 in https://doi.org/10.18154/RWTH-2023-09669 for an example why this is needed.
384 return gamma * eps_multi / storm::utility::convertNumber<GeometryValueType>(std::sqrt(objectives.size()));
385}
386
387template<typename SparseModelType, typename GeometryValueType>
388void SparsePcaaQuery<SparseModelType, GeometryValueType>::exportPlotOfCurrentApproximation(Environment const& env,
389 std::vector<RefinementStep> const& refinementSteps,
390 PolytopePtr overApproximation) const {
391 STORM_LOG_ERROR_COND(objectives.size() == 2, "Exporting plot requested but this is only implemented for the two-dimensional case.");
392
393 // Get achievable points as well as a hyperrectangle that is used to guarantee that the resulting polytopes are bounded.
394 storm::storage::geometry::Hyperrectangle<GeometryValueType> boundaries(
395 std::vector<GeometryValueType>(objectives.size(), storm::utility::zero<GeometryValueType>()),
396 std::vector<GeometryValueType>(objectives.size(), storm::utility::zero<GeometryValueType>()));
397 std::vector<std::vector<GeometryValueType>> achievablePoints;
398 achievablePoints.reserve(refinementSteps.size());
399 for (auto const& step : refinementSteps) {
400 achievablePoints.push_back(transformObjectiveValuesToOriginal(this->objectives, step.achievablePoint));
401 boundaries.enlarge(achievablePoints.back());
402 }
403
404 PolytopePtr underApproximation = Polytope::createDownwardClosure(achievablePoints);
405 auto transformedUnderApprox = transformObjectivePolytopeToOriginal(this->objectives, underApproximation);
406 auto transformedOverApprox = transformObjectivePolytopeToOriginal(this->objectives, overApproximation);
407
408 auto underApproxVertices = transformedUnderApprox->getVertices();
409 for (auto const& v : underApproxVertices) {
410 boundaries.enlarge(v);
411 }
412 auto overApproxVertices = transformedOverApprox->getVertices();
413 for (auto const& v : overApproxVertices) {
414 boundaries.enlarge(v);
415 }
416
417 // Further enlarge the boundaries a little
418 storm::utility::vector::scaleVectorInPlace(boundaries.lowerBounds(), GeometryValueType(15) / GeometryValueType(10));
419 storm::utility::vector::scaleVectorInPlace(boundaries.upperBounds(), GeometryValueType(15) / GeometryValueType(10));
420
421 auto boundariesAsPolytope = boundaries.asPolytope();
422 std::vector<std::string> columnHeaders = {"x", "y"};
423
424 std::vector<std::vector<double>> pointsForPlotting;
425 if (env.modelchecker().multi().getPlotPathUnderApproximation()) {
426 underApproxVertices = transformedUnderApprox->intersection(boundariesAsPolytope)->getVerticesInClockwiseOrder();
427 pointsForPlotting.reserve(underApproxVertices.size());
428 for (auto const& v : underApproxVertices) {
429 pointsForPlotting.push_back(storm::utility::vector::convertNumericVector<double>(v));
430 }
431 storm::io::exportDataToCSVFile<double, std::string>(env.modelchecker().multi().getPlotPathUnderApproximation().get(), pointsForPlotting, columnHeaders);
432 }
433
434 if (env.modelchecker().multi().getPlotPathOverApproximation()) {
435 pointsForPlotting.clear();
436 overApproxVertices = transformedOverApprox->intersection(boundariesAsPolytope)->getVerticesInClockwiseOrder();
437 pointsForPlotting.reserve(overApproxVertices.size());
438 for (auto const& v : overApproxVertices) {
439 pointsForPlotting.push_back(storm::utility::vector::convertNumericVector<double>(v));
440 }
441 storm::io::exportDataToCSVFile<double, std::string>(env.modelchecker().multi().getPlotPathOverApproximation().get(), pointsForPlotting, columnHeaders);
442 }
443
444 if (env.modelchecker().multi().getPlotPathParetoPoints()) {
445 pointsForPlotting.clear();
446 pointsForPlotting.reserve(achievablePoints.size());
447 for (auto const& v : achievablePoints) {
448 pointsForPlotting.push_back(storm::utility::vector::convertNumericVector<double>(v));
449 }
450 storm::io::exportDataToCSVFile<double, std::string>(env.modelchecker().multi().getPlotPathParetoPoints().get(), pointsForPlotting, columnHeaders);
451 }
452}
453
454template class SparsePcaaQuery<storm::models::sparse::Mdp<double>, storm::RationalNumber>;
455template class SparsePcaaQuery<storm::models::sparse::MarkovAutomaton<double>, storm::RationalNumber>;
456
457template class SparsePcaaQuery<storm::models::sparse::Mdp<storm::RationalNumber>, storm::RationalNumber>;
459} // namespace storm::modelchecker::multiobjective
ModelCheckerEnvironment & modelchecker()
MultiObjectiveModelCheckerEnvironment & multi()
SparsePcaaQuery(PreprocessorResult &preprocessorResult)
Creates a new query for the Pareto curve approximation algorithm (Pcaa).
std::unique_ptr< CheckResult > check(Environment const &env, bool produceScheduler)
Invokes the computation and retrieves the result.
A class that implements the LpSolver interface using Z3.
Definition Z3LpSolver.h:23
static std::shared_ptr< Polytope< GeometryValueType > > createUniversalPolytope()
#define STORM_LOG_INFO(message)
Definition logging.h:24
#define STORM_LOG_WARN(message)
Definition logging.h:25
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:11
#define STORM_LOG_WARN_COND(cond, message)
Definition macros.h:38
#define STORM_LOG_ERROR_COND(cond, message)
Definition macros.h:52
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
#define STORM_PRINT_AND_LOG(message)
Definition macros.h:68
Expression sum(std::vector< storm::expressions::Expression > const &expressions)
void exportDataToCSVFile(std::string filepath, std::vector< std::vector< DataType > > const &data, boost::optional< std::vector< Header1Type > > const &header1=boost::none, boost::optional< std::vector< Header2Type > > const &header2=boost::none)
Definition export.h:13
bool isStrict(ComparisonType t)
auto findSeparatingHalfspace(auto const &refinementSteps, std::vector< GeometryValueType > const &point)
std::shared_ptr< storm::storage::geometry::Polytope< GeometryValueType > > transformObjectivePolytopeToOriginal(std::vector< Objective< ValueType > > const &objectives, std::shared_ptr< storm::storage::geometry::Polytope< GeometryValueType > > const &polytope)
std::unique_ptr< PcaaWeightVectorChecker< ModelType > > createWeightVectorChecker(preprocessing::SparseMultiObjectivePreprocessorResult< ModelType > const &preprocessorResult)
std::vector< GeometryValueType > transformObjectiveValuesToOriginal(std::vector< Objective< ValueType > > const &objectives, std::vector< GeometryValueType > const &point)
GeometryValueType transformObjectiveValueToOriginal(Objective< ValueType > const &objective, GeometryValueType const &value)
SettingsType const & getModule()
Get module.
bool constexpr minimize(OptimizationDirection d)
bool isTerminate()
Check whether the program should terminate (due to some abort signal).
std::vector< TargetType > convertNumericVector(std::vector< SourceType > const &oldVector)
Converts the given vector to the given ValueType Assumes that both, TargetType and SourceType are num...
Definition vector.h:966
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:473
std::string toString(std::vector< ValueType > const &vector)
Output vector as string.
Definition vector.h:1179
void scaleVectorInPlace(std::vector< ValueType1 > &target, ValueType2 const &factor)
Multiplies each element of the given vector with the given factor and writes the result into the vect...
Definition vector.h:447
ValueType zero()
Definition constants.cpp:24
ValueType one()
Definition constants.cpp:19
ValueType sqrt(ValueType const &number)
TargetType convertNumber(SourceType const &number)
static const bool IsExact