Browse Source
started implementing deterministic scheduler finding approach for multi-objective model checking
tempestpy_adaptions
started implementing deterministic scheduler finding approach for multi-objective model checking
tempestpy_adaptions
TimQu
7 years ago
6 changed files with 714 additions and 0 deletions
-
73src/storm/modelchecker/multiobjective/MultiObjectivePostprocessing.cpp
-
21src/storm/modelchecker/multiobjective/MultiObjectivePostprocessing.h
-
384src/storm/modelchecker/multiobjective/deterministicScheds/DeterministicParetoExplorer.cpp
-
174src/storm/modelchecker/multiobjective/deterministicScheds/DeterministicParetoExplorer.h
-
32src/storm/modelchecker/multiobjective/deterministicScheds/MultiObjectiveSchedulerEvaluator.cpp
-
30src/storm/modelchecker/multiobjective/deterministicScheds/MultiObjectiveSchedulerEvaluator.h
@ -0,0 +1,73 @@ |
|||||
|
#include "storm/modelchecker/multiobjective/MultiObjectivePostprocessing.h"
|
||||
|
|
||||
|
#include "storm/adapters/RationalNumberAdapter.h"
|
||||
|
|
||||
|
namespace storm { |
||||
|
namespace modelchecker { |
||||
|
namespace multiobjective { |
||||
|
|
||||
|
template<typename ValueType, typename GeometryValueType> |
||||
|
std::vector<GeometryValueType> transformObjectiveValuesToOriginal(std::vector<Objective<ValueType>> objectives, std::vector<GeometryValueType> const& point) { |
||||
|
std::vector<GeometryValueType> result; |
||||
|
result.reserve(point.size()); |
||||
|
for (uint_fast64_t objIndex = 0; objIndex < objectives.size(); ++objIndex) { |
||||
|
auto const& obj = objectives[objIndex]; |
||||
|
if (storm::solver::maximize(obj.formula->getOptimalityType())) { |
||||
|
if (obj.considersComplementaryEvent) { |
||||
|
result.push_back(storm::utility::one<GeometryValueType>() - point[objIndex]); |
||||
|
} else { |
||||
|
result.push_back(point[objIndex]); |
||||
|
} |
||||
|
} else { |
||||
|
if (obj.considersComplementaryEvent) { |
||||
|
result.push_back(storm::utility::one<GeometryValueType>() + point[objIndex]); |
||||
|
} else { |
||||
|
result.push_back(-point[objIndex]); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
template<typename ValueType, typename GeometryValueType> |
||||
|
std::shared_ptr<storm::storage::geometry::Polytope<GeometryValueType>> transformObjectivePolytopeToOriginal(std::vector<Objective<ValueType>> objectives, std::shared_ptr<storm::storage::geometry::Polytope<GeometryValueType>> const& polytope) { |
||||
|
if(polytope->isEmpty()) { |
||||
|
return storm::storage::geometry::Polytope<GeometryValueType>::createEmptyPolytope(); |
||||
|
} |
||||
|
if(polytope->isUniversal()) { |
||||
|
return storm::storage::geometry::Polytope<GeometryValueType>::createUniversalPolytope(); |
||||
|
} |
||||
|
uint_fast64_t numObjectives = objectives.size(); |
||||
|
std::vector<std::vector<GeometryValueType>> transformationMatrix(numObjectives, std::vector<GeometryValueType>(numObjectives, storm::utility::zero<GeometryValueType>())); |
||||
|
std::vector<GeometryValueType> transformationVector; |
||||
|
transformationVector.reserve(numObjectives); |
||||
|
for(uint_fast64_t objIndex = 0; objIndex < numObjectives; ++objIndex) { |
||||
|
auto const& obj = objectives[objIndex]; |
||||
|
if (storm::solver::maximize(obj.formula->getOptimalityType())) { |
||||
|
if (obj.considersComplementaryEvent) { |
||||
|
transformationMatrix[objIndex][objIndex] = -storm::utility::one<GeometryValueType>(); |
||||
|
transformationVector.push_back(storm::utility::one<GeometryValueType>()); |
||||
|
} else { |
||||
|
transformationMatrix[objIndex][objIndex] = storm::utility::one<GeometryValueType>(); |
||||
|
transformationVector.push_back(storm::utility::zero<GeometryValueType>()); |
||||
|
} |
||||
|
} else { |
||||
|
if (obj.considersComplementaryEvent) { |
||||
|
transformationMatrix[objIndex][objIndex] = storm::utility::one<GeometryValueType>(); |
||||
|
transformationVector.push_back(storm::utility::one<GeometryValueType>()); |
||||
|
} else { |
||||
|
transformationMatrix[objIndex][objIndex] = -storm::utility::one<GeometryValueType>(); |
||||
|
transformationVector.push_back(storm::utility::zero<GeometryValueType>()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return polytope->affineTransformation(transformationMatrix, transformationVector); |
||||
|
} |
||||
|
|
||||
|
template std::vector<storm::RationalNumber> transformObjectiveValuesToOriginal(std::vector<Objective<double>> objectives, std::vector<storm::RationalNumber> const& point); |
||||
|
template std::shared_ptr<storm::storage::geometry::Polytope<storm::RationalNumber>> transformObjectivePolytopeToOriginal(std::vector<Objective<double>> objectives, std::shared_ptr<storm::storage::geometry::Polytope<storm::RationalNumber>> const& polytope); |
||||
|
template std::vector<storm::RationalNumber> transformObjectiveValuesToOriginal(std::vector<Objective<storm::RationalNumber>> objectives, std::vector<storm::RationalNumber> const& point); |
||||
|
template std::shared_ptr<storm::storage::geometry::Polytope<storm::RationalNumber>> transformObjectivePolytopeToOriginal(std::vector<Objective<storm::RationalNumber>> objectives, std::shared_ptr<storm::storage::geometry::Polytope<storm::RationalNumber>> const& polytope); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,21 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <vector> |
||||
|
#include <memory> |
||||
|
|
||||
|
#include "storm/modelchecker/multiobjective/Objective.h" |
||||
|
#include "storm/storage/geometry/Polytope.h" |
||||
|
|
||||
|
namespace storm { |
||||
|
namespace modelchecker { |
||||
|
namespace multiobjective { |
||||
|
|
||||
|
template<typename ValueType, typename GeometryValueType> |
||||
|
std::vector<GeometryValueType> transformObjectiveValuesToOriginal(std::vector<Objective<ValueType>> objectives, std::vector<GeometryValueType> const& point); |
||||
|
|
||||
|
template<typename ValueType, typename GeometryValueType> |
||||
|
std::shared_ptr<storm::storage::geometry::Polytope<GeometryValueType>> transformObjectivePolytopeToOriginal(std::vector<Objective<ValueType>> objectives, std::shared_ptr<storm::storage::geometry::Polytope<GeometryValueType>> const& polytope); |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,384 @@ |
|||||
|
#include <sstream>
|
||||
|
#include <storm/models/sparse/MarkovAutomaton.h>
|
||||
|
|
||||
|
#include "storm/modelchecker/multiobjective/deterministicScheds/DeterministicParetoExplorer.h"
|
||||
|
#include "storm/models/sparse/MarkovAutomaton.h"
|
||||
|
#include "storm/models/sparse/Mdp.h"
|
||||
|
#include "storm/models/sparse/StandardRewardModel.h"
|
||||
|
|
||||
|
|
||||
|
#include "storm/utility/export.h"
|
||||
|
|
||||
|
#include "storm/exceptions/InvalidOperationException.h"
|
||||
|
|
||||
|
|
||||
|
namespace storm { |
||||
|
namespace modelchecker { |
||||
|
namespace multiobjective { |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
DeterministicParetoExplorer<SparseModelType, GeometryValueType>::Point::Point(std::vector<GeometryValueType> const& coordinates) : coordinates(coordinates), paretoOptimal(false) { |
||||
|
STORM_LOG_ASSERT(!this->coordinates.empty(), "Points with dimension 0 are not supported"); |
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
DeterministicParetoExplorer<SparseModelType, GeometryValueType>::Point::Point(std::vector<GeometryValueType>&& coordinates) : coordinates(std::move(coordinates)), paretoOptimal(false) { |
||||
|
STORM_LOG_ASSERT(!this->coordinates.empty(), "Points with dimension 0 are not supported"); |
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
std::vector<GeometryValueType>& DeterministicParetoExplorer<SparseModelType, GeometryValueType>::Point::get() { |
||||
|
return coordinates; |
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
std::vector<GeometryValueType> const& DeterministicParetoExplorer<SparseModelType, GeometryValueType>::Point::get() const { |
||||
|
return coordinates; |
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
uint64_t DeterministicParetoExplorer<SparseModelType, GeometryValueType>::Point::dimension() const { |
||||
|
STORM_LOG_ASSERT(!coordinates.empty(), "Points with dimension 0 are not supported"); |
||||
|
return coordinates.size(); |
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
typename DeterministicParetoExplorer<SparseModelType, GeometryValueType>::Point::DominanceResult DeterministicParetoExplorer<SparseModelType, GeometryValueType>::Point::getDominance(Point const& other) const { |
||||
|
STORM_LOG_ASSERT(this->dimension() == other.dimension(), "Non-Equal dimensions of points: [" << this->toString() << "] vs. [" << other.toString() << "]"); |
||||
|
auto thisIt = this->get().begin(); |
||||
|
auto otherIt = other.get().begin(); |
||||
|
auto thisItE = this->get().end(); |
||||
|
|
||||
|
// Find the first entry where the points differ
|
||||
|
while (*thisIt == *otherIt) { |
||||
|
++thisIt; |
||||
|
++otherIt; |
||||
|
if (thisIt == thisItE) { |
||||
|
return DominanceResult::Equal; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (*thisIt > *otherIt) { |
||||
|
// *this might dominate other
|
||||
|
for (++thisIt, ++otherIt; thisIt != thisItE; ++thisIt, ++otherIt) { |
||||
|
if (*thisIt < *otherIt) { |
||||
|
return DominanceResult::Incomparable; |
||||
|
} |
||||
|
} |
||||
|
return DominanceResult::Dominates; |
||||
|
} else { |
||||
|
assert(*thisIt < *otherIt); |
||||
|
// *this might be dominated by other
|
||||
|
for (++thisIt, ++otherIt; thisIt != thisItE; ++thisIt, ++otherIt) { |
||||
|
if (*thisIt > *otherIt) { |
||||
|
return DominanceResult::Incomparable; |
||||
|
} |
||||
|
} |
||||
|
return DominanceResult::Dominated; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
void DeterministicParetoExplorer<SparseModelType, GeometryValueType>::Point::setParetoOptimal(bool value) { |
||||
|
paretoOptimal = value; |
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
bool DeterministicParetoExplorer<SparseModelType, GeometryValueType>::Point::isParetoOptimal() const { |
||||
|
return paretoOptimal; |
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
std::string DeterministicParetoExplorer<SparseModelType, GeometryValueType>::Point::toString(bool convertToDouble) const { |
||||
|
std::stringstream out; |
||||
|
bool first = true; |
||||
|
for (auto const& pi : this->get()) { |
||||
|
if (first) { |
||||
|
first = false; |
||||
|
} else { |
||||
|
out << ", "; |
||||
|
} |
||||
|
if (convertToDouble) { |
||||
|
out << pi; |
||||
|
} else { |
||||
|
out << pi; |
||||
|
} |
||||
|
} |
||||
|
return out.str(); |
||||
|
} |
||||
|
|
||||
|
// template <class SparseModelType, typename GeometryValueType>
|
||||
|
// bool operator<(typename DeterministicParetoExplorer<SparseModelType, GeometryValueType>::Point const& lhs, typename DeterministicParetoExplorer<SparseModelType, GeometryValueType>::Point const& rhs) {
|
||||
|
// STORM_LOG_ASSERT(lhs.dimension() == rhs.dimension(), "Non-Equal dimensions of points: " << lhs << " vs. " << rhs);
|
||||
|
// for (uint64_t i = 0; i < lhs.dimension(); ++i) {
|
||||
|
// if (lhs.get()[i] < rhs.get()[i]) {
|
||||
|
// return true;
|
||||
|
// } else if (lhs.get()[i] != rhs.get()[i]) {
|
||||
|
// return false;
|
||||
|
// }
|
||||
|
// }
|
||||
|
// return false;
|
||||
|
// }
|
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
DeterministicParetoExplorer<SparseModelType, GeometryValueType>::Pointset::Pointset() : currId(1) { |
||||
|
// Intentionally left empty
|
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
boost::optional<typename DeterministicParetoExplorer<SparseModelType, GeometryValueType>::PointId> DeterministicParetoExplorer<SparseModelType, GeometryValueType>::Pointset::addPoint(Point const& point) { |
||||
|
|
||||
|
// Find dominated and dominating points
|
||||
|
auto pointsIt = points.begin(); |
||||
|
auto pointsItE = points.end(); |
||||
|
while (pointsIt != points.end()) { |
||||
|
switch (point.getDominance(pointsIt->second)) { |
||||
|
case Point::DominanceResult::Incomparable: |
||||
|
// Nothing to be done for this point
|
||||
|
++pointsIt; |
||||
|
case Point::DominanceResult::Dominates: |
||||
|
// Found a point in the set that is dominated by the new point, so we erase it
|
||||
|
pointsIt = points.erase(pointsIt); |
||||
|
case Point::DominanceResult::Dominated: |
||||
|
// The new point is dominated by another point.
|
||||
|
return boost::none; |
||||
|
case Point::DominanceResult::Equal: |
||||
|
if (point.isParetoOptimal()) { |
||||
|
pointsIt->second.setParetoOptimal(); |
||||
|
} |
||||
|
return pointsIt->first; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
points.emplace_hint(points.end(), currId, point); |
||||
|
return currId++; |
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
typename DeterministicParetoExplorer<SparseModelType, GeometryValueType>::Point const& DeterministicParetoExplorer<SparseModelType, GeometryValueType>::Pointset::getPoint(PointId const& id) const { |
||||
|
return points.at(id); |
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
uint64_t DeterministicParetoExplorer<SparseModelType, GeometryValueType>::Pointset::size() const { |
||||
|
return points.size(); |
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
void DeterministicParetoExplorer<SparseModelType, GeometryValueType>::Pointset::collectPointsInPolytope(std::set<PointId>& collectedPoints, Polytope const& polytope) { |
||||
|
for (auto const& p : points) { |
||||
|
if (polytope->contains(p.second.get())) { |
||||
|
collectedPoints.insert(p.first); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
void DeterministicParetoExplorer<SparseModelType, GeometryValueType>::Pointset::printToStream(std::ostream& out, bool includeIDs, bool convertToDouble) { |
||||
|
for (auto const& p : this->points) { |
||||
|
if (includeIDs) { |
||||
|
out << p.first << ": [" << p.second.toString(convertToDouble) << "]" << std::endl; |
||||
|
} else { |
||||
|
out << p.second.toString(convertToDouble) << std::endl; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
DeterministicParetoExplorer<SparseModelType, GeometryValueType>::Facet::Facet(storm::storage::geometry::Halfspace<GeometryValueType> const& halfspace) : halfspace(halfspace) { |
||||
|
// Intentionally left empty
|
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
DeterministicParetoExplorer<SparseModelType, GeometryValueType>::Facet::Facet(storm::storage::geometry::Halfspace<GeometryValueType>&& halfspace) : halfspace(std::move(halfspace)) { |
||||
|
// Intentionally left empty
|
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
void DeterministicParetoExplorer<SparseModelType, GeometryValueType>::Facet::addPoint(PointId const& pointId) { |
||||
|
paretoPointsOnFacet.push_back(pointId); |
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
std::vector<typename DeterministicParetoExplorer<SparseModelType, GeometryValueType>::PointId> const& DeterministicParetoExplorer<SparseModelType, GeometryValueType>::Facet::getPoints() const { |
||||
|
return paretoPointsOnFacet; |
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
uint64_t DeterministicParetoExplorer<SparseModelType, GeometryValueType>::Facet::getNumberOfPoints() const { |
||||
|
return paretoPointsOnFacet.size(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
typename DeterministicParetoExplorer<SparseModelType, GeometryValueType>::Polytope DeterministicParetoExplorer<SparseModelType, GeometryValueType>::Facet::getInducedSimplex(Pointset const& pointset) const { |
||||
|
std::vector<std::vector<GeometryValueType>> vertices; |
||||
|
STORM_LOG_ASSERT(getNumberOfPoints() > 0, "Tried to compute the induced simplex, but not enough points are given."); |
||||
|
auto pointIdIt = paretoPointsOnFacet.begin(); |
||||
|
auto pointIdItE = paretoPointsOnFacet.end(); |
||||
|
vertices.push_back(pointset.getPoint(*pointIdIt).get()); |
||||
|
std::vector<GeometryValueType> minPoint = vertices.back(); |
||||
|
|
||||
|
for (++pointIdIt; pointIdIt != pointIdItE; ++pointIdIt) { |
||||
|
vertices.push_back(pointset.getPoint(*pointIdIt).get()); |
||||
|
auto pIt = vertices.back().begin(); |
||||
|
for (auto& mi : minPoint) { |
||||
|
mi = std::min(mi, *pIt); |
||||
|
++pIt; |
||||
|
} |
||||
|
} |
||||
|
vertices.push_back(std::move(minPoint)); |
||||
|
|
||||
|
// This facet might lie at the 'border', which means that the downward closure has to be taken in some directions
|
||||
|
storm::storage::BitVector dimensionsForDownwardClosure = storm::utility::vector::filterZero(this->halfspace.normalVector()); |
||||
|
STORM_LOG_ASSERT(dimensionsForDownwardClosure.getNumberOfSetBits() + vertices.size() >= halfspace.normalVector().size() + 1, "The number of points on the facet is insufficient"); |
||||
|
if (dimensionsForDownwardClosure.empty()) { |
||||
|
return storm::storage::geometry::Polytope<GeometryValueType>::create(vertices); |
||||
|
} else { |
||||
|
return storm::storage::geometry::Polytope<GeometryValueType>::createSelectiveDownwardClosure(vertices, dimensionsForDownwardClosure); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
DeterministicParetoExplorer<SparseModelType, GeometryValueType>::DeterministicParetoExplorer(preprocessing::SparseMultiObjectivePreprocessorResult<SparseModelType>& preprocessorResult) { |
||||
|
// Intentionally left empty
|
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
std::unique_ptr<CheckResult> DeterministicParetoExplorer<SparseModelType, GeometryValueType>::check(Environment const& env) { |
||||
|
|
||||
|
clean(); |
||||
|
initializeFacets(env); |
||||
|
while (!unprocessedFacets.empty()) { |
||||
|
Facet f = std::move(unprocessedFacets.front()); |
||||
|
unprocessedFacets.pop(); |
||||
|
processFacet(env, f); |
||||
|
} |
||||
|
|
||||
|
// Todo: Prepare check result
|
||||
|
return nullptr; |
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
void DeterministicParetoExplorer<SparseModelType, GeometryValueType>::clean() { |
||||
|
pointset = Pointset(); |
||||
|
unprocessedFacets = std::queue<Facet>(); |
||||
|
overApproximation = storm::storage::geometry::Polytope<GeometryValueType>::createUniversalPolytope(); |
||||
|
unachievableAreas.clear(); |
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
void DeterministicParetoExplorer<SparseModelType, GeometryValueType>::initializeFacets(Environment const& env) { |
||||
|
// TODO
|
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
bool DeterministicParetoExplorer<SparseModelType, GeometryValueType>::checkFacetPrecision(Environment const& env, Facet const& f) { |
||||
|
// TODO
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
void DeterministicParetoExplorer<SparseModelType, GeometryValueType>::processFacet(Environment const& env, Facet const& f) { |
||||
|
// TODO
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
bool DeterministicParetoExplorer<SparseModelType, GeometryValueType>::optimizeAndSplitFacet(Environment const& env, Facet const& f) { |
||||
|
// TODO
|
||||
|
return false; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
bool DeterministicParetoExplorer<SparseModelType, GeometryValueType>::findAndCheckCachedPoints(Environment const& env, Facet const& f, Polytope const& inducedSimplex, std::set<PointId>& collectedPoints) { |
||||
|
// TODO
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
bool DeterministicParetoExplorer<SparseModelType, GeometryValueType>::analyzePointsOnFacet(Environment const& env, Facet const& f, Polytope const& inducedSimplex, std::set<PointId>& collectedPoints) { |
||||
|
|
||||
|
// TODO
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
bool DeterministicParetoExplorer<SparseModelType, GeometryValueType>::analyzePointsInSimplex(Environment const& env, Facet const& f, Polytope const& inducedSimplex, std::set<PointId>& collectedPoints) { |
||||
|
|
||||
|
// TODO
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
void DeterministicParetoExplorer<SparseModelType, GeometryValueType>::exportPlotOfCurrentApproximation(Environment const& env) { |
||||
|
/*
|
||||
|
STORM_LOG_ERROR_COND(objectives.size()==2, "Exporting plot requested but this is only implemented for the two-dimensional case."); |
||||
|
|
||||
|
auto transformedUnderApprox = transformPolytopeToOriginalModel(underApproximation); |
||||
|
auto transformedOverApprox = transformPolytopeToOriginalModel(overApproximation); |
||||
|
|
||||
|
// Get pareto points as well as a hyperrectangle that is used to guarantee that the resulting polytopes are bounded.
|
||||
|
storm::storage::geometry::Hyperrectangle<GeometryValueType> boundaries(std::vector<GeometryValueType>(objectives.size(), storm::utility::zero<GeometryValueType>()), std::vector<GeometryValueType>(objectives.size(), storm::utility::zero<GeometryValueType>())); |
||||
|
std::vector<std::vector<GeometryValueType>> paretoPoints; |
||||
|
paretoPoints.reserve(refinementSteps.size()); |
||||
|
for(auto const& step : refinementSteps) { |
||||
|
paretoPoints.push_back(transformPointToOriginalModel(step.lowerBoundPoint)); |
||||
|
boundaries.enlarge(paretoPoints.back()); |
||||
|
} |
||||
|
auto underApproxVertices = transformedUnderApprox->getVertices(); |
||||
|
for(auto const& v : underApproxVertices) { |
||||
|
boundaries.enlarge(v); |
||||
|
} |
||||
|
auto overApproxVertices = transformedOverApprox->getVertices(); |
||||
|
for(auto const& v : overApproxVertices) { |
||||
|
boundaries.enlarge(v); |
||||
|
} |
||||
|
|
||||
|
//Further enlarge the boundaries a little
|
||||
|
storm::utility::vector::scaleVectorInPlace(boundaries.lowerBounds(), GeometryValueType(15) / GeometryValueType(10)); |
||||
|
storm::utility::vector::scaleVectorInPlace(boundaries.upperBounds(), GeometryValueType(15) / GeometryValueType(10)); |
||||
|
|
||||
|
auto boundariesAsPolytope = boundaries.asPolytope(); |
||||
|
std::vector<std::string> columnHeaders = {"x", "y"}; |
||||
|
|
||||
|
std::vector<std::vector<double>> pointsForPlotting; |
||||
|
if (env.modelchecker().multi().getPlotPathUnderApproximation()) { |
||||
|
underApproxVertices = transformedUnderApprox->intersection(boundariesAsPolytope)->getVerticesInClockwiseOrder(); |
||||
|
pointsForPlotting.reserve(underApproxVertices.size()); |
||||
|
for(auto const& v : underApproxVertices) { |
||||
|
pointsForPlotting.push_back(storm::utility::vector::convertNumericVector<double>(v)); |
||||
|
} |
||||
|
storm::utility::exportDataToCSVFile<double, std::string>(env.modelchecker().multi().getPlotPathUnderApproximation().get(), pointsForPlotting, columnHeaders); |
||||
|
} |
||||
|
|
||||
|
if (env.modelchecker().multi().getPlotPathOverApproximation()) { |
||||
|
pointsForPlotting.clear(); |
||||
|
overApproxVertices = transformedOverApprox->intersection(boundariesAsPolytope)->getVerticesInClockwiseOrder(); |
||||
|
pointsForPlotting.reserve(overApproxVertices.size()); |
||||
|
for(auto const& v : overApproxVertices) { |
||||
|
pointsForPlotting.push_back(storm::utility::vector::convertNumericVector<double>(v)); |
||||
|
} |
||||
|
storm::utility::exportDataToCSVFile<double, std::string>(env.modelchecker().multi().getPlotPathOverApproximation().get(), pointsForPlotting, columnHeaders); |
||||
|
} |
||||
|
|
||||
|
if (env.modelchecker().multi().getPlotPathParetoPoints()) { |
||||
|
pointsForPlotting.clear(); |
||||
|
pointsForPlotting.reserve(paretoPoints.size()); |
||||
|
for(auto const& v : paretoPoints) { |
||||
|
pointsForPlotting.push_back(storm::utility::vector::convertNumericVector<double>(v)); |
||||
|
} |
||||
|
storm::utility::exportDataToCSVFile<double, std::string>(env.modelchecker().multi().getPlotPathParetoPoints().get(), pointsForPlotting, columnHeaders); |
||||
|
} |
||||
|
}; |
||||
|
*/ |
||||
|
} |
||||
|
|
||||
|
template class DeterministicParetoExplorer<storm::models::sparse::Mdp<double>, storm::RationalNumber>; |
||||
|
template class DeterministicParetoExplorer<storm::models::sparse::Mdp<storm::RationalNumber>, storm::RationalNumber>; |
||||
|
template class DeterministicParetoExplorer<storm::models::sparse::MarkovAutomaton<double>, storm::RationalNumber>; |
||||
|
template class DeterministicParetoExplorer<storm::models::sparse::MarkovAutomaton<storm::RationalNumber>, storm::RationalNumber>; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,174 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <memory> |
||||
|
|
||||
|
#include "storm/modelchecker/multiobjective/preprocessing/SparseMultiObjectivePreprocessorResult.h" |
||||
|
#include "storm/storage/geometry/Polytope.h" |
||||
|
#include "storm/storage/geometry/Halfspace.h" |
||||
|
#include "storm/modelchecker/results/CheckResult.h" |
||||
|
|
||||
|
namespace storm { |
||||
|
|
||||
|
class Environment; |
||||
|
|
||||
|
namespace modelchecker { |
||||
|
namespace multiobjective { |
||||
|
|
||||
|
template <class SparseModelType, typename GeometryValueType> |
||||
|
class DeterministicParetoExplorer { |
||||
|
public: |
||||
|
typedef uint64_t PointId; |
||||
|
typedef typename std::shared_ptr<storm::storage::geometry::Polytope<GeometryValueType>> Polytope; |
||||
|
|
||||
|
class Point { |
||||
|
public: |
||||
|
Point(std::vector<GeometryValueType> const& coordinates); |
||||
|
Point(std::vector<GeometryValueType>&& coordinates); |
||||
|
|
||||
|
std::vector<GeometryValueType> const& get() const; |
||||
|
std::vector<GeometryValueType>& get(); |
||||
|
|
||||
|
uint64_t dimension() const; |
||||
|
|
||||
|
enum class DominanceResult { |
||||
|
Incomparable, |
||||
|
Dominates, |
||||
|
Dominated, |
||||
|
Equal |
||||
|
}; |
||||
|
DominanceResult getDominance(Point const& other) const; |
||||
|
|
||||
|
void setParetoOptimal(bool value = true); |
||||
|
bool isParetoOptimal() const; |
||||
|
|
||||
|
std::string toString(bool convertToDouble = false) const; |
||||
|
|
||||
|
private: |
||||
|
std::vector<GeometryValueType> coordinates; |
||||
|
bool paretoOptimal; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
class Pointset { |
||||
|
public: |
||||
|
Pointset(); |
||||
|
|
||||
|
/*! |
||||
|
* If the given point is not dominated by another point in the set, it is added |
||||
|
* to the set and its ID is returned. |
||||
|
* If the point is dominated by another point, boost::none is returned. |
||||
|
* Erases all points in the set, that are dominated by the given point. |
||||
|
* If the same point is already contained in the set, its id is returned |
||||
|
*/ |
||||
|
boost::optional<PointId> addPoint(Point const& point); |
||||
|
|
||||
|
/*! |
||||
|
* Returns the point with the given ID |
||||
|
*/ |
||||
|
Point const& getPoint(PointId const& id) const; |
||||
|
|
||||
|
/*! |
||||
|
* Returns the number of points currently contained in the set |
||||
|
*/ |
||||
|
uint64_t size() const; |
||||
|
|
||||
|
void collectPointsInPolytope(std::set<PointId>& collectedPoints, Polytope const& polytope); |
||||
|
|
||||
|
void printToStream(std::ostream& out, bool includeIDs = true, bool convertToDouble = false); |
||||
|
|
||||
|
private: |
||||
|
std::map<PointId, Point> points; |
||||
|
PointId currId; |
||||
|
}; |
||||
|
|
||||
|
class Facet { |
||||
|
public: |
||||
|
Facet(storm::storage::geometry::Halfspace<GeometryValueType> const& halfspace); |
||||
|
Facet(storm::storage::geometry::Halfspace<GeometryValueType>&& halfspace); |
||||
|
void addPoint(PointId const& pointId); |
||||
|
std::vector<PointId> const& getPoints() const; |
||||
|
uint64_t getNumberOfPoints() const; |
||||
|
|
||||
|
/*! |
||||
|
* Creates a polytope that captures all points that lie 'under' the facet |
||||
|
* More precisely, the vertices of the polytope are the points on the facet |
||||
|
* and point p with p_i = min {x_i | x lies on facet} |
||||
|
*/ |
||||
|
Polytope getInducedSimplex(Pointset const& pointset) const; |
||||
|
|
||||
|
|
||||
|
|
||||
|
private: |
||||
|
storm::storage::geometry::Halfspace<GeometryValueType> halfspace; |
||||
|
std::vector<PointId> paretoPointsOnFacet; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
DeterministicParetoExplorer(preprocessing::SparseMultiObjectivePreprocessorResult<SparseModelType>& preprocessorResult); |
||||
|
|
||||
|
virtual std::unique_ptr<CheckResult> check(Environment const& env); |
||||
|
|
||||
|
void exportPlotOfCurrentApproximation(Environment const& env); |
||||
|
|
||||
|
private: |
||||
|
|
||||
|
/*! |
||||
|
* Cleans up all cached results from a previous check call |
||||
|
*/ |
||||
|
void clean(); |
||||
|
|
||||
|
/*! |
||||
|
* Builds the initial facets by optimizing the objectives individually. |
||||
|
* Adds the facets that need further processing to unprocessedFacets |
||||
|
*/ |
||||
|
void initializeFacets(Environment const& env); |
||||
|
|
||||
|
/*! |
||||
|
* Checks the precision of the given Facet and returns true, if no further processing of the facet is necessary |
||||
|
*/ |
||||
|
bool checkFacetPrecision(Environment const& env, Facet const& f); |
||||
|
|
||||
|
/*! Processes the given facet as follows: |
||||
|
* 1. Optimize in the facet direction. Potentially, this adds new, unprocessed facets |
||||
|
* 2. Find points that have already been collected so far such that they lie in the induced simplex of the facet. |
||||
|
* 3. Find more points that lie on the facet |
||||
|
* 4. Find all points that lie in the induced simplex or prove that there are none |
||||
|
*/ |
||||
|
void processFacet(Environment const& env, Facet const& f); |
||||
|
|
||||
|
/*! |
||||
|
* Optimizes in the facet direction. If this results in a point that does not lie on the facet, |
||||
|
* 1. The new Pareto optimal point is added |
||||
|
* 2. New facets are generated and (if not already precise enough) added to unprocessedFacets |
||||
|
* 3. true is returned |
||||
|
*/ |
||||
|
bool optimizeAndSplitFacet(Environment const& env, Facet const& f); |
||||
|
|
||||
|
/*! |
||||
|
* Finds all points that lie within the induced Simplex of the given facet. |
||||
|
* Returns true if the facet is sufficiently precise when considering all added points |
||||
|
*/ |
||||
|
bool findAndCheckCachedPoints(Environment const& env, Facet const& f, Polytope const& inducedSimplex, std::set<PointId>& collectedPoints); |
||||
|
|
||||
|
/*! |
||||
|
* Finds points that lie on the facet |
||||
|
* Returns true if the facet has been analyzed sufficiently precise. |
||||
|
* If false is returned, it means that *all* points that lie on the facet have been analyzed but the analysis is still not precise enough |
||||
|
* |
||||
|
* use smt to find an eps-box in the simplex that does not contain a point. Add new points until one in the box is found. repeat. |
||||
|
* stop when no more points or boxes can be found. |
||||
|
*/ |
||||
|
bool analyzePointsOnFacet(Environment const& env, Facet const& f, Polytope const& inducedSimplex, std::set<PointId>& collectedPoints); |
||||
|
|
||||
|
bool analyzePointsInSimplex(Environment const& env, Facet const& f, Polytope const& inducedSimplex, std::set<PointId>& collectedPoints); |
||||
|
|
||||
|
Pointset pointset; |
||||
|
std::queue<Facet> unprocessedFacets; |
||||
|
Polytope overApproximation; |
||||
|
std::vector<Polytope> unachievableAreas; |
||||
|
|
||||
|
}; |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,32 @@ |
|||||
|
#include "storm/modelchecker/multiobjective/deterministicScheds/MultiObjectiveSchedulerEvaluator.h"
|
||||
|
|
||||
|
namespace storm { |
||||
|
namespace modelchecker { |
||||
|
namespace multiobjective { |
||||
|
|
||||
|
template <class ModelType> |
||||
|
MultiObjectiveSchedulerEvaluator<ModelType>::MultiObjectiveSchedulerEvaluator(preprocessing::SparseMultiObjectivePreprocessorResult<ModelType>& preprocessorResult) { |
||||
|
// TODO
|
||||
|
} |
||||
|
|
||||
|
template <class ModelType> |
||||
|
void MultiObjectiveSchedulerEvaluator<ModelType>::check(std::vector<uint64_t> const& scheduler) { |
||||
|
// TODO
|
||||
|
} |
||||
|
|
||||
|
template <class ModelType> |
||||
|
std::vector<typename MultiObjectiveSchedulerEvaluator<ModelType>::ValueType> const& MultiObjectiveSchedulerEvaluator<ModelType>::getResultForObjective(uint64_t objIndex) const { |
||||
|
return results[objIndex]; |
||||
|
} |
||||
|
|
||||
|
template <class ModelType> |
||||
|
std::vector<ValueType> MultiObjectiveSchedulerEvaluator<ModelType>::getInitialStateResults() const { |
||||
|
std::vector<ValueType> res; |
||||
|
for (auto objResult : results) { |
||||
|
res.push_back(objResult[initialState]); |
||||
|
} |
||||
|
return res; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,30 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <vector> |
||||
|
|
||||
|
#include "storm/modelchecker/multiobjective/preprocessing/SparseMultiObjectivePreprocessorResult.h" |
||||
|
|
||||
|
namespace storm { |
||||
|
namespace modelchecker { |
||||
|
namespace multiobjective { |
||||
|
|
||||
|
template <class ModelType> |
||||
|
class MultiObjectiveSchedulerEvaluator { |
||||
|
public: |
||||
|
|
||||
|
typedef typename ModelType::ValueType ValueType; |
||||
|
|
||||
|
MultiObjectiveSchedulerEvaluator(preprocessing::SparseMultiObjectivePreprocessorResult<ModelType>& preprocessorResult); |
||||
|
|
||||
|
void check(std::vector<uint64_t> const& scheduler); |
||||
|
|
||||
|
std::vector<ValueType> const& getResultForObjective(uint64_t objIndex) const; |
||||
|
std::vector<ValueType> getInitialStateResults() const; |
||||
|
|
||||
|
private: |
||||
|
std::vector<std::vector<ValueType>> results; |
||||
|
uint64_t initialState; |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue