@ -12,7 +12,9 @@
# include "storm/environment/modelchecker/MultiObjectiveModelCheckerEnvironment.h"
# include "storm/utility/export.h"
# include "storm/utility/solver.h"
# include "storm/exceptions/UnexpectedException.h"
# include "storm/exceptions/InvalidOperationException.h"
@ -21,12 +23,12 @@ namespace storm {
namespace multiobjective {
template < class SparseModelType , typename GeometryValueType >
DeterministicParetoExplorer < SparseModelType , GeometryValueType > : : Point : : Point ( std : : vector < GeometryValueType > const & coordinates ) : coordinates ( coordinates ) , paretoOptimal ( false ) {
DeterministicParetoExplorer < SparseModelType , GeometryValueType > : : Point : : Point ( std : : vector < GeometryValueType > const & coordinates ) : coordinates ( coordinates ) , paretoOptimal ( false ) , onFacet ( 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 ) {
DeterministicParetoExplorer < SparseModelType , GeometryValueType > : : Point : : Point ( std : : vector < GeometryValueType > & & coordinates ) : coordinates ( std : : move ( coordinates ) ) , paretoOptimal ( false ) , onFacet ( false ) {
STORM_LOG_ASSERT ( ! this - > coordinates . empty ( ) , " Points with dimension 0 are not supported " ) ;
}
@ -92,6 +94,16 @@ namespace storm {
return paretoOptimal ;
}
template < class SparseModelType , typename GeometryValueType >
void DeterministicParetoExplorer < SparseModelType , GeometryValueType > : : Point : : setOnFacet ( bool value ) {
onFacet = value ;
}
template < class SparseModelType , typename GeometryValueType >
bool DeterministicParetoExplorer < SparseModelType , GeometryValueType > : : Point : : liesOnFacet ( ) const {
return onFacet ;
}
template < class SparseModelType , typename GeometryValueType >
std : : string DeterministicParetoExplorer < SparseModelType , GeometryValueType > : : Point : : toString ( bool convertToDouble ) const {
std : : stringstream out ;
@ -147,7 +159,12 @@ namespace storm {
STORM_LOG_WARN ( " Potential precision issues: Found a point that dominates another point which was flagged as pareto optimal. Distance of points is " < < std : : sqrt ( storm : : utility : : convertNumber < double > ( storm : : storage : : geometry : : squaredEuclideanDistance ( pointsIt - > second . get ( ) , point . get ( ) ) ) ) ) ;
point . setParetoOptimal ( true ) ;
}
if ( pointsIt - > second . liesOnFacet ( ) ) {
// Do not erase points that lie on a facet
+ + pointsIt ;
} else {
pointsIt = points . erase ( pointsIt ) ;
}
break ;
case Point : : DominanceResult : : Dominated :
// The new point is dominated by another point.
@ -156,6 +173,9 @@ namespace storm {
if ( point . isParetoOptimal ( ) ) {
pointsIt - > second . setParetoOptimal ( ) ;
}
if ( point . liesOnFacet ( ) ) {
pointsIt - > second . setOnFacet ( ) ;
}
return pointsIt - > first ;
}
}
@ -234,8 +254,17 @@ namespace storm {
}
template < class SparseModelType , typename GeometryValueType >
void DeterministicParetoExplorer < SparseModelType , GeometryValueType > : : Facet : : addPoint ( PointId const & pointId ) {
void DeterministicParetoExplorer < SparseModelType , GeometryValueType > : : Facet : : addPoint ( PointId const & pointId , Point const & point ) {
inducedSimplex = nullptr ;
GeometryValueType product = storm : : utility : : vector : : dotProduct ( getHalfspace ( ) . normalVector ( ) , point . get ( ) ) ;
if ( product ! = getHalfspace ( ) . offset ( ) ) {
if ( product < getHalfspace ( ) . offset ( ) ) {
STORM_LOG_DEBUG ( " The point on the facet actually has distance " < < storm : : utility : : convertNumber < double > ( getHalfspace ( ) . euclideanDistance ( point . get ( ) ) ) ) ;
} else {
STORM_LOG_DEBUG ( " Halfspace of facet is shifted by " < < storm : : utility : : convertNumber < double > ( getHalfspace ( ) . euclideanDistance ( point . get ( ) ) ) < < " to capture all points that are supposed to lie on the facet. " ) ;
halfspace . offset ( ) = product ;
}
}
paretoPointsOnFacet . push_back ( pointId ) ;
}
@ -282,6 +311,11 @@ namespace storm {
return inducedSimplex ;
}
template < class SparseModelType , typename GeometryValueType >
DeterministicParetoExplorer < SparseModelType , GeometryValueType > : : FacetAnalysisContext : : FacetAnalysisContext ( Facet & f ) : facet ( f ) {
// Intentionally left empty
}
template < class SparseModelType , typename GeometryValueType >
DeterministicParetoExplorer < SparseModelType , GeometryValueType > : : DeterministicParetoExplorer ( preprocessing : : SparseMultiObjectivePreprocessorResult < SparseModelType > & preprocessorResult ) : model ( preprocessorResult . preprocessedModel ) , objectives ( preprocessorResult . objectives ) {
originalModelInitialState = * preprocessorResult . originalModel . getInitialStates ( ) . begin ( ) ;
@ -391,7 +425,7 @@ namespace storm {
Point p ( storm : : utility : : vector : : convertNumericVector < GeometryValueType > ( * pIt ) ) ;
if ( last ) {
last = false ;
p . setParetoOptimal ( true ) ;
p . setOnFacet ( ) ;
// Adapt the overapproximation
std : : vector < GeometryValueType > normalVector ( objectives . size ( ) , storm : : utility : : zero < GeometryValueType > ( ) ) ;
normalVector [ objIndex ] = storm : : utility : : one < GeometryValueType > ( ) ;
@ -406,7 +440,7 @@ namespace storm {
Facet f ( std : : move ( h ) ) ;
for ( auto const & p : pointset ) {
if ( f . getHalfspace ( ) . isPointOnBoundary ( p . second . get ( ) ) ) {
f . addPoint ( p . first ) ;
f . addPoint ( p . first , p . second ) ;
}
}
STORM_LOG_ASSERT ( std : : count ( f . getHalfspace ( ) . normalVector ( ) . begin ( ) , f . getHalfspace ( ) . normalVector ( ) . end ( ) , storm : : utility : : zero < GeometryValueType > ( ) ) + f . getNumberOfPoints ( ) = = objectives . size ( ) , " Unexpected number of points on facet. " ) ;
@ -441,17 +475,18 @@ namespace storm {
if ( optimizeAndSplitFacet ( env , f ) ) {
return ;
}
std : : set < PointId > collectedPoints ;
if ( findAndCheckCachedPoints ( env , f , collectedPoints ) ) {
FacetAnalysisContext context = createAnalysisContext ( env , f ) ;
if ( findAndCheckCachedPoints ( env , context ) ) {
return ;
}
if ( analyzePointsOnFacet ( env , f , collectedPoints ) ) {
if ( analyzePointsOnFacet ( env , context ) ) {
return ;
}
if ( analyzePointsInSimplex ( env , f , collectedPoints ) ) {
if ( analyzePointsInSimplex ( env , context ) ) {
return ;
}
@ -459,10 +494,47 @@ namespace storm {
STORM_LOG_ERROR ( " Facet " < < f . getHalfspace ( ) . toString ( true ) < < " could not be analyzed completely. " ) ;
}
template < class SparseModelType , typename GeometryValueType >
typename DeterministicParetoExplorer < SparseModelType , GeometryValueType > : : FacetAnalysisContext DeterministicParetoExplorer < SparseModelType , GeometryValueType > : : createAnalysisContext ( Environment const & env , Facet & f ) {
FacetAnalysisContext res ( f ) ;
res . expressionManager = std : : make_shared < storm : : expressions : : ExpressionManager > ( ) ;
res . smtSolver = storm : : utility : : solver : : SmtSolverFactory ( ) . create ( * res . expressionManager ) ;
Polytope const & inducedPoly = res . facet . getInducedSimplex ( pointset ) ;
res . x = inducedPoly - > declareVariables ( * res . expressionManager , " x " ) ;
for ( auto const & c : inducedPoly - > getConstraints ( * res . expressionManager , res . x ) ) {
res . smtSolver - > add ( c ) ;
}
res . xMinusEps = inducedPoly - > declareVariables ( * res . expressionManager , " y " ) ;
for ( auto const & c : inducedPoly - > getConstraints ( * res . expressionManager , res . xMinusEps ) ) {
res . smtSolver - > add ( c ) ;
}
auto eps = res . expressionManager - > rational ( env . modelchecker ( ) . multi ( ) . getPrecision ( ) ) ;
storm : : expressions : : Expression xme ;
for ( uint64_t i = 0 ; i < res . x . size ( ) ; + + i ) {
storm : : expressions : : Expression subExpr = ( res . xMinusEps [ i ] . getExpression ( ) = = res . x [ i ] . getExpression ( ) - eps ) ;
if ( i = = 0 ) {
xme = subExpr ;
} else {
xme = xme & & subExpr ;
}
}
res . smtSolver - > add ( xme ) ;
res . smtSolver - > push ( ) ;
return res ;
}
template < class SparseModelType , typename GeometryValueType >
bool DeterministicParetoExplorer < SparseModelType , GeometryValueType > : : optimizeAndSplitFacet ( Environment const & env , Facet & f ) {
// Obtain the correct weight vector
auto weightVector = storm : : utility : : vector : : convertNumericVector < ModelValueType > ( f . getHalfspace ( ) . normalVector ( ) ) ;
bool weightVectorYieldsParetoOptimalPoint = ! storm : : utility : : vector : : hasZeroEntry ( weightVector ) ;
for ( uint_fast64_t objIndex = 0 ; objIndex < this - > objectives . size ( ) ; + + objIndex ) {
if ( storm : : solver : : minimize ( objectives [ objIndex ] . formula - > getOptimalityType ( ) ) ) {
weightVector [ objIndex ] * = - storm : : utility : : one < ModelValueType > ( ) ;
@ -482,7 +554,8 @@ namespace storm {
Point p ( storm : : utility : : vector : : convertNumericVector < GeometryValueType > ( * pIt ) ) ;
if ( last ) {
last = false ;
p . setParetoOptimal ( true ) ;
p . setParetoOptimal ( weightVectorYieldsParetoOptimalPoint ) ;
p . setOnFacet ( ) ;
addHalfspaceToOverApproximation ( env , f . getHalfspace ( ) . normalVector ( ) , p ) ;
optPointId = pointset . addPoint ( env , std : : move ( p ) ) ;
} else {
@ -508,13 +581,13 @@ namespace storm {
if ( ! storm : : utility : : vector : : hasNegativeEntry ( h . normalVector ( ) ) ) {
STORM_LOG_ASSERT ( h . isPointOnBoundary ( optPoint . get ( ) ) , " Unexpected facet found while splitting. " ) ;
Facet fNew ( std : : move ( h ) ) ;
fNew . addPoint ( optPointId . get ( ) ) ;
fNew . addPoint ( optPointId . get ( ) , optPoint ) ;
auto vertexIt = vertices . begin ( ) ;
+ + vertexIt ;
for ( auto const & pId : f . getPoints ( ) ) {
assert ( pointset . getPoint ( pId ) . get ( ) = = * vertexIt ) ;
if ( fNew . getHalfspace ( ) . isPointOnBoundary ( * vertexIt ) ) {
fNew . addPoint ( pId ) ;
fNew . addPoint ( pId , pointset . getPoint ( pId ) ) ;
}
+ + vertexIt ;
}
@ -533,20 +606,49 @@ namespace storm {
}
template < class SparseModelType , typename GeometryValueType >
bool DeterministicParetoExplorer < SparseModelType , GeometryValueType > : : findAndCheckCachedPoints ( Environment const & env , Facet & f , std : : set < PointId > & collectedPoints ) {
// TODO
bool DeterministicParetoExplorer < SparseModelType , GeometryValueType > : : findAndCheckCachedPoints ( Environment const & env , FacetAnalysisContext & context ) {
Polytope inducedPoly = context . facet . getInducedSimplex ( pointset ) ;
pointset . collectPointsInPolytope ( context . collectedPoints , inducedPoly ) ;
if ( context . collectedPoints . size ( ) ! = context . facet . getNumberOfPoints ( ) ) {
STORM_LOG_ASSERT ( context . collectedPoints . size ( ) > context . facet . getNumberOfPoints ( ) , " Did not find all points on the facet " ) ;
// formulate SMT constraints to assert that xMinusEps is not dominated by one of the cached points
for ( auto const & pId : context . collectedPoints ) {
auto const & coordinates = pointset . getPoint ( pId ) . get ( ) ;
storm : : expressions : : Expression pointAchievesXMinusEps ;
for ( uint64_t i = 0 ; i < coordinates . size ( ) ; + + i ) {
storm : : expressions : : Expression subExpr = context . xMinusEps [ i ] < = context . expressionManager - > rational ( coordinates [ i ] ) ;
if ( i = = 0 ) {
pointAchievesXMinusEps = subExpr ;
} else {
pointAchievesXMinusEps = pointAchievesXMinusEps & & subExpr ;
}
}
context . smtSolver - > add ( ! pointAchievesXMinusEps ) ;
}
context . smtSolver - > push ( ) ;
auto smtCheckResult = context . smtSolver - > check ( ) ;
if ( smtCheckResult = = storm : : solver : : SmtSolver : : CheckResult : : Unsat ) {
// There is no point x that violates the 'precision criterion' for this facet
return true ;
} else {
STORM_LOG_THROW ( smtCheckResult = = storm : : solver : : SmtSolver : : CheckResult : : Sat , storm : : exceptions : : UnexpectedException , " The smt solver did not yield sat or unsat. " ) ;
return false ;
}
}
return false ;
}
template < class SparseModelType , typename GeometryValueType >
bool DeterministicParetoExplorer < SparseModelType , GeometryValueType > : : analyzePointsOnFacet ( Environment const & env , Facet & f , std : : set < PointId > & collectedPoints ) {
bool DeterministicParetoExplorer < SparseModelType , GeometryValueType > : : analyzePointsOnFacet ( Environment const & env , FacetAnalysisContext & context ) {
// TODO
return false ;
}
template < class SparseModelType , typename GeometryValueType >
bool DeterministicParetoExplorer < SparseModelType , GeometryValueType > : : analyzePointsInSimplex ( Environment const & env , Facet & f , std : : set < PointId > & collectedPoints ) {
bool DeterministicParetoExplorer < SparseModelType , GeometryValueType > : : analyzePointsInSimplex ( Environment const & env , FacetAnalysisContext & context ) {
// TODO
return false ;
}