Storm 1.11.1.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
SparsePcaaQuery.cpp
Go to the documentation of this file.
2
6#include "storm/io/export.h"
16
17namespace storm {
18namespace modelchecker {
19namespace multiobjective {
20
21template<class SparseModelType, typename GeometryValueType>
23 if (storm::settings::getModule<storm::settings::modules::CoreSettings>().isShowStatisticsSet()) {
24 STORM_PRINT_AND_LOG("Pareto Curve Approximation Algorithm terminated after " << this->refinementSteps.size() << " refinement steps.\n");
25 }
26}
27
28template<class SparseModelType, typename GeometryValueType>
37
38template<class SparseModelType, typename GeometryValueType>
40 Point const& pointToBeSeparated) {
41 STORM_LOG_DEBUG("Searching a weight vector to seperate the point given by "
42 << storm::utility::vector::toString(storm::utility::vector::convertNumericVector<double>(pointToBeSeparated)) << ".");
43
44 if (underApproximation->isEmpty()) {
45 // In this case, every weight vector is separating
46 uint_fast64_t objIndex = diracWeightVectorsToBeChecked.getNextSetIndex(0) % pointToBeSeparated.size();
47 WeightVector result(pointToBeSeparated.size(), storm::utility::zero<GeometryValueType>());
48 result[objIndex] = storm::utility::one<GeometryValueType>();
49 diracWeightVectorsToBeChecked.set(objIndex, false);
50 return result;
51 }
52
53 // Reaching this point means that the underApproximation contains halfspaces. The seperating vector has to be the normal vector of one of these halfspaces.
54 // We pick one with maximal distance to the given point. However, Dirac weight vectors that only assign a non-zero weight to a single objective take
55 // precedence.
56 STORM_LOG_ASSERT(!underApproximation->contains(pointToBeSeparated),
57 "Tried to find a separating point but the point is already contained in the underApproximation");
58 std::vector<storm::storage::geometry::Halfspace<GeometryValueType>> halfspaces = underApproximation->getHalfspaces();
59 uint_fast64_t farestHalfspaceIndex = halfspaces.size();
60 GeometryValueType farestDistance = -storm::utility::one<GeometryValueType>();
61 bool foundSeparatingDiracVector = false;
62 for (uint_fast64_t halfspaceIndex = 0; halfspaceIndex < halfspaces.size(); ++halfspaceIndex) {
63 GeometryValueType distance = halfspaces[halfspaceIndex].euclideanDistance(pointToBeSeparated);
64 if (!storm::utility::isZero(distance)) {
65 storm::storage::BitVector nonZeroVectorEntries = ~storm::utility::vector::filterZero<GeometryValueType>(halfspaces[halfspaceIndex].normalVector());
66 bool isSingleObjectiveVector =
67 nonZeroVectorEntries.getNumberOfSetBits() == 1 && diracWeightVectorsToBeChecked.get(nonZeroVectorEntries.getNextSetIndex(0));
68 // Check if this halfspace is a better candidate than the current one
69 if ((!foundSeparatingDiracVector && isSingleObjectiveVector) ||
70 (foundSeparatingDiracVector == isSingleObjectiveVector && distance > farestDistance)) {
71 foundSeparatingDiracVector = foundSeparatingDiracVector || isSingleObjectiveVector;
72 farestHalfspaceIndex = halfspaceIndex;
73 farestDistance = distance;
74 }
75 }
76 }
77 if (foundSeparatingDiracVector) {
78 diracWeightVectorsToBeChecked &= storm::utility::vector::filterZero<GeometryValueType>(halfspaces[farestHalfspaceIndex].normalVector());
79 }
80
81 STORM_LOG_THROW(farestHalfspaceIndex < halfspaces.size(), storm::exceptions::UnexpectedException, "There is no seperating vector.");
82 STORM_LOG_DEBUG("Found separating weight vector: "
83 << storm::utility::vector::toString(storm::utility::vector::convertNumericVector<double>(halfspaces[farestHalfspaceIndex].normalVector()))
84 << ".");
85 return halfspaces[farestHalfspaceIndex].normalVector();
86}
87
88template<class SparseModelType, typename GeometryValueType>
90 // Normalize the direction vector so that the entries sum up to one
92 direction, storm::utility::one<GeometryValueType>() / std::accumulate(direction.begin(), direction.end(), storm::utility::zero<GeometryValueType>()));
93 weightVectorChecker->check(env, storm::utility::vector::convertNumericVector<typename SparseModelType::ValueType>(direction));
94 STORM_LOG_DEBUG("weighted objectives checker result (under approximation) is " << storm::utility::vector::toString(
95 storm::utility::vector::convertNumericVector<double>(weightVectorChecker->getUnderApproximationOfInitialStateResults())));
96 RefinementStep step;
97 step.weightVector = direction;
98 step.lowerBoundPoint = storm::utility::vector::convertNumericVector<GeometryValueType>(weightVectorChecker->getUnderApproximationOfInitialStateResults());
99 step.upperBoundPoint = storm::utility::vector::convertNumericVector<GeometryValueType>(weightVectorChecker->getOverApproximationOfInitialStateResults());
100 // For the minimizing objectives, we need to scale the corresponding entries with -1 as we want to consider the downward closure
101 for (uint_fast64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
102 if (storm::solver::minimize(this->objectives[objIndex].formula->getOptimalityType())) {
103 step.lowerBoundPoint[objIndex] *= -storm::utility::one<GeometryValueType>();
104 step.upperBoundPoint[objIndex] *= -storm::utility::one<GeometryValueType>();
105 }
106 }
107 if (produceScheduler) {
108 step.scheduler = weightVectorChecker->computeScheduler();
109 }
110 refinementSteps.push_back(std::move(step));
111
112 updateOverApproximation();
113 updateUnderApproximation();
114}
115
116template<class SparseModelType, typename GeometryValueType>
119 refinementSteps.back().weightVector, storm::utility::vector::dotProduct(refinementSteps.back().weightVector, refinementSteps.back().upperBoundPoint));
120
121 // Due to numerical issues, it might be the case that the updated overapproximation does not contain the underapproximation,
122 // e.g., when the new point is strictly contained in the underapproximation. Check if this is the case.
123 GeometryValueType maximumOffset = h.offset();
124 for (auto const& step : refinementSteps) {
125 maximumOffset = std::max(maximumOffset, storm::utility::vector::dotProduct(h.normalVector(), step.lowerBoundPoint));
126 }
127 if (maximumOffset > h.offset()) {
128 // We correct the issue by shifting the halfspace such that it contains the underapproximation
129 h.offset() = maximumOffset;
130 STORM_LOG_WARN("Numerical issues: The overapproximation would not contain the underapproximation. Hence, a halfspace is shifted by "
131 << storm::utility::convertNumber<double>(h.invert().euclideanDistance(refinementSteps.back().upperBoundPoint)) << ".");
132 }
133 overApproximation = overApproximation->intersection(h);
134 STORM_LOG_DEBUG("Updated OverApproximation to " << overApproximation->toString(true));
135}
136
137template<class SparseModelType, typename GeometryValueType>
139 std::vector<Point> paretoPoints;
140 paretoPoints.reserve(refinementSteps.size());
141 for (auto const& step : refinementSteps) {
142 paretoPoints.push_back(step.lowerBoundPoint);
143 }
145 STORM_LOG_DEBUG("Updated UnderApproximation to " << underApproximation->toString(true));
146}
147
148template<class SparseModelType, typename GeometryValueType>
150 return env.modelchecker().multi().isMaxStepsSet() && this->refinementSteps.size() >= env.modelchecker().multi().getMaxSteps();
151}
152
153template<typename SparseModelType, typename GeometryValueType>
155 STORM_LOG_ERROR_COND(objectives.size() == 2, "Exporting plot requested but this is only implemented for the two-dimensional case.");
156
157 auto transformedUnderApprox = transformObjectivePolytopeToOriginal(this->objectives, underApproximation);
158 auto transformedOverApprox = transformObjectivePolytopeToOriginal(this->objectives, overApproximation);
159
160 // Get pareto points as well as a hyperrectangle that is used to guarantee that the resulting polytopes are bounded.
162 std::vector<GeometryValueType>(objectives.size(), storm::utility::zero<GeometryValueType>()),
163 std::vector<GeometryValueType>(objectives.size(), storm::utility::zero<GeometryValueType>()));
164 std::vector<std::vector<GeometryValueType>> paretoPoints;
165 paretoPoints.reserve(refinementSteps.size());
166 for (auto const& step : refinementSteps) {
167 paretoPoints.push_back(transformObjectiveValuesToOriginal(this->objectives, step.lowerBoundPoint));
168 boundaries.enlarge(paretoPoints.back());
169 }
170 auto underApproxVertices = transformedUnderApprox->getVertices();
171 for (auto const& v : underApproxVertices) {
172 boundaries.enlarge(v);
173 }
174 auto overApproxVertices = transformedOverApprox->getVertices();
175 for (auto const& v : overApproxVertices) {
176 boundaries.enlarge(v);
177 }
178
179 // Further enlarge the boundaries a little
180 storm::utility::vector::scaleVectorInPlace(boundaries.lowerBounds(), GeometryValueType(15) / GeometryValueType(10));
181 storm::utility::vector::scaleVectorInPlace(boundaries.upperBounds(), GeometryValueType(15) / GeometryValueType(10));
182
183 auto boundariesAsPolytope = boundaries.asPolytope();
184 std::vector<std::string> columnHeaders = {"x", "y"};
185
186 std::vector<std::vector<double>> pointsForPlotting;
188 underApproxVertices = transformedUnderApprox->intersection(boundariesAsPolytope)->getVerticesInClockwiseOrder();
189 pointsForPlotting.reserve(underApproxVertices.size());
190 for (auto const& v : underApproxVertices) {
191 pointsForPlotting.push_back(storm::utility::vector::convertNumericVector<double>(v));
192 }
193 storm::io::exportDataToCSVFile<double, std::string>(env.modelchecker().multi().getPlotPathUnderApproximation().get(), pointsForPlotting, columnHeaders);
194 }
195
197 pointsForPlotting.clear();
198 overApproxVertices = transformedOverApprox->intersection(boundariesAsPolytope)->getVerticesInClockwiseOrder();
199 pointsForPlotting.reserve(overApproxVertices.size());
200 for (auto const& v : overApproxVertices) {
201 pointsForPlotting.push_back(storm::utility::vector::convertNumericVector<double>(v));
202 }
203 storm::io::exportDataToCSVFile<double, std::string>(env.modelchecker().multi().getPlotPathOverApproximation().get(), pointsForPlotting, columnHeaders);
204 }
205
207 pointsForPlotting.clear();
208 pointsForPlotting.reserve(paretoPoints.size());
209 for (auto const& v : paretoPoints) {
210 pointsForPlotting.push_back(storm::utility::vector::convertNumericVector<double>(v));
211 }
212 storm::io::exportDataToCSVFile<double, std::string>(env.modelchecker().multi().getPlotPathParetoPoints().get(), pointsForPlotting, columnHeaders);
213 }
214}
215
216template class SparsePcaaQuery<storm::models::sparse::Mdp<double>, storm::RationalNumber>;
217template class SparsePcaaQuery<storm::models::sparse::MarkovAutomaton<double>, storm::RationalNumber>;
218
219template class SparsePcaaQuery<storm::models::sparse::Mdp<storm::RationalNumber>, storm::RationalNumber>;
221} // namespace multiobjective
222} // namespace modelchecker
223} // namespace storm
ModelCheckerEnvironment & modelchecker()
MultiObjectiveModelCheckerEnvironment & multi()
SparsePcaaQuery(preprocessing::SparseMultiObjectivePreprocessorResult< SparseModelType > &preprocessorResult)
std::shared_ptr< storm::storage::geometry::Polytope< GeometryValueType > > overApproximation
WeightVector findSeparatingVector(Point const &pointToBeSeparated)
std::unique_ptr< PcaaWeightVectorChecker< SparseModelType > > weightVectorChecker
bool maxStepsPerformed(Environment const &env) const
void performRefinementStep(Environment const &env, WeightVector &&direction, bool produceScheduler)
std::shared_ptr< storm::storage::geometry::Polytope< GeometryValueType > > underApproximation
void exportPlotOfCurrentApproximation(Environment const &env) const
std::vector< Objective< typename SparseModelType::ValueType > > objectives
static std::unique_ptr< PcaaWeightVectorChecker< ModelType > > create(preprocessing::SparseMultiObjectivePreprocessorResult< ModelType > const &preprocessorResult)
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:16
uint64_t getNextSetIndex(uint64_t startingIndex) const
Retrieves the index of the bit that is the next bit set to true in the bit vector.
uint64_t getNumberOfSetBits() const
Returns the number of bits that are set to true in this bit vector.
Halfspace< ValueType > invert() const
Definition Halfspace.h:67
std::vector< ValueType > const & normalVector() const
Definition Halfspace.h:112
ValueType const & offset() const
Definition Halfspace.h:120
void enlarge(std::vector< ValueType > const &point)
std::vector< ValueType > const & lowerBounds() const
std::shared_ptr< Polytope< ValueType > > asPolytope() const
std::vector< ValueType > const & upperBounds() const
static std::shared_ptr< Polytope< ValueType > > createDownwardClosure(std::vector< Point > const &points)
Creates the downward closure of the given points (i.e., the set { x | ex.
Definition Polytope.cpp:40
static std::shared_ptr< Polytope< ValueType > > createUniversalPolytope()
Creates the universal polytope (i.e., the set R^n)
Definition Polytope.cpp:24
static std::shared_ptr< Polytope< ValueType > > createEmptyPolytope()
Creates the empty polytope (i.e., emptyset)
Definition Polytope.cpp:29
#define STORM_LOG_WARN(message)
Definition logging.h:25
#define STORM_LOG_DEBUG(message)
Definition logging.h:18
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:11
#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
std::vector< GeometryValueType > transformObjectiveValuesToOriginal(std::vector< Objective< ValueType > > objectives, std::vector< GeometryValueType > const &point)
std::shared_ptr< storm::storage::geometry::Polytope< GeometryValueType > > transformObjectivePolytopeToOriginal(std::vector< Objective< ValueType > > objectives, std::shared_ptr< storm::storage::geometry::Polytope< GeometryValueType > > const &polytope)
bool constexpr minimize(OptimizationDirection d)
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:1145
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
bool isZero(ValueType const &a)
Definition constants.cpp:39
std::optional< storm::storage::Scheduler< typename SparseModelType::ValueType > > scheduler