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->setWeightedPrecision(storm::utility::convertNumber<ModelValueType>(env.solver().minMax().getPrecision()));
472 wvChecker->check(env, storm::utility::vector::convertNumericVector<ModelValueType>(weightVector));
473 pointCoord = storm::utility::vector::convertNumericVector<GeometryValueType>(wvChecker->getAchievablePoint());
474 negateMinObjectives(pointCoord);
475 offset = storm::utility::convertNumber<GeometryValueType>(wvChecker->getOptimalWeightedSum());
477 lpChecker->setCurrentWeightVector(env, weightVector);
478 auto optionalPoint = lpChecker->check(env, overApproximation);
479 STORM_LOG_THROW(optionalPoint.has_value(), storm::exceptions::UnexpectedException,
"Unable to find a point in the current overapproximation.");
480 pointCoord = std::move(optionalPoint->first);
481 offset = std::move(optionalPoint->second);
486 addHalfspaceToOverApproximation(env, weightVector, offset);
487 pointset.addPoint(env, std::move(p));
490 auto initialHalfspaces = pointset.downwardClosure()->getHalfspaces();
491 for (
auto& h : initialHalfspaces) {
492 Facet f(std::move(h));
493 for (
auto const& p : pointset) {
494 if (f.getHalfspace().isPointOnBoundary(p.second.get())) {
495 f.addPoint(p.first, p.second);
498 STORM_LOG_ASSERT(std::count(f.getHalfspace().normalVector().begin(), f.getHalfspace().normalVector().end(), storm::utility::zero<GeometryValueType>()) +
499 f.getNumberOfPoints() >=
501 "Not enough points on facet.");
503 unprocessedFacets.push(std::move(f));
507template<
class SparseModelType,
typename GeometryValueType>
508std::vector<GeometryValueType> DeterministicSchedsParetoExplorer<SparseModelType, GeometryValueType>::getReferenceCoordinates(Environment
const& env)
const {
509 std::vector<GeometryValueType> result;
510 for (uint64_t objIndex = 0; objIndex < objectives.size(); ++objIndex) {
512 storm::utility::convertNumber<GeometryValueType>(objectiveHelper[objIndex].getLowerValueBoundAtState(*model->getInitialStates().begin())));
517template<
class SparseModelType,
typename GeometryValueType>
518void DeterministicSchedsParetoExplorer<SparseModelType, GeometryValueType>::processFacet(Environment
const& env, Facet& f) {
520 lpChecker->setCurrentWeightVector(env, f.getHalfspace().normalVector());
523 if (optimizeAndSplitFacet(env, f)) {
528 for (
auto const& point : pointset) {
529 polytopeTree.substractDownwardClosure(point.second.get(), eps);
530 if (polytopeTree.isEmpty()) {
534 if (!polytopeTree.isEmpty()) {
536 lpChecker->setCurrentWeightVector(env, f.getHalfspace().normalVector());
538 auto res = lpChecker->check(env, polytopeTree, eps);
539 for (
auto const& infeasableArea : res.second) {
540 addUnachievableArea(env, infeasableArea);
542 for (
auto& achievablePoint : res.first) {
543 pointset.addPoint(env, Point(std::move(achievablePoint)));
548template<
typename GeometryValueType>
549bool closePoints(std::vector<GeometryValueType>
const& first, std::vector<GeometryValueType>
const& second, GeometryValueType
const& maxDistance) {
550 for (uint64_t i = 0; i < first.size(); ++i) {
551 if (storm::utility::abs<GeometryValueType>(first[i] - second[i]) > maxDistance) {
558template<
class SparseModelType,
typename GeometryValueType>
559bool DeterministicSchedsParetoExplorer<SparseModelType, GeometryValueType>::optimizeAndSplitFacet(
Environment const& env, Facet& f) {
561 boost::optional<PointId> optPointId;
562 std::vector<GeometryValueType> pointCoord;
563 GeometryValueType offset;
565 wvChecker->setWeightedPrecision(storm::utility::convertNumber<ModelValueType>(env.
solver().
minMax().
getPrecision()));
566 wvChecker->check(env, storm::utility::vector::convertNumericVector<ModelValueType>(f.getHalfspace().normalVector()));
567 pointCoord = storm::utility::vector::convertNumericVector<GeometryValueType>(wvChecker->getAchievablePoint());
568 negateMinObjectives(pointCoord);
569 offset = storm::utility::convertNumber<GeometryValueType>(wvChecker->getOptimalWeightedSum());
571 auto currentArea = overApproximation->intersection(f.getHalfspace().invert());
572 auto optionalPoint = lpChecker->check(env, overApproximation, eps);
573 if (optionalPoint.has_value()) {
574 pointCoord = std::move(optionalPoint->first);
577 pointCoord = pointset.getPoint(f.getPoints().front()).get();
579 offset = std::move(optionalPoint->second);
584 addHalfspaceToOverApproximation(env, f.getHalfspace().normalVector(), offset);
585 optPointId = pointset.addPoint(env, std::move(p));
589 auto const& optPoint = pointset.getPoint(*optPointId);
590 if (f.getHalfspace().contains(optPoint.get())) {
597 std::vector<std::vector<GeometryValueType>> vertices;
598 vertices.push_back(optPoint.get());
600 minmaxPrec += minmaxPrec;
601 for (
auto const& pId : f.getPoints()) {
602 vertices.push_back(pointset.getPoint(pId).get());
603 remainingArea.substractDownwardClosure(vertices.back(), eps);
605 "Found Pareto optimal points that are close to each other. This can be due to numerical issues. Maybe try exact mode?");
607 if (remainingArea.isEmpty()) {
615 for (
auto& h : newHalfspaceCandidates) {
617 STORM_LOG_ASSERT(h.isPointOnBoundary(optPoint.get()),
"Unexpected facet found while splitting.");
618 Facet fNew(std::move(h));
619 fNew.addPoint(optPointId.get(), optPoint);
620 auto vertexIt = vertices.begin();
622 for (
auto const& pId : f.getPoints()) {
623 assert(pointset.getPoint(pId).get() == *vertexIt);
624 if (fNew.getHalfspace().isPointOnBoundary(*vertexIt)) {
625 fNew.addPoint(pId, pointset.getPoint(pId));
629 assert(vertexIt == vertices.end());
630 unprocessedFacets.push(std::move(fNew));
641template<
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
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.
uint64_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::unique_ptr< PcaaWeightVectorChecker< ModelType > > createWeightVectorChecker(preprocessing::SparseMultiObjectivePreprocessorResult< ModelType > const &preprocessorResult)
bool closePoints(std::vector< GeometryValueType > const &first, std::vector< GeometryValueType > const &second, GeometryValueType const &maxDistance)
std::vector< GeometryValueType > transformObjectiveValuesToOriginal(std::vector< Objective< ValueType > > const &objectives, std::vector< GeometryValueType > const &point)
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