18template<
typename ValueType>
20 if (halfspaces.empty()) {
22 emptyStatus = EmptyStatus::Nonempty;
24 Eigen::Index maxCol = halfspaces.front().normalVector().size();
25 Eigen::Index maxRow = halfspaces.size();
28 for (Eigen::Index row = 0; row < A.rows(); ++row) {
29 assert((Eigen::Index)halfspaces[row].normalVector().size() == maxCol);
30 b(row) = halfspaces[row].offset();
33 emptyStatus = EmptyStatus::Unknown;
37template<
typename ValueType>
40 emptyStatus = EmptyStatus::Empty;
42 std::vector<EigenVector> eigenPoints;
43 eigenPoints.reserve(points.size());
44 for (
auto const& p : points) {
52 emptyStatus = EmptyStatus::Nonempty;
56template<
typename ValueType>
58 boost::optional<std::vector<Point>>
const& points) {
60 STORM_LOG_WARN_COND(!points,
"Creating a NativePolytope where halfspaces AND points are given. The points will be ignored.");
61 return std::make_shared<NativePolytope<ValueType>>(*halfspaces);
63 return std::make_shared<NativePolytope<ValueType>>(*points);
65 STORM_LOG_THROW(
false, storm::exceptions::UnexpectedException,
"Creating a NativePolytope but no representation was given.");
69template<
typename ValueType>
74template<
typename ValueType>
76 : emptyStatus(
std::move(other.emptyStatus)), A(
std::move(other.A)), b(
std::move(other.b)) {
80template<
typename ValueType>
82 : emptyStatus(emptyStatus), A(halfspaceMatrix), b(halfspaceVector) {
86template<
typename ValueType>
88 : emptyStatus(emptyStatus), A(halfspaceMatrix), b(halfspaceVector) {
92template<
typename ValueType>
97template<
typename ValueType>
99 std::vector<EigenVector> eigenVertices = getEigenVertices();
100 std::vector<Point> result;
101 result.reserve(eigenVertices.size());
102 for (
auto const& p : eigenVertices) {
108template<
typename ValueType>
110 std::vector<Halfspace<ValueType>> result;
111 result.reserve(A.rows());
113 for (Eigen::Index row = 0; row < A.rows(); ++row) {
119template<
typename ValueType>
121 if (emptyStatus == EmptyStatus::Unknown) {
124 std::vector<storm::expressions::Expression> constraints = getConstraints(*manager, declareVariables(*manager,
"x"));
125 for (
auto const& constraint : constraints) {
126 solver->add(constraint);
128 switch (solver->check()) {
130 emptyStatus = EmptyStatus::Nonempty;
133 emptyStatus = EmptyStatus::Empty;
136 STORM_LOG_THROW(
false, storm::exceptions::UnexpectedException,
"Unexpected result of SMT solver during emptyness-check of Polytope.");
140 return emptyStatus == EmptyStatus::Empty;
143template<
typename ValueType>
145 return A.rows() == 0;
148template<
typename ValueType>
151 for (Eigen::Index row = 0; row < A.rows(); ++row) {
152 if ((A.row(row) * x)(0) > b(row)) {
159template<
typename ValueType>
161 STORM_LOG_THROW(other->isNativePolytope(), storm::exceptions::InvalidArgumentException,
162 "Invoked operation between a NativePolytope and a different polytope implementation. This is not supported");
163 if (this->isUniversal()) {
165 }
else if (other->isUniversal()) {
171 std::vector<storm::expressions::Variable> variables = declareVariables(*manager,
"x");
172 std::vector<storm::expressions::Expression> constraints = getConstraints(*manager, variables);
174 for (
auto const& constraint : constraints) {
175 constraintsThis = constraintsThis && constraint;
177 solver->add(!constraintsThis);
179 for (
auto const& constraint : constraints) {
180 solver->add(constraint);
182 switch (solver->check()) {
188 STORM_LOG_THROW(
false, storm::exceptions::UnexpectedException,
"Unexpected result of SMT solver during containment check of two polytopes.");
194template<
typename ValueType>
196 STORM_LOG_THROW(rhs->isNativePolytope(), storm::exceptions::InvalidArgumentException,
197 "Invoked operation between a NativePolytope and a different polytope implementation. This is not supported");
198 if (this->isUniversal()) {
200 }
else if (rhs->isUniversal()) {
201 return std::make_shared<NativePolytope<ValueType>>(*this);
204 EigenMatrix resultA(A.rows() + nativeRhs.A.rows(), A.cols());
205 resultA << A, nativeRhs.A;
207 resultb << b, nativeRhs.b;
208 return std::make_shared<NativePolytope<ValueType>>(EmptyStatus::Unknown, std::move(resultA), std::move(resultb));
212template<
typename ValueType>
218 resultb(0) = halfspace.
offset();
219 return std::make_shared<NativePolytope<ValueType>>(EmptyStatus::Unknown, std::move(resultA), std::move(resultb));
224 resultb << b, halfspace.
offset();
225 return std::make_shared<NativePolytope<ValueType>>(EmptyStatus::Unknown, std::move(resultA), std::move(resultb));
228template<
typename ValueType>
230 STORM_LOG_THROW(rhs->isNativePolytope(), storm::exceptions::InvalidArgumentException,
231 "Invoked operation between a NativePolytope and a different polytope implementation. This is not supported");
232 if (this->isEmpty()) {
234 }
else if (rhs->isEmpty()) {
235 return std::make_shared<NativePolytope<ValueType>>(*this);
236 }
else if (this->isUniversal() || rhs->isUniversal()) {
237 return std::make_shared<NativePolytope<ValueType>>(std::vector<Halfspace<ValueType>>());
240 STORM_LOG_WARN_COND_DEBUG(
false,
"Implementation of convex union of two polytopes only works if the polytopes are bounded. This is not checked.");
243 std::vector<EigenVector> resultVertices = this->getEigenVertices();
244 resultVertices.insert(resultVertices.end(), std::make_move_iterator(rhsVertices.begin()), std::make_move_iterator(rhsVertices.end()));
251template<
typename ValueType>
253 STORM_LOG_THROW(rhs->isNativePolytope(), storm::exceptions::InvalidArgumentException,
254 "Invoked operation between a NativePolytope and a different polytope implementation. This is not supported");
257 if (this->isEmpty() || nativeRhs.
isEmpty()) {
258 return std::make_shared<NativePolytope<ValueType>>(std::vector<Point>());
261 std::vector<std::pair<EigenVector, ValueType>> resultConstraints;
262 resultConstraints.reserve(A.rows() + nativeRhs.A.rows());
265 for (Eigen::Index
i = 0;
i < A.rows(); ++
i) {
266 auto optimizationRes = nativeRhs.
optimize(A.row(
i));
267 if (optimizationRes.second) {
268 resultConstraints.emplace_back(A.row(
i), b(
i) + (A.row(
i) * optimizationRes.first)(0));
274 for (Eigen::Index
i = 0;
i < nativeRhs.A.rows(); ++
i) {
275 auto optimizationRes = optimize(nativeRhs.A.row(
i));
276 if (optimizationRes.second) {
277 resultConstraints.emplace_back(nativeRhs.A.row(
i), nativeRhs.b(
i) + (nativeRhs.A.row(
i) * optimizationRes.first)(0));
282 if (resultConstraints.empty()) {
283 return std::make_shared<NativePolytope<ValueType>>(std::vector<Halfspace<ValueType>>());
285 EigenMatrix newA(resultConstraints.size(), resultConstraints.front().first.rows());
287 for (Eigen::Index
i = 0;
i < newA.rows(); ++
i) {
288 newA.row(
i) = resultConstraints[
i].first;
289 newb(
i) = resultConstraints[
i].second;
291 return std::make_shared<NativePolytope<ValueType>>(EmptyStatus::Nonempty, std::move(newA), std::move(newb));
295template<
typename ValueType>
297 STORM_LOG_THROW(!matrix.empty(), storm::exceptions::InvalidArgumentException,
"Invoked affine transformation with a matrix without rows.");
298 Eigen::Index rows = matrix.size();
299 Eigen::Index columns = matrix.front().size();
301 for (Eigen::Index row = 0; row < rows; ++row) {
306 Eigen::FullPivLU<EigenMatrix> luMatrix(eigenMatrix);
307 STORM_LOG_THROW(luMatrix.isInvertible(), storm::exceptions::NotImplementedException,
308 "Affine Transformation of native polytope only implemented if the transformation matrix is invertable");
310 return std::make_shared<NativePolytope<ValueType>>(std::vector<Halfspace<ValueType>>());
314 return std::make_shared<NativePolytope<ValueType>>(emptyStatus, std::move(newA), std::move(newb));
317template<
typename ValueType>
320 return std::make_pair(
Point(),
false);
324 std::vector<storm::expressions::Variable> variables;
325 variables.reserve(A.cols());
326 for (Eigen::Index
i = 0;
i < A.cols(); ++
i) {
329 std::vector<storm::expressions::Expression> constraints = getConstraints(solver.
getManager(), variables);
330 for (
auto const& constraint : constraints) {
336 auto result = std::make_pair(
Point(),
true);
337 result.first.reserve(variables.size());
338 for (
auto const& var : variables) {
344 return std::make_pair(
Point(),
false);
348template<
typename ValueType>
351 return std::make_pair(EigenVector(),
false);
355 std::vector<storm::expressions::Variable> variables;
356 variables.reserve(A.cols());
357 for (Eigen::Index
i = 0;
i < A.cols(); ++
i) {
358 variables.push_back(solver.addUnboundedContinuousVariable(
"x" + std::to_string(
i),
static_cast<ValueType
>(direction(
i))));
360 std::vector<storm::expressions::Expression> constraints = getConstraints(solver.getManager(), variables);
361 for (
auto const& constraint : constraints) {
362 solver.addConstraint(
"", constraint);
366 if (solver.isOptimal()) {
367 auto result = std::make_pair(EigenVector(A.cols()),
true);
368 for (Eigen::Index
i = 0;
i < A.cols(); ++
i) {
369 result.first(
i) = solver.getContinuousValue(variables[
i]);
374 return std::make_pair(EigenVector(),
false);
378template<
typename ValueType>
382template<
typename ValueType>
389template<
typename ValueType>
391 std::string
const& namePrefix)
const {
392 std::vector<storm::expressions::Variable> result;
393 result.reserve(A.cols());
394 for (Eigen::Index col = 0; col < A.cols(); ++col) {
395 result.push_back(manager.declareVariable(namePrefix + std::to_string(col), manager.getRationalType()));
400template<
typename ValueType>
402 std::vector<storm::expressions::Variable>
const& variables)
const {
403 std::vector<storm::expressions::Expression> result;
404 for (Eigen::Index row = 0; row < A.rows(); ++row) {
406 for (Eigen::Index col = 1; col < A.cols(); ++col) {
407 lhs = lhs + manager.rational(A(row, col)) * variables[col].getExpression();
409 result.push_back(lhs <= manager.rational(b(row)));
414template<
typename ValueType>
417 return create(boost::none, {});
421 auto variables = declareVariables(*manager,
"x");
422 std::vector<storm::expressions::Expression> constraints = getConstraints(*manager, variables);
423 for (
auto const& constraint : constraints) {
424 solver->add(constraint);
427 for (Eigen::Index row = 0; row < A.rows(); ++row) {
429 for (Eigen::Index col = 1; col < A.cols(); ++col) {
430 lhs = lhs + manager->rational(A(row, col)) * variables[col].getExpression();
433 solver->add(lhs >= manager->rational(b(row)));
434 switch (solver->check()) {
436 keptConstraints.
set(row,
true);
440 STORM_LOG_THROW(
false, storm::exceptions::UnexpectedException,
"Unexpected result of SMT solver during emptyness-check of Polytope.");
445 std::vector<Halfspace<ValueType>> newHalfspaces;
447 for (
auto row : keptConstraints) {
450 return create(newHalfspaces, boost::none);
static Eigen::Matrix< ValueType, Eigen::Dynamic, 1 > toEigenVector(std::vector< ValueType > const &v)
static std::vector< ValueType > toStdVector(Eigen::Matrix< ValueType, Eigen::Dynamic, 1 > const &v)
This class is responsible for managing a set of typed variables and all expressions using these varia...
storm::expressions::ExpressionManager const & getManager() const
Retrieves the manager for the variables created for this solver.
Variable addUnboundedContinuousVariable(std::string const &name, ValueType objectiveFunctionCoefficient=0)
Registers a unbounded continuous variable, i.e.
A class that implements the LpSolver interface using Z3.
virtual void update() const override
Updates the model to make the variables that have been declared since the last call to update usable.
virtual ValueType getContinuousValue(Variable const &variable) const override
Retrieves the value of the continuous variable with the given name.
virtual void addConstraint(std::string const &name, Constraint const &constraint) override
Adds a the given constraint to the LP problem.
virtual bool isOptimal() const override
Retrieves whether the model was found to be optimal, i.e.
virtual void optimize() const override
Optimizes the LP problem previously constructed.
A bit vector that is internally represented as a vector of 64-bit values.
uint64_t getNumberOfSetBits() const
Returns the number of bits that are set to true in this bit vector.
void set(uint64_t index, bool value=true)
Sets the given truth value at the given index.
std::vector< ValueType > const & normalVector() const
ValueType const & offset() const
std::vector< EigenVector > & getResultVertices()
void generateVerticesFromConstraints(EigenMatrix const &constraintMatrix, EigenVector const &constraintVector, bool generateRelevantHyperplanesAndVertexSets)
virtual bool contains(Point const &point) const override
Returns true iff the given point is inside of the polytope.
NativePolytope(std::vector< Halfspace< ValueType > > const &halfspaces)
Creates a NativePolytope from the given halfspaces The resulting polytope is defined as the intersect...
Eigen::Matrix< ValueType, Eigen::Dynamic, Eigen::Dynamic > EigenMatrix
virtual std::vector< storm::expressions::Variable > declareVariables(storm::expressions::ExpressionManager &manager, std::string const &namePrefix) const override
declares one variable for each dimension and returns the obtained variables.
virtual std::pair< Point, bool > optimize(Point const &direction) const override
Finds an optimal point inside this polytope w.r.t.
virtual std::shared_ptr< Polytope< ValueType > > clean() override
Performs cleaning operations, e.g., deleting redundant halfspaces.
virtual std::shared_ptr< Polytope< ValueType > > affineTransformation(std::vector< Point > const &matrix, Point const &vector) const override
Returns the affine transformation of this polytope P w.r.t.
virtual bool isNativePolytope() const override
Polytope< ValueType >::Point Point
virtual std::vector< Point > getVertices() const override
Returns the vertices of this polytope.
Eigen::Matrix< ValueType, Eigen::Dynamic, 1 > EigenVector
virtual bool isUniversal() const override
Returns whether this polytope is universal (i.e., equals R^n).
virtual std::shared_ptr< Polytope< ValueType > > intersection(std::shared_ptr< Polytope< ValueType > > const &rhs) const override
Intersects this polytope with rhs and returns the result.
virtual std::shared_ptr< Polytope< ValueType > > minkowskiSum(std::shared_ptr< Polytope< ValueType > > const &rhs) const override
Returns the minkowskiSum of this polytope and rhs.
static std::shared_ptr< Polytope< ValueType > > create(boost::optional< std::vector< Halfspace< ValueType > > > const &halfspaces, boost::optional< std::vector< Point > > const &points)
Creates a NativePolytope from the given halfspaces or points.
virtual bool isEmpty() const override
Returns whether this polytope is the empty set.
virtual std::vector< storm::expressions::Expression > getConstraints(storm::expressions::ExpressionManager const &manager, std::vector< storm::expressions::Variable > const &variables) const override
returns the constrains defined by this polytope as an expression over the given variables
virtual ~NativePolytope()
virtual std::vector< Halfspace< ValueType > > getHalfspaces() const override
Returns the halfspaces of this polytope.
virtual std::shared_ptr< Polytope< ValueType > > convexUnion(std::shared_ptr< Polytope< ValueType > > const &rhs) const override
Returns the convex union of this polytope and rhs.
void generateHalfspacesFromPoints(std::vector< EigenVector > &points, bool generateRelevantVerticesAndVertexSets)
EigenMatrix & getResultMatrix()
EigenVector & getResultVector()
virtual std::unique_ptr< storm::solver::SmtSolver > create(storm::expressions::ExpressionManager &manager) const
Creates a new SMT solver instance.
#define STORM_LOG_WARN_COND(cond, message)
#define STORM_LOG_THROW(cond, exception, message)
#define STORM_LOG_WARN_COND_DEBUG(cond, message)