26template<
class SparseModelType,
typename GeometryValueType>
28 : coordinates(coordinates), onFacet(false), paretoOptimal(false) {
29 STORM_LOG_ASSERT(!this->coordinates.empty(),
"Points with dimension 0 are not supported.");
32template<
class SparseModelType,
typename GeometryValueType>
34 : coordinates(
std::move(coordinates)), onFacet(false), paretoOptimal(false) {
35 STORM_LOG_ASSERT(!this->coordinates.empty(),
"Points with dimension 0 are not supported.");
38template<
class SparseModelType,
typename GeometryValueType>
43template<
class SparseModelType,
typename GeometryValueType>
48template<
class SparseModelType,
typename GeometryValueType>
50 STORM_LOG_ASSERT(!coordinates.empty(),
"Points with dimension 0 are not supported.");
51 return coordinates.size();
54template<
class SparseModelType,
typename GeometryValueType>
58 auto thisIt = this->
get().begin();
59 auto otherIt = other.
get().begin();
60 auto thisItE = this->
get().end();
63 while (*thisIt == *otherIt) {
66 if (thisIt == thisItE) {
71 if (*thisIt > *otherIt) {
73 for (++thisIt, ++otherIt; thisIt != thisItE; ++thisIt, ++otherIt) {
74 if (*thisIt < *otherIt) {
80 STORM_LOG_ASSERT(*thisIt < *otherIt,
"Expected iterator value less than other.");
82 for (++thisIt, ++otherIt; thisIt != thisItE; ++thisIt, ++otherIt) {
83 if (*thisIt > *otherIt) {
91template<
class SparseModelType,
typename GeometryValueType>
96template<
class SparseModelType,
typename GeometryValueType>
101template<
class SparseModelType,
typename GeometryValueType>
103 paretoOptimal = value;
106template<
class SparseModelType,
typename GeometryValueType>
108 return paretoOptimal;
111template<
class SparseModelType,
typename GeometryValueType>
113 std::stringstream out;
115 for (
auto const& pi : this->
get()) {
121 if (convertToDouble) {
122 out << storm::utility::convertNumber<double>(pi);
130template<
class SparseModelType,
typename GeometryValueType>
135template<
class SparseModelType,
typename GeometryValueType>
136boost::optional<typename DeterministicSchedsParetoExplorer<SparseModelType, GeometryValueType>::PointId>
139 auto pointsIt = points.begin();
140 while (pointsIt != points.end()) {
141 switch (point.getDominance(pointsIt->second)) {
147 if (pointsIt->second.liesOnFacet()) {
149 pointsIt->second.setParetoOptimal(
false);
152 pointsIt = points.erase(pointsIt);
159 if (point.liesOnFacet()) {
160 pointsIt->second.setOnFacet();
162 return pointsIt->first;
166 point.setParetoOptimal(
true);
169 std::cout <<
"## achievable point: [" << point.toString(
true) <<
"]\n";
171 points.emplace_hint(points.end(), currId, std::move(point));
175template<
class SparseModelType,
typename GeometryValueType>
178 return points.at(
id);
181template<
class SparseModelType,
typename GeometryValueType>
184 return points.begin();
187template<
class SparseModelType,
typename GeometryValueType>
193template<
class SparseModelType,
typename GeometryValueType>
195 return points.size();
198template<
class SparseModelType,
typename GeometryValueType>
201 std::vector<std::vector<GeometryValueType>> pointsAsVector;
202 pointsAsVector.reserve(
size());
203 for (
auto const& p : points) {
204 pointsAsVector.push_back(p.second.get());
209template<
class SparseModelType,
typename GeometryValueType>
212 for (
auto const& p : points) {
213 if (polytope->contains(p.second.get())) {
214 collectedPoints.insert(p.first);
219template<
class SparseModelType,
typename GeometryValueType>
221 for (
auto const& p : this->points) {
223 out << p.first <<
": [" << p.second.toString(convertToDouble) <<
"]\n";
225 out << p.second.toString(convertToDouble) <<
'\n';
230template<
class SparseModelType,
typename GeometryValueType>
232 : halfspace(halfspace) {
236template<
class SparseModelType,
typename GeometryValueType>
238 : halfspace(
std::move(halfspace)) {
242template<
class SparseModelType,
typename GeometryValueType>
248template<
class SparseModelType,
typename GeometryValueType>
257 <<
" to capture all points that are supposed to lie on the facet.");
258 halfspace.offset() = product;
261 pointsOnFacet.push_back(pointId);
264template<
class SparseModelType,
typename GeometryValueType>
265std::vector<typename DeterministicSchedsParetoExplorer<SparseModelType, GeometryValueType>::PointId>
const&
267 return pointsOnFacet;
270template<
class SparseModelType,
typename GeometryValueType>
272 return pointsOnFacet.size();
275template<
class SparseModelType,
typename GeometryValueType>
278 std::vector<GeometryValueType>
const& referenceCoordinates) {
279 std::vector<std::vector<GeometryValueType>> vertices = {referenceCoordinates};
280 for (
auto const& pId : pointsOnFacet) {
281 vertices.push_back(pointset.getPoint(pId).get());
286 "The number of points on the facet is insufficient.");
287 if (dimensionsForDownwardClosure.
empty()) {
294template<
class SparseModelType,
typename GeometryValueType>
297 : model(preprocessorResult.preprocessedModel), objectives(preprocessorResult.objectives) {
298 originalModelInitialState = *preprocessorResult.
originalModel.getInitialStates().begin();
299 objectiveHelper.reserve(objectives.size());
300 for (
auto const& obj : objectives) {
301 objectiveHelper.emplace_back(*model, obj);
302 STORM_LOG_ASSERT(!objectiveHelper.back().hasThreshold(),
"Unexpected input: got a Pareto query with a thresholded objective.");
304 lpChecker = std::make_shared<DeterministicSchedsLpChecker<SparseModelType, GeometryValueType>>(*model, objectiveHelper);
306 wvChecker = storm::modelchecker::multiobjective::createWeightVectorChecker(preprocessorResult);
312template<
class SparseModelType,
typename GeometryValueType>
315 for (
auto& obj : objectiveHelper) {
316 obj.computeLowerUpperBounds(env);
318 initializeFacets(env);
322 std::vector<GeometryValueType> pmax, pmin;
323 for (
auto const& point : pointset) {
324 auto const& coordinates = point.second.get();
325 if (pmax.empty() && pmin.empty()) {
329 for (uint64_t i = 0; i < pmax.size(); ++i) {
330 pmax[i] = std::max(pmax[i], coordinates[i]);
331 pmin[i] = std::min(pmin[i], coordinates[i]);
336 epsScalingFactor += epsScalingFactor;
338 for (uint64_t i = 0; i < pmax.size(); ++i) {
339 eps.push_back((pmax[i] - pmin[i]) * epsScalingFactor);
342 << i <<
" to 1e-8 since the difference between the highest and lowest value is below 1e-8.");
346 STORM_LOG_INFO(
"Relative precision for deterministic scheduler Pareto explorer is "
350 storm::exceptions::IllegalArgumentException,
"Unknown multiobjective precision type.");
353 eps = std::vector<GeometryValueType>(objectives.size(), ei);
355 while (!unprocessedFacets.empty()) {
356 Facet f = std::move(unprocessedFacets.front());
357 unprocessedFacets.pop();
358 processFacet(env, f);
361 std::vector<std::vector<ModelValueType>> paretoPoints;
362 paretoPoints.reserve(pointset.size());
363 for (
auto const& p : pointset) {
364 if (p.second.isParetoOptimal()) {
365 paretoPoints.push_back(
369 return std::make_unique<storm::modelchecker::ExplicitParetoCurveCheckResult<ModelValueType>>(originalModelInitialState, std::move(paretoPoints),
nullptr,
373template<
class SparseModelType,
typename GeometryValueType>
374void DeterministicSchedsParetoExplorer<SparseModelType, GeometryValueType>::clean() {
375 pointset = Pointset();
376 unprocessedFacets = std::queue<Facet>();
378 unachievableAreas.clear();
381template<
class SparseModelType,
typename GeometryValueType>
382void DeterministicSchedsParetoExplorer<SparseModelType, GeometryValueType>::addHalfspaceToOverApproximation(
Environment const& env,
383 std::vector<GeometryValueType>
const& normalVector,
384 GeometryValueType
const& offset) {
386 std::cout <<
"## overapproximation halfspace: [";
388 for (
auto const& xi : normalVector) {
394 std::cout << storm::utility::convertNumber<double>(xi);
398 storm::storage::geometry::Halfspace<GeometryValueType> overApproxHalfspace(normalVector, offset);
399 overApproximation = overApproximation->intersection(overApproxHalfspace);
402template<
class SparseModelType,
typename GeometryValueType>
403void DeterministicSchedsParetoExplorer<SparseModelType, GeometryValueType>::addUnachievableArea(Environment
const& env, Polytope
const& area) {
404 if (env.modelchecker().multi().isPrintResultsSet()) {
405 std::vector<std::vector<GeometryValueType>> vertices;
406 if (objectives.size() == 2) {
407 vertices = area->getVerticesInClockwiseOrder();
409 vertices = area->getVertices();
411 std::cout <<
"## unachievable polytope: ";
412 bool firstVertex =
true;
413 for (
auto const& v : vertices) {
420 bool firstEntry =
true;
421 for (
auto const& vi : v) {
427 std::cout << storm::utility::convertNumber<double>(vi);
433 unachievableAreas.push_back(area);
436template<
class SparseModelType,
typename GeometryValueType>
438DeterministicSchedsParetoExplorer<SparseModelType, GeometryValueType>::negateMinObjectives(Polytope
const& polytope)
const {
440 std::vector<std::vector<GeometryValueType>> transformationMatrix(objectives.size(), zeroRow);
441 for (uint64_t objIndex = 0; objIndex < objectives.size(); ++objIndex) {
442 if (objectiveHelper[objIndex].minimizing()) {
448 return polytope->affineTransformation(transformationMatrix, zeroRow);
451template<
class SparseModelType,
typename GeometryValueType>
452void DeterministicSchedsParetoExplorer<SparseModelType, GeometryValueType>::negateMinObjectives(std::vector<GeometryValueType>& vector)
const {
453 for (uint64_t objIndex = 0; objIndex < this->objectives.size(); ++objIndex) {
454 if (objectiveHelper[objIndex].minimizing()) {
460template<
class SparseModelType,
typename GeometryValueType>
461void DeterministicSchedsParetoExplorer<SparseModelType, GeometryValueType>::initializeFacets(Environment
const& env) {
462 for (uint64_t objIndex = 0; objIndex < objectives.size(); ++objIndex) {
465 std::vector<GeometryValueType> pointCoord;
466 GeometryValueType offset;
471 negateMinObjectives(pointCoord);
474 lpChecker->setCurrentWeightVector(env, weightVector);
475 auto optionalPoint = lpChecker->check(env, overApproximation);
476 STORM_LOG_THROW(optionalPoint.has_value(), storm::exceptions::UnexpectedException,
"Unable to find a point in the current overapproximation.");
477 pointCoord = std::move(optionalPoint->first);
478 offset = std::move(optionalPoint->second);
483 addHalfspaceToOverApproximation(env, weightVector, offset);
484 pointset.addPoint(env, std::move(p));
487 auto initialHalfspaces = pointset.downwardClosure()->getHalfspaces();
488 for (
auto& h : initialHalfspaces) {
489 Facet f(std::move(h));
490 for (
auto const& p : pointset) {
491 if (f.getHalfspace().isPointOnBoundary(p.second.get())) {
492 f.addPoint(p.first, p.second);
496 f.getNumberOfPoints() >=
498 "Not enough points on facet.");
500 unprocessedFacets.push(std::move(f));
504template<
class SparseModelType,
typename GeometryValueType>
505std::vector<GeometryValueType> DeterministicSchedsParetoExplorer<SparseModelType, GeometryValueType>::getReferenceCoordinates(Environment
const& env)
const {
506 std::vector<GeometryValueType> result;
507 for (uint64_t objIndex = 0; objIndex < objectives.size(); ++objIndex) {
514template<
class SparseModelType,
typename GeometryValueType>
515void DeterministicSchedsParetoExplorer<SparseModelType, GeometryValueType>::processFacet(Environment
const& env, Facet& f) {
517 lpChecker->setCurrentWeightVector(env, f.getHalfspace().normalVector());
520 if (optimizeAndSplitFacet(env, f)) {
524 storm::storage::geometry::PolytopeTree<GeometryValueType> polytopeTree(f.getInducedPolytope(pointset, getReferenceCoordinates(env)));
525 for (
auto const& point : pointset) {
526 polytopeTree.substractDownwardClosure(point.second.get(), eps);
527 if (polytopeTree.isEmpty()) {
531 if (!polytopeTree.isEmpty()) {
533 lpChecker->setCurrentWeightVector(env, f.getHalfspace().normalVector());
535 auto res = lpChecker->check(env, polytopeTree, eps);
536 for (
auto const& infeasableArea : res.second) {
537 addUnachievableArea(env, infeasableArea);
539 for (
auto& achievablePoint : res.first) {
540 pointset.addPoint(env, Point(std::move(achievablePoint)));
545template<
typename GeometryValueType>
546bool closePoints(std::vector<GeometryValueType>
const& first, std::vector<GeometryValueType>
const& second, GeometryValueType
const& maxDistance) {
547 for (uint64_t i = 0; i < first.size(); ++i) {
555template<
class SparseModelType,
typename GeometryValueType>
556bool DeterministicSchedsParetoExplorer<SparseModelType, GeometryValueType>::optimizeAndSplitFacet(
Environment const& env, Facet& f) {
558 boost::optional<PointId> optPointId;
559 std::vector<GeometryValueType> pointCoord;
560 GeometryValueType offset;
565 negateMinObjectives(pointCoord);
568 auto currentArea = overApproximation->intersection(f.getHalfspace().invert());
569 auto optionalPoint = lpChecker->check(env, overApproximation, eps);
570 if (optionalPoint.has_value()) {
571 pointCoord = std::move(optionalPoint->first);
574 pointCoord = pointset.getPoint(f.getPoints().front()).get();
576 offset = std::move(optionalPoint->second);
581 addHalfspaceToOverApproximation(env, f.getHalfspace().normalVector(), offset);
582 optPointId = pointset.addPoint(env, std::move(p));
586 auto const& optPoint = pointset.getPoint(*optPointId);
587 if (f.getHalfspace().contains(optPoint.get())) {
593 storm::storage::geometry::PolytopeTree<GeometryValueType> remainingArea(overApproximation->intersection(f.getHalfspace().invert()));
594 std::vector<std::vector<GeometryValueType>> vertices;
595 vertices.push_back(optPoint.get());
597 minmaxPrec += minmaxPrec;
598 for (
auto const& pId : f.getPoints()) {
599 vertices.push_back(pointset.getPoint(pId).get());
600 remainingArea.substractDownwardClosure(vertices.back(), eps);
602 "Found Pareto optimal points that are close to each other. This can be due to numerical issues. Maybe try exact mode?");
604 if (remainingArea.isEmpty()) {
612 for (
auto& h : newHalfspaceCandidates) {
614 STORM_LOG_ASSERT(h.isPointOnBoundary(optPoint.get()),
"Unexpected facet found while splitting.");
615 Facet fNew(std::move(h));
616 fNew.addPoint(optPointId.get(), optPoint);
617 auto vertexIt = vertices.begin();
619 for (
auto const& pId : f.getPoints()) {
620 STORM_LOG_ASSERT(pointset.getPoint(pId).get() == *vertexIt,
"Vertex mismatch.");
621 if (fNew.getHalfspace().isPointOnBoundary(*vertexIt)) {
622 fNew.addPoint(pId, pointset.getPoint(pId));
626 STORM_LOG_ASSERT(vertexIt == vertices.end(),
"Vertex iterator not at end.");
627 unprocessedFacets.push(std::move(fNew));
638template<
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,...
#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)
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)
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...
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...
ValueType abs(ValueType const &number)
TargetType convertNumber(SourceType const &number)
SparseModelType const & originalModel
bool containsOnlyTotalRewardFormulas() const