#include "storm/storage/geometry/Polytope.h" #include #include "storm/adapters/RationalFunctionAdapter.h" #include "storm/adapters/HyproAdapter.h" #include "storm/storage/geometry/HyproPolytope.h" #include "storm/storage/geometry/NativePolytope.h" #include "storm/utility/macros.h" #include "storm/exceptions/NotImplementedException.h" #include "storm/exceptions/IllegalFunctionCallException.h" namespace storm { namespace storage { namespace geometry { template std::shared_ptr> Polytope::create(std::vector> const& halfspaces) { return create(halfspaces, boost::none); } template std::shared_ptr> Polytope::create(std::vector const& points) { return create(boost::none, points); } template std::shared_ptr> Polytope::createUniversalPolytope() { return create(std::vector>(), boost::none); } template std::shared_ptr> Polytope::createEmptyPolytope() { return create(boost::none, std::vector()); } template std::shared_ptr> Polytope::create(boost::optional>> const& halfspaces, boost::optional> const& points) { #ifdef STORM_HAVE_HYPRO return HyproPolytope::create(halfspaces, points); #else return NativePolytope::create(halfspaces, points); #endif } template std::shared_ptr> Polytope::createDownwardClosure(std::vector const& points) { if (points.empty()) { // In this case, the downwardclosure is empty return createEmptyPolytope(); } // Reduce this call to a more general method storm::storage::BitVector dimensions(points.front().size(), true); return createSelectiveDownwardClosure(points, dimensions); } template std::shared_ptr> Polytope::createSelectiveDownwardClosure(std::vector const& points, storm::storage::BitVector const& selectedDimensions) { if (points.empty()) { // In this case, the downwardclosure is empty return createEmptyPolytope(); } if (selectedDimensions.empty()) { return create(points); } assert(points.front().size() == selectedDimensions.size()); std::vector> halfspaces; // We build the convex hull of the given points. // However, auxiliary points (that will always be in the selective downward closure) are added. // Then, the halfspaces of the resulting polytope are a superset of the halfspaces of the downward closure. std::vector auxiliaryPoints = points; auxiliaryPoints.reserve(auxiliaryPoints.size() * (1 + selectedDimensions.getNumberOfSetBits())); for (auto const& point : points) { for (auto const& dim : selectedDimensions) { auxiliaryPoints.push_back(point); auxiliaryPoints.back()[dim] -= storm::utility::one(); } } std::vector> auxiliaryHalfspaces = create(auxiliaryPoints)->getHalfspaces(); // The downward closure is obtained by erasing the halfspaces for which the normal vector is negative for one of the selected dimensions. for (auto& h : auxiliaryHalfspaces) { bool allGreaterEqZero = true; for (auto const& dim : selectedDimensions) { allGreaterEqZero &= (h.normalVector()[dim] >= storm::utility::zero()); } if (allGreaterEqZero){ halfspaces.push_back(std::move(h)); } } return create(halfspaces); } template Polytope::Polytope() { // Intentionally left empty } template Polytope::~Polytope() { // Intentionally left empty } #ifdef STORM_HAVE_CARL template <> std::vector::Point> Polytope::getVerticesInClockwiseOrder() const{ std::vector vertices = getVertices(); if(vertices.size()<=2) { // In this case, every ordering is clockwise return vertices; } STORM_LOG_THROW(vertices.front().size()==2, storm::exceptions::IllegalFunctionCallException, "Getting Vertices in clockwise order is only possible for a 2D-polytope."); std::vector neighborsOfVertices(vertices.size(), storm::storage::BitVector(vertices.size(), false)); std::vector> halfspaces = getHalfspaces(); for(auto const& h : halfspaces) { storm::storage::BitVector verticesOnHalfspace(vertices.size(), false); for(uint_fast64_t v = 0; v result; result.reserve(vertices.size()); storm::storage::BitVector unprocessedVertices(vertices.size(), true); uint_fast64_t currentVertex = 0; for(uint_fast64_t v = 1; v std::vector::Point> Polytope::getVerticesInClockwiseOrder() const{ STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Functionality not implemented."); // Note that the implementation for RationalNumber above only works for exact ValueType since // checking whether the distance between halfspace and point is zero is problematic otherwise. return std::vector(); } template std::shared_ptr> Polytope::shift(Point const& b) const { // perform an affine transformation with identity matrix std::vector idMatrix(b.size(), Point(b.size(), storm::utility::zero())); for (uint64_t i = 0; i < b.size(); ++i) { idMatrix[i][i] = storm::utility::one(); } return affineTransformation(idMatrix, b); } template std::vector>> Polytope::setMinus(std::shared_ptr> const& rhs) const { std::vector>> result; auto rhsHalfspaces = rhs->getHalfspaces(); std::shared_ptr> remaining = nullptr; for (auto const& h : rhsHalfspaces) { Polytope const& ref = (remaining == nullptr) ? *this : *remaining; auto next = ref.intersection(h.invert()); if (!next->isEmpty()) { result.push_back(next); } remaining = ref.intersection(h); if (remaining->isEmpty()) { break; } } return result; } template std::shared_ptr> Polytope::downwardClosure() const { return createDownwardClosure(this->getVertices()); } template std::vector Polytope::declareVariables(storm::expressions::ExpressionManager& manager, std::string const& namePrefix) const { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Functionality not implemented for this polytope implementation."); std::vector result; return result; } template std::vector Polytope::getConstraints(storm::expressions::ExpressionManager const& manager, std::vector const& variables) const { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Functionality not implemented for this polytope implementation."); std::vector result; return result; } template template std::shared_ptr> Polytope::convertNumberRepresentation() const { if(isEmpty()) { return Polytope::createEmptyPolytope(); } auto halfspaces = getHalfspaces(); std::vector> halfspacesPrime; halfspacesPrime.reserve(halfspaces.size()); for(auto const& h : halfspaces) { halfspacesPrime.emplace_back(storm::utility::vector::convertNumericVector(h.normalVector()), storm::utility::convertNumber(h.offset())); } return Polytope::create(halfspacesPrime); } template std::string Polytope::toString(bool numbersAsDouble) const { auto halfspaces = this->getHalfspaces(); std::stringstream stream; stream << "Polytope with " << halfspaces.size() << " Halfspaces" << (halfspaces.empty() ? "" : ":") << std::endl; for (auto const& h : halfspaces) { stream << " " << h.toString(numbersAsDouble) << std::endl; } return stream.str(); } template bool Polytope::isHyproPolytope() const { return false; } template bool Polytope::isNativePolytope() const { return false; } template std::shared_ptr> Polytope::clean() { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "functionality not implemented for this polytope type."); return nullptr; } template class Polytope; template std::shared_ptr> Polytope::convertNumberRepresentation() const; #ifdef STORM_HAVE_CARL template class Polytope; template std::shared_ptr> Polytope::convertNumberRepresentation() const; template std::shared_ptr> Polytope::convertNumberRepresentation() const; template std::shared_ptr> Polytope::convertNumberRepresentation() const; #endif } } }