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