26namespace modelchecker {
27namespace multiobjective {
29template<
class SparseModelType,
typename GeometryValueType>
31 : coordinates(coordinates), onFacet(false), paretoOptimal(false) {
32 STORM_LOG_ASSERT(!this->coordinates.empty(),
"Points with dimension 0 are not supported");
35template<
class SparseModelType,
typename GeometryValueType>
37 : coordinates(
std::move(coordinates)), onFacet(false), paretoOptimal(false) {
38 STORM_LOG_ASSERT(!this->coordinates.empty(),
"Points with dimension 0 are not supported");
41template<
class SparseModelType,
typename GeometryValueType>
46template<
class SparseModelType,
typename GeometryValueType>
51template<
class SparseModelType,
typename GeometryValueType>
53 STORM_LOG_ASSERT(!coordinates.empty(),
"Points with dimension 0 are not supported");
54 return coordinates.size();
57template<
class SparseModelType,
typename GeometryValueType>
61 auto thisIt = this->get().begin();
62 auto otherIt = other.
get().begin();
63 auto thisItE = this->get().end();
66 while (*thisIt == *otherIt) {
69 if (thisIt == thisItE) {
70 return DominanceResult::Equal;
74 if (*thisIt > *otherIt) {
76 for (++thisIt, ++otherIt; thisIt != thisItE; ++thisIt, ++otherIt) {
77 if (*thisIt < *otherIt) {
78 return DominanceResult::Incomparable;
81 return DominanceResult::Dominates;
83 assert(*thisIt < *otherIt);
85 for (++thisIt, ++otherIt; thisIt != thisItE; ++thisIt, ++otherIt) {
86 if (*thisIt > *otherIt) {
87 return DominanceResult::Incomparable;
90 return DominanceResult::Dominated;
94template<
class SparseModelType,
typename GeometryValueType>
99template<
class SparseModelType,
typename GeometryValueType>
104template<
class SparseModelType,
typename GeometryValueType>
106 paretoOptimal = value;
109template<
class SparseModelType,
typename GeometryValueType>
111 return paretoOptimal;
114template<
class SparseModelType,
typename GeometryValueType>
116 std::stringstream out;
118 for (
auto const& pi : this->get()) {
124 if (convertToDouble) {
125 out << storm::utility::convertNumber<double>(pi);
133template<
class SparseModelType,
typename GeometryValueType>
138template<
class SparseModelType,
typename GeometryValueType>
139boost::optional<typename DeterministicSchedsParetoExplorer<SparseModelType, GeometryValueType>::PointId>
142 auto pointsIt = points.begin();
143 while (pointsIt != points.end()) {
144 switch (point.getDominance(pointsIt->second)) {
150 if (pointsIt->second.liesOnFacet()) {
152 pointsIt->second.setParetoOptimal(
false);
155 pointsIt = points.erase(pointsIt);
162 if (point.liesOnFacet()) {
163 pointsIt->second.setOnFacet();
165 return pointsIt->first;
169 point.setParetoOptimal(
true);
172 std::cout <<
"## achievable point: [" << point.toString(
true) <<
"]\n";
174 points.emplace_hint(points.end(), currId, std::move(point));
178template<
class SparseModelType,
typename GeometryValueType>
181 return points.at(
id);
184template<
class SparseModelType,
typename GeometryValueType>
187 return points.begin();
190template<
class SparseModelType,
typename GeometryValueType>
196template<
class SparseModelType,
typename GeometryValueType>
198 return points.size();
201template<
class SparseModelType,
typename GeometryValueType>
204 std::vector<std::vector<GeometryValueType>> pointsAsVector;
205 pointsAsVector.reserve(size());
206 for (
auto const& p : points) {
207 pointsAsVector.push_back(p.second.get());
212template<
class SparseModelType,
typename GeometryValueType>
215 for (
auto const& p : points) {
216 if (polytope->contains(p.second.get())) {
217 collectedPoints.insert(p.first);
222template<
class SparseModelType,
typename GeometryValueType>
224 for (
auto const& p : this->points) {
226 out << p.first <<
": [" << p.second.toString(convertToDouble) <<
"]\n";
228 out << p.second.toString(convertToDouble) <<
'\n';
233template<
class SparseModelType,
typename GeometryValueType>
235 : halfspace(halfspace) {
239template<
class SparseModelType,
typename GeometryValueType>
241 : halfspace(
std::move(halfspace)) {
245template<
class SparseModelType,
typename GeometryValueType>
251template<
class SparseModelType,
typename GeometryValueType>
254 if (product != getHalfspace().offset()) {
255 if (product < getHalfspace().offset()) {
257 << storm::utility::convertNumber<double>(getHalfspace().euclideanDistance(point.
get())));
259 STORM_LOG_DEBUG(
"Halfspace of facet is shifted by " << storm::utility::convertNumber<double>(getHalfspace().euclideanDistance(point.
get()))
260 <<
" to capture all points that are supposed to lie on the facet.");
261 halfspace.offset() = product;
264 pointsOnFacet.push_back(pointId);
267template<
class SparseModelType,
typename GeometryValueType>
268std::vector<typename DeterministicSchedsParetoExplorer<SparseModelType, GeometryValueType>::PointId>
const&
270 return pointsOnFacet;
273template<
class SparseModelType,
typename GeometryValueType>
275 return pointsOnFacet.size();
278template<
class SparseModelType,
typename GeometryValueType>
281 std::vector<GeometryValueType>
const& referenceCoordinates) {
282 std::vector<std::vector<GeometryValueType>> vertices = {referenceCoordinates};
283 for (
auto const& pId : pointsOnFacet) {
289 "The number of points on the facet is insufficient");
290 if (dimensionsForDownwardClosure.
empty()) {
297template<
class SparseModelType,
typename GeometryValueType>
300 : model(preprocessorResult.preprocessedModel), objectives(preprocessorResult.objectives) {
301 originalModelInitialState = *preprocessorResult.
originalModel.getInitialStates().begin();
302 objectiveHelper.reserve(objectives.size());
303 for (
auto const& obj : objectives) {
304 objectiveHelper.emplace_back(*model, obj);
305 STORM_LOG_ASSERT(!objectiveHelper.back().hasThreshold(),
"Unexpected input: got a Pareto query with a thresholded objective.");
307 lpChecker = std::make_shared<DeterministicSchedsLpChecker<SparseModelType, GeometryValueType>>(*model, objectiveHelper);
315template<
class SparseModelType,
typename GeometryValueType>
318 for (
auto& obj : objectiveHelper) {
319 obj.computeLowerUpperBounds(env);
321 initializeFacets(env);
325 std::vector<GeometryValueType> pmax, pmin;
326 for (
auto const& point : pointset) {
327 auto const& coordinates = point.second.get();
328 if (pmax.empty() && pmin.empty()) {
332 for (uint64_t i = 0; i < pmax.size(); ++i) {
333 pmax[i] = std::max(pmax[i], coordinates[i]);
334 pmin[i] = std::min(pmin[i], coordinates[i]);
339 epsScalingFactor += epsScalingFactor;
341 for (uint64_t i = 0; i < pmax.size(); ++i) {
342 eps.push_back((pmax[i] - pmin[i]) * epsScalingFactor);
343 if (eps.back() < storm::utility::convertNumber<GeometryValueType>(1e-8)) {
345 << i <<
" to 1e-8 since the difference between the highest and lowest value is below 1e-8.");
346 eps.back() = storm::utility::convertNumber<GeometryValueType>(1e-8);
349 STORM_LOG_INFO(
"Relative precision for deterministic scheduler Pareto explorer is "
353 storm::exceptions::IllegalArgumentException,
"Unknown multiobjective precision type.");
356 eps = std::vector<GeometryValueType>(objectives.size(), ei);
358 while (!unprocessedFacets.empty()) {
359 Facet f = std::move(unprocessedFacets.front());
360 unprocessedFacets.pop();
361 processFacet(env, f);
364 std::vector<std::vector<ModelValueType>> paretoPoints;
365 paretoPoints.reserve(pointset.size());
366 for (
auto const& p : pointset) {
367 if (p.second.isParetoOptimal()) {
368 paretoPoints.push_back(
372 return std::make_unique<storm::modelchecker::ExplicitParetoCurveCheckResult<ModelValueType>>(originalModelInitialState, std::move(paretoPoints),
nullptr,
376template<
class SparseModelType,
typename GeometryValueType>
378 pointset = Pointset();
379 unprocessedFacets = std::queue<Facet>();
381 unachievableAreas.clear();
384template<
class SparseModelType,
typename GeometryValueType>
385void DeterministicSchedsParetoExplorer<SparseModelType, GeometryValueType>::addHalfspaceToOverApproximation(
Environment const& env,
386 std::vector<GeometryValueType>
const& normalVector,
387 GeometryValueType
const& offset) {
389 std::cout <<
"## overapproximation halfspace: [";
391 for (
auto const& xi : normalVector) {
397 std::cout << storm::utility::convertNumber<double>(xi);
399 std::cout <<
"];[" << storm::utility::convertNumber<double>(offset) <<
"]\n";
402 overApproximation = overApproximation->intersection(overApproxHalfspace);
405template<
class SparseModelType,
typename GeometryValueType>
406void DeterministicSchedsParetoExplorer<SparseModelType, GeometryValueType>::addUnachievableArea(Environment
const& env, Polytope
const& area) {
407 if (env.modelchecker().multi().isPrintResultsSet()) {
408 std::vector<std::vector<GeometryValueType>> vertices;
409 if (objectives.size() == 2) {
410 vertices = area->getVerticesInClockwiseOrder();
412 vertices = area->getVertices();
414 std::cout <<
"## unachievable polytope: ";
415 bool firstVertex =
true;
416 for (
auto const& v : vertices) {
423 bool firstEntry =
true;
424 for (
auto const& vi : v) {
430 std::cout << storm::utility::convertNumber<double>(vi);
436 unachievableAreas.push_back(area);
439template<
class SparseModelType,
typename GeometryValueType>
441DeterministicSchedsParetoExplorer<SparseModelType, GeometryValueType>::negateMinObjectives(Polytope
const& polytope)
const {
442 std::vector<GeometryValueType> zeroRow(objectives.size(), storm::utility::zero<GeometryValueType>());
443 std::vector<std::vector<GeometryValueType>> transformationMatrix(objectives.size(), zeroRow);
444 for (uint64_t objIndex = 0; objIndex < objectives.size(); ++objIndex) {
445 if (objectiveHelper[objIndex].minimizing()) {
446 transformationMatrix[objIndex][objIndex] = -storm::utility::one<GeometryValueType>();
448 transformationMatrix[objIndex][objIndex] = storm::utility::one<GeometryValueType>();
451 return polytope->affineTransformation(transformationMatrix, zeroRow);
454template<
class SparseModelType,
typename GeometryValueType>
455void DeterministicSchedsParetoExplorer<SparseModelType, GeometryValueType>::negateMinObjectives(std::vector<GeometryValueType>& vector)
const {
456 for (uint64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
457 if (objectiveHelper[objIndex].minimizing()) {
458 vector[objIndex] *= -storm::utility::one<GeometryValueType>();
463template<
class SparseModelType,
typename GeometryValueType>
464void DeterministicSchedsParetoExplorer<SparseModelType, GeometryValueType>::initializeFacets(Environment
const& env) {
465 for (uint64_t objIndex = 0; objIndex < objectives.size(); ++objIndex) {
466 std::vector<GeometryValueType> weightVector(objectives.size(), storm::utility::zero<GeometryValueType>());
467 weightVector[objIndex] = storm::utility::one<GeometryValueType>();
468 std::vector<GeometryValueType> pointCoord;
469 GeometryValueType offset;
471 wvChecker->check(env, storm::utility::vector::convertNumericVector<ModelValueType>(weightVector));
472 pointCoord = storm::utility::vector::convertNumericVector<GeometryValueType>(wvChecker->getUnderApproximationOfInitialStateResults());
473 negateMinObjectives(pointCoord);
474 auto upperBoundPoint = storm::utility::vector::convertNumericVector<GeometryValueType>(wvChecker->getOverApproximationOfInitialStateResults());
475 negateMinObjectives(upperBoundPoint);
478 lpChecker->setCurrentWeightVector(env, weightVector);
479 auto optionalPoint = lpChecker->check(env, overApproximation);
480 STORM_LOG_THROW(optionalPoint.has_value(), storm::exceptions::UnexpectedException,
"Unable to find a point in the current overapproximation.");
481 pointCoord = std::move(optionalPoint->first);
482 offset = std::move(optionalPoint->second);
487 addHalfspaceToOverApproximation(env, weightVector, offset);
488 pointset.addPoint(env, std::move(p));
491 auto initialHalfspaces = pointset.downwardClosure()->getHalfspaces();
492 for (
auto& h : initialHalfspaces) {
493 Facet f(std::move(h));
494 for (
auto const& p : pointset) {
495 if (f.getHalfspace().isPointOnBoundary(p.second.get())) {
496 f.addPoint(p.first, p.second);
499 STORM_LOG_ASSERT(std::count(f.getHalfspace().normalVector().begin(), f.getHalfspace().normalVector().end(), storm::utility::zero<GeometryValueType>()) +
500 f.getNumberOfPoints() >=
502 "Not enough points on facet.");
504 unprocessedFacets.push(std::move(f));
508template<
class SparseModelType,
typename GeometryValueType>
509std::vector<GeometryValueType> DeterministicSchedsParetoExplorer<SparseModelType, GeometryValueType>::getReferenceCoordinates(Environment
const& env)
const {
510 std::vector<GeometryValueType> result;
511 for (uint64_t objIndex = 0; objIndex < objectives.size(); ++objIndex) {
513 storm::utility::convertNumber<GeometryValueType>(objectiveHelper[objIndex].getLowerValueBoundAtState(*model->getInitialStates().begin())));
518template<
class SparseModelType,
typename GeometryValueType>
519void DeterministicSchedsParetoExplorer<SparseModelType, GeometryValueType>::processFacet(Environment
const& env, Facet& f) {
521 lpChecker->setCurrentWeightVector(env, f.getHalfspace().normalVector());
524 if (optimizeAndSplitFacet(env, f)) {
529 for (
auto const& point : pointset) {
530 polytopeTree.substractDownwardClosure(point.second.get(), eps);
531 if (polytopeTree.isEmpty()) {
535 if (!polytopeTree.isEmpty()) {
537 lpChecker->setCurrentWeightVector(env, f.getHalfspace().normalVector());
539 auto res = lpChecker->check(env, polytopeTree, eps);
540 for (
auto const& infeasableArea : res.second) {
541 addUnachievableArea(env, infeasableArea);
543 for (
auto& achievablePoint : res.first) {
544 pointset.addPoint(env, Point(std::move(achievablePoint)));
549template<
typename GeometryValueType>
550bool closePoints(std::vector<GeometryValueType>
const& first, std::vector<GeometryValueType>
const& second, GeometryValueType
const& maxDistance) {
551 for (uint64_t i = 0; i < first.size(); ++i) {
552 if (storm::utility::abs<GeometryValueType>(first[i] - second[i]) > maxDistance) {
559template<
class SparseModelType,
typename GeometryValueType>
560bool DeterministicSchedsParetoExplorer<SparseModelType, GeometryValueType>::optimizeAndSplitFacet(
Environment const& env, Facet& f) {
562 boost::optional<PointId> optPointId;
563 std::vector<GeometryValueType> pointCoord;
564 GeometryValueType offset;
566 wvChecker->check(env, storm::utility::vector::convertNumericVector<ModelValueType>(f.getHalfspace().normalVector()));
567 pointCoord = storm::utility::vector::convertNumericVector<GeometryValueType>(wvChecker->getUnderApproximationOfInitialStateResults());
568 negateMinObjectives(pointCoord);
569 auto upperBoundPoint = storm::utility::vector::convertNumericVector<GeometryValueType>(wvChecker->getOverApproximationOfInitialStateResults());
570 negateMinObjectives(upperBoundPoint);
573 auto currentArea = overApproximation->intersection(f.getHalfspace().invert());
574 auto optionalPoint = lpChecker->check(env, overApproximation, eps);
575 if (optionalPoint.has_value()) {
576 pointCoord = std::move(optionalPoint->first);
579 pointCoord = pointset.getPoint(f.getPoints().front()).get();
581 offset = std::move(optionalPoint->second);
586 addHalfspaceToOverApproximation(env, f.getHalfspace().normalVector(), offset);
587 optPointId = pointset.addPoint(env, std::move(p));
591 auto const& optPoint = pointset.getPoint(*optPointId);
592 if (f.getHalfspace().contains(optPoint.get())) {
599 std::vector<std::vector<GeometryValueType>> vertices;
600 vertices.push_back(optPoint.get());
602 minmaxPrec += minmaxPrec;
603 for (
auto const& pId : f.getPoints()) {
604 vertices.push_back(pointset.getPoint(pId).get());
605 remainingArea.substractDownwardClosure(vertices.back(), eps);
607 "Found Pareto optimal points that are close to each other. This can be due to numerical issues. Maybe try exact mode?");
609 if (remainingArea.isEmpty()) {
617 for (
auto& h : newHalfspaceCandidates) {
619 STORM_LOG_ASSERT(h.isPointOnBoundary(optPoint.get()),
"Unexpected facet found while splitting.");
620 Facet fNew(std::move(h));
621 fNew.addPoint(optPointId.get(), optPoint);
622 auto vertexIt = vertices.begin();
624 for (
auto const& pId : f.getPoints()) {
625 assert(pointset.getPoint(pId).get() == *vertexIt);
626 if (fNew.getHalfspace().isPointOnBoundary(*vertexIt)) {
627 fNew.addPoint(pId, pointset.getPoint(pId));
631 assert(vertexIt == vertices.end());
632 unprocessedFacets.push(std::move(fNew));
643template<
class SparseModelType,
typename GeometryValueType>
SolverEnvironment & solver()
ModelCheckerEnvironment & modelchecker()
storm::RationalNumber const & getPrecision() const
MultiObjectiveModelCheckerEnvironment & multi()
@ RelativeToDiff
Absolute precision.
bool isPrintResultsSet() const
storm::RationalNumber const & getPrecision() const
PrecisionType const & getPrecisionType() const
MinMaxSolverEnvironment & minMax()
void addPoint(PointId const &pointId, Point const &point)
std::vector< PointId > const & getPoints() const
uint64_t getNumberOfPoints() const
storm::storage::geometry::Halfspace< GeometryValueType > const & getHalfspace() const
Polytope getInducedPolytope(Pointset const &pointset, std::vector< GeometryValueType > const &referenceCoordinates)
Creates a polytope that captures all points that lie 'under' the facet.
Facet(storm::storage::geometry::Halfspace< GeometryValueType > const &halfspace)
DominanceResult getDominance(Point const &other) const
std::string toString(bool convertToDouble=false) const
std::vector< GeometryValueType > const & get() const
uint64_t dimension() const
void setParetoOptimal(bool value=true)
void setOnFacet(bool value=true)
Point(std::vector< GeometryValueType > const &coordinates)
bool isParetoOptimal() const
std::map< PointId, Point >::const_iterator iterator_type
iterator_type end() const
void printToStream(std::ostream &out, bool includeIDs=true, bool convertToDouble=false)
Polytope downwardClosure() const
Returns the downward closure of the contained points.
Point const & getPoint(PointId const &id) const
Returns the point with the given ID.
void collectPointsInPolytope(std::set< PointId > &collectedPoints, Polytope const &polytope)
iterator_type begin() const
uint64_t size() const
Returns the number of points currently contained in the set.
boost::optional< PointId > addPoint(Environment const &env, Point &&point)
If the given point is not dominated by another point in the set, it is added to the set and its ID is...
Implements the exploration of the Pareto front.
virtual std::unique_ptr< CheckResult > check(Environment const &env)
void exportPlotOfCurrentApproximation(Environment const &env)
DeterministicSchedsParetoExplorer(preprocessing::SparseMultiObjectivePreprocessorResult< SparseModelType > &preprocessorResult)
std::shared_ptr< storm::storage::geometry::Polytope< GeometryValueType > > Polytope
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.
bool empty() const
Retrieves whether no bits are set to true in this bit vector.
uint_fast64_t getNumberOfSetBits() const
Returns the number of bits that are set to true in this bit vector.
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.
static std::shared_ptr< Polytope< ValueType > > create(std::vector< Halfspace< ValueType > > const &halfspaces)
Creates a polytope from the given halfspaces.
static std::shared_ptr< Polytope< ValueType > > createUniversalPolytope()
Creates the universal polytope (i.e., the set R^n)
static std::shared_ptr< Polytope< ValueType > > createSelectiveDownwardClosure(std::vector< Point > const &points, storm::storage::BitVector const &selectedDimensions)
Creates the downward closure of the given points but only with respect to the selected dimensions,...
Represents a set of points in Euclidean space.
#define STORM_LOG_INFO(message)
#define STORM_LOG_WARN(message)
#define STORM_LOG_DEBUG(message)
#define STORM_LOG_ASSERT(cond, message)
#define STORM_LOG_WARN_COND(cond, message)
#define STORM_LOG_THROW(cond, exception, message)
std::vector< GeometryValueType > transformObjectiveValuesToOriginal(std::vector< Objective< ValueType > > objectives, std::vector< GeometryValueType > const &point)
bool closePoints(std::vector< GeometryValueType > const &first, std::vector< GeometryValueType > const &second, GeometryValueType const &maxDistance)
T dotProduct(std::vector< T > const &firstOperand, std::vector< T > const &secondOperand)
Computes the dot product (aka scalar product) and returns the result.
std::string toString(std::vector< ValueType > const &vector)
Output vector as string.
bool hasNegativeEntry(std::vector< T > const &v)
storm::storage::BitVector filterZero(std::vector< T > const &values)
Retrieves a bit vector containing all the indices for which the value at this position is equal to ze...
SparseModelType const & originalModel
bool containsOnlyTotalRewardFormulas() const