Storm
A Modern Probabilistic Model Checker
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
NativePolytope.cpp
Go to the documentation of this file.
2
10
14
15namespace storm {
16namespace storage {
17namespace geometry {
18
19template<typename ValueType>
21 if (halfspaces.empty()) {
22 // The polytope is universal
23 emptyStatus = EmptyStatus::Nonempty;
24 } else {
25 Eigen::Index maxCol = halfspaces.front().normalVector().size();
26 Eigen::Index maxRow = halfspaces.size();
27 A = EigenMatrix(maxRow, maxCol);
28 b = EigenVector(maxRow);
29 for (Eigen::Index row = 0; row < A.rows(); ++row) {
30 assert((Eigen::Index)halfspaces[row].normalVector().size() == maxCol);
31 b(row) = halfspaces[row].offset();
32 A.row(row) = storm::adapters::EigenAdapter::toEigenVector(halfspaces[row].normalVector());
33 }
34 emptyStatus = EmptyStatus::Unknown;
35 }
36}
37
38template<typename ValueType>
39NativePolytope<ValueType>::NativePolytope(std::vector<Point> const& points) {
40 if (points.empty()) {
41 emptyStatus = EmptyStatus::Empty;
42 } else {
43 std::vector<EigenVector> eigenPoints;
44 eigenPoints.reserve(points.size());
45 for (auto const& p : points) {
46 eigenPoints.emplace_back(storm::adapters::EigenAdapter::toEigenVector(p));
47 }
48
50 qh.generateHalfspacesFromPoints(eigenPoints, false);
51 A = std::move(qh.getResultMatrix());
52 b = std::move(qh.getResultVector());
53 emptyStatus = EmptyStatus::Nonempty;
54 }
55}
56
57template<typename ValueType>
58std::shared_ptr<Polytope<ValueType>> NativePolytope<ValueType>::create(boost::optional<std::vector<Halfspace<ValueType>>> const& halfspaces,
59 boost::optional<std::vector<Point>> const& points) {
60 if (halfspaces) {
61 STORM_LOG_WARN_COND(!points, "Creating a NativePolytope where halfspaces AND points are given. The points will be ignored.");
62 return std::make_shared<NativePolytope<ValueType>>(*halfspaces);
63 } else if (points) {
64 return std::make_shared<NativePolytope<ValueType>>(*points);
65 }
66 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Creating a NativePolytope but no representation was given.");
67 return nullptr;
68}
69
70template<typename ValueType>
71NativePolytope<ValueType>::NativePolytope(NativePolytope<ValueType> const& other) : emptyStatus(other.emptyStatus), A(other.A), b(other.b) {
72 // Intentionally left empty
73}
74
75template<typename ValueType>
77 : emptyStatus(std::move(other.emptyStatus)), A(std::move(other.A)), b(std::move(other.b)) {
78 // Intentionally left empty
79}
80
81template<typename ValueType>
82NativePolytope<ValueType>::NativePolytope(EmptyStatus const& emptyStatus, EigenMatrix const& halfspaceMatrix, EigenVector const& halfspaceVector)
83 : emptyStatus(emptyStatus), A(halfspaceMatrix), b(halfspaceVector) {
84 // Intentionally left empty
85}
86
87template<typename ValueType>
88NativePolytope<ValueType>::NativePolytope(EmptyStatus&& emptyStatus, EigenMatrix&& halfspaceMatrix, EigenVector&& halfspaceVector)
89 : emptyStatus(emptyStatus), A(halfspaceMatrix), b(halfspaceVector) {
90 // Intentionally left empty
91}
92
93template<typename ValueType>
95 // Intentionally left empty
96}
97
98template<typename ValueType>
99std::vector<typename Polytope<ValueType>::Point> NativePolytope<ValueType>::getVertices() const {
100 std::vector<EigenVector> eigenVertices = getEigenVertices();
101 std::vector<Point> result;
102 result.reserve(eigenVertices.size());
103 for (auto const& p : eigenVertices) {
105 }
106 return result;
107}
108
109template<typename ValueType>
110std::vector<Halfspace<ValueType>> NativePolytope<ValueType>::getHalfspaces() const {
111 std::vector<Halfspace<ValueType>> result;
112 result.reserve(A.rows());
113
114 for (Eigen::Index row = 0; row < A.rows(); ++row) {
115 result.emplace_back(storm::adapters::EigenAdapter::toStdVector(EigenVector(A.row(row))), b(row));
116 }
117 return result;
118}
119
120template<typename ValueType>
122 if (emptyStatus == EmptyStatus::Unknown) {
123 std::shared_ptr<storm::expressions::ExpressionManager> manager(new storm::expressions::ExpressionManager());
124 std::unique_ptr<storm::solver::SmtSolver> solver = storm::utility::solver::SmtSolverFactory().create(*manager);
125 std::vector<storm::expressions::Expression> constraints = getConstraints(*manager, declareVariables(*manager, "x"));
126 for (auto const& constraint : constraints) {
127 solver->add(constraint);
128 }
129 switch (solver->check()) {
131 emptyStatus = EmptyStatus::Nonempty;
132 break;
134 emptyStatus = EmptyStatus::Empty;
135 break;
136 default:
137 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected result of SMT solver during emptyness-check of Polytope.");
138 break;
139 }
140 }
141 return emptyStatus == EmptyStatus::Empty;
142}
143
144template<typename ValueType>
146 return A.rows() == 0;
147}
148
149template<typename ValueType>
152 for (Eigen::Index row = 0; row < A.rows(); ++row) {
153 if ((A.row(row) * x)(0) > b(row)) {
154 return false;
155 }
156 }
157 return true;
158}
159
160template<typename ValueType>
161bool NativePolytope<ValueType>::contains(std::shared_ptr<Polytope<ValueType>> const& other) const {
162 STORM_LOG_THROW(other->isNativePolytope(), storm::exceptions::InvalidArgumentException,
163 "Invoked operation between a NativePolytope and a different polytope implementation. This is not supported");
164 if (this->isUniversal()) {
165 return true;
166 } else if (other->isUniversal()) {
167 return false;
168 } else {
169 // Check whether there is one point in other that is not in this
170 std::shared_ptr<storm::expressions::ExpressionManager> manager(new storm::expressions::ExpressionManager());
171 std::unique_ptr<storm::solver::SmtSolver> solver = storm::utility::solver::SmtSolverFactory().create(*manager);
172 std::vector<storm::expressions::Variable> variables = declareVariables(*manager, "x");
173 std::vector<storm::expressions::Expression> constraints = getConstraints(*manager, variables);
174 storm::expressions::Expression constraintsThis = manager->boolean(true);
175 for (auto const& constraint : constraints) {
176 constraintsThis = constraintsThis && constraint;
177 }
178 solver->add(!constraintsThis);
179 constraints = dynamic_cast<NativePolytope<ValueType> const&>(*other).getConstraints(*manager, variables);
180 for (auto const& constraint : constraints) {
181 solver->add(constraint);
182 }
183 switch (solver->check()) {
185 return false;
187 return true;
188 default:
189 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected result of SMT solver during containment check of two polytopes.");
190 return false;
191 }
192 }
193}
194
195template<typename ValueType>
196std::shared_ptr<Polytope<ValueType>> NativePolytope<ValueType>::intersection(std::shared_ptr<Polytope<ValueType>> const& rhs) const {
197 STORM_LOG_THROW(rhs->isNativePolytope(), storm::exceptions::InvalidArgumentException,
198 "Invoked operation between a NativePolytope and a different polytope implementation. This is not supported");
199 if (this->isUniversal()) {
200 return rhs;
201 } else if (rhs->isUniversal()) {
202 return std::make_shared<NativePolytope<ValueType>>(*this);
203 } else {
204 NativePolytope<ValueType> const& nativeRhs = dynamic_cast<NativePolytope<ValueType> const&>(*rhs);
205 EigenMatrix resultA(A.rows() + nativeRhs.A.rows(), A.cols());
206 resultA << A, nativeRhs.A;
207 EigenVector resultb(resultA.rows());
208 resultb << b, nativeRhs.b;
209 return std::make_shared<NativePolytope<ValueType>>(EmptyStatus::Unknown, std::move(resultA), std::move(resultb));
210 }
211}
212
213template<typename ValueType>
214std::shared_ptr<Polytope<ValueType>> NativePolytope<ValueType>::intersection(Halfspace<ValueType> const& halfspace) const {
215 if (A.rows() == 0) {
216 // No constraints yet
218 EigenVector resultb(1);
219 resultb(0) = halfspace.offset();
220 return std::make_shared<NativePolytope<ValueType>>(EmptyStatus::Unknown, std::move(resultA), std::move(resultb));
221 }
222 EigenMatrix resultA(A.rows() + 1, A.cols());
223 resultA << A, storm::adapters::EigenAdapter::toEigenVector(halfspace.normalVector()).transpose();
224 EigenVector resultb(resultA.rows());
225 resultb << b, halfspace.offset();
226 return std::make_shared<NativePolytope<ValueType>>(EmptyStatus::Unknown, std::move(resultA), std::move(resultb));
227}
228
229template<typename ValueType>
230std::shared_ptr<Polytope<ValueType>> NativePolytope<ValueType>::convexUnion(std::shared_ptr<Polytope<ValueType>> const& rhs) const {
231 STORM_LOG_THROW(rhs->isNativePolytope(), storm::exceptions::InvalidArgumentException,
232 "Invoked operation between a NativePolytope and a different polytope implementation. This is not supported");
233 if (this->isEmpty()) {
234 return std::make_shared<NativePolytope<ValueType>>(dynamic_cast<NativePolytope<ValueType> const&>(*rhs));
235 } else if (rhs->isEmpty()) {
236 return std::make_shared<NativePolytope<ValueType>>(*this);
237 } else if (this->isUniversal() || rhs->isUniversal()) {
238 return std::make_shared<NativePolytope<ValueType>>(std::vector<Halfspace<ValueType>>());
239 }
240
241 STORM_LOG_WARN_COND_DEBUG(false, "Implementation of convex union of two polytopes only works if the polytopes are bounded. This is not checked.");
242
243 std::vector<EigenVector> rhsVertices = dynamic_cast<NativePolytope<ValueType> const&>(*rhs).getEigenVertices();
244 std::vector<EigenVector> resultVertices = this->getEigenVertices();
245 resultVertices.insert(resultVertices.end(), std::make_move_iterator(rhsVertices.begin()), std::make_move_iterator(rhsVertices.end()));
246
248 qh.generateHalfspacesFromPoints(resultVertices, false);
249 return std::make_shared<NativePolytope<ValueType>>(EmptyStatus::Nonempty, std::move(qh.getResultMatrix()), std::move(qh.getResultVector()));
250}
251
252template<typename ValueType>
253std::shared_ptr<Polytope<ValueType>> NativePolytope<ValueType>::minkowskiSum(std::shared_ptr<Polytope<ValueType>> const& rhs) const {
254 STORM_LOG_THROW(rhs->isNativePolytope(), storm::exceptions::InvalidArgumentException,
255 "Invoked operation between a NativePolytope and a different polytope implementation. This is not supported");
256 NativePolytope<ValueType> const& nativeRhs = dynamic_cast<NativePolytope<ValueType> const&>(*rhs);
257
258 if (this->isEmpty() || nativeRhs.isEmpty()) {
259 return std::make_shared<NativePolytope<ValueType>>(std::vector<Point>());
260 }
261
262 std::vector<std::pair<EigenVector, ValueType>> resultConstraints;
263 resultConstraints.reserve(A.rows() + nativeRhs.A.rows());
264
265 // evaluation of rhs in directions of lhs
266 for (Eigen::Index i = 0; i < A.rows(); ++i) {
267 auto optimizationRes = nativeRhs.optimize(A.row(i));
268 if (optimizationRes.second) {
269 resultConstraints.emplace_back(A.row(i), b(i) + (A.row(i) * optimizationRes.first)(0));
270 }
271 // If optimizationRes.second is false, it means that rhs is unbounded in this direction, i.e., the current constraint is not inserted
272 }
273
274 // evaluation of lhs in directions of rhs
275 for (Eigen::Index i = 0; i < nativeRhs.A.rows(); ++i) {
276 auto optimizationRes = optimize(nativeRhs.A.row(i));
277 if (optimizationRes.second) {
278 resultConstraints.emplace_back(nativeRhs.A.row(i), nativeRhs.b(i) + (nativeRhs.A.row(i) * optimizationRes.first)(0));
279 }
280 // If optimizationRes.second is false, it means that rhs is unbounded in this direction, i.e., the current constraint is not inserted
281 }
282
283 if (resultConstraints.empty()) {
284 return std::make_shared<NativePolytope<ValueType>>(std::vector<Halfspace<ValueType>>());
285 } else {
286 EigenMatrix newA(resultConstraints.size(), resultConstraints.front().first.rows());
287 EigenVector newb(resultConstraints.size());
288 for (Eigen::Index i = 0; i < newA.rows(); ++i) {
289 newA.row(i) = resultConstraints[i].first;
290 newb(i) = resultConstraints[i].second;
291 }
292 return std::make_shared<NativePolytope<ValueType>>(EmptyStatus::Nonempty, std::move(newA), std::move(newb));
293 }
294}
295
296template<typename ValueType>
297std::shared_ptr<Polytope<ValueType>> NativePolytope<ValueType>::affineTransformation(std::vector<Point> const& matrix, Point const& vector) const {
298 STORM_LOG_THROW(!matrix.empty(), storm::exceptions::InvalidArgumentException, "Invoked affine transformation with a matrix without rows.");
299 Eigen::Index rows = matrix.size();
300 Eigen::Index columns = matrix.front().size();
301 EigenMatrix eigenMatrix(rows, columns);
302 for (Eigen::Index row = 0; row < rows; ++row) {
303 eigenMatrix.row(row) = storm::adapters::EigenAdapter::toEigenVector(matrix[row]);
304 }
306
307 Eigen::FullPivLU<EigenMatrix> luMatrix(eigenMatrix);
308 STORM_LOG_THROW(luMatrix.isInvertible(), storm::exceptions::NotImplementedException,
309 "Affine Transformation of native polytope only implemented if the transformation matrix is invertable");
310 if (isUniversal()) {
311 return std::make_shared<NativePolytope<ValueType>>(std::vector<Halfspace<ValueType>>());
312 }
313 EigenMatrix newA = A * luMatrix.inverse();
314 EigenVector newb = b + (newA * eigenVector);
315 return std::make_shared<NativePolytope<ValueType>>(emptyStatus, std::move(newA), std::move(newb));
316}
317
318template<typename ValueType>
319std::pair<typename NativePolytope<ValueType>::Point, bool> NativePolytope<ValueType>::optimize(Point const& direction) const {
320 if (isUniversal()) {
321 return std::make_pair(Point(), false);
322 }
323
324 storm::solver::Z3LpSolver<ValueType> solver(storm::solver::OptimizationDirection::Maximize);
325 std::vector<storm::expressions::Variable> variables;
326 variables.reserve(A.cols());
327 for (Eigen::Index i = 0; i < A.cols(); ++i) {
328 variables.push_back(solver.addUnboundedContinuousVariable("x" + std::to_string(i), direction[i]));
329 }
330 std::vector<storm::expressions::Expression> constraints = getConstraints(solver.getManager(), variables);
331 for (auto const& constraint : constraints) {
332 solver.addConstraint("", constraint);
333 }
334 solver.update();
335 solver.optimize();
336 if (solver.isOptimal()) {
337 auto result = std::make_pair(Point(), true);
338 result.first.reserve(variables.size());
339 for (auto const& var : variables) {
340 result.first.push_back(solver.getContinuousValue(var));
341 }
342 return result;
343 } else {
344 // solution is infinity or infeasible
345 return std::make_pair(Point(), false);
346 }
347}
348
349template<typename ValueType>
350std::pair<typename NativePolytope<ValueType>::EigenVector, bool> NativePolytope<ValueType>::optimize(EigenVector const& direction) const {
351 if (isUniversal()) {
352 return std::make_pair(EigenVector(), false);
353 }
354
355 storm::solver::Z3LpSolver<ValueType> solver(storm::solver::OptimizationDirection::Maximize);
356 std::vector<storm::expressions::Variable> variables;
357 variables.reserve(A.cols());
358 for (Eigen::Index i = 0; i < A.cols(); ++i) {
359 variables.push_back(solver.addUnboundedContinuousVariable("x" + std::to_string(i), static_cast<ValueType>(direction(i))));
360 }
361 std::vector<storm::expressions::Expression> constraints = getConstraints(solver.getManager(), variables);
362 for (auto const& constraint : constraints) {
363 solver.addConstraint("", constraint);
364 }
365 solver.update();
366 solver.optimize();
367 if (solver.isOptimal()) {
368 auto result = std::make_pair(EigenVector(A.cols()), true);
369 for (Eigen::Index i = 0; i < A.cols(); ++i) {
370 result.first(i) = solver.getContinuousValue(variables[i]);
371 }
372 return result;
373 } else {
374 // solution is infinity or infeasible
375 return std::make_pair(EigenVector(), false);
376 }
377}
378
379template<typename ValueType>
381 return true;
382}
383template<typename ValueType>
384std::vector<typename NativePolytope<ValueType>::EigenVector> NativePolytope<ValueType>::getEigenVertices() const {
386 he.generateVerticesFromConstraints(A, b, false);
387 return he.getResultVertices();
388}
389
390template<typename ValueType>
392 std::string const& namePrefix) const {
393 std::vector<storm::expressions::Variable> result;
394 result.reserve(A.cols());
395 for (Eigen::Index col = 0; col < A.cols(); ++col) {
396 result.push_back(manager.declareVariable(namePrefix + std::to_string(col), manager.getRationalType()));
397 }
398 return result;
399}
400
401template<typename ValueType>
402std::vector<storm::expressions::Expression> NativePolytope<ValueType>::getConstraints(storm::expressions::ExpressionManager const& manager,
403 std::vector<storm::expressions::Variable> const& variables) const {
404 std::vector<storm::expressions::Expression> result;
405 for (Eigen::Index row = 0; row < A.rows(); ++row) {
406 storm::expressions::Expression lhs = manager.rational(A(row, 0)) * variables[0].getExpression();
407 for (Eigen::Index col = 1; col < A.cols(); ++col) {
408 lhs = lhs + manager.rational(A(row, col)) * variables[col].getExpression();
409 }
410 result.push_back(lhs <= manager.rational(b(row)));
411 }
412 return result;
413}
414
415template<typename ValueType>
416std::shared_ptr<Polytope<ValueType>> NativePolytope<ValueType>::clean() {
417 if (isEmpty()) {
418 return create(boost::none, {});
419 }
420 std::shared_ptr<storm::expressions::ExpressionManager> manager(new storm::expressions::ExpressionManager());
421 std::unique_ptr<storm::solver::SmtSolver> solver = storm::utility::solver::SmtSolverFactory().create(*manager);
422 auto variables = declareVariables(*manager, "x");
423 std::vector<storm::expressions::Expression> constraints = getConstraints(*manager, variables);
424 for (auto const& constraint : constraints) {
425 solver->add(constraint);
426 }
427 storm::storage::BitVector keptConstraints(A.rows(), false);
428 for (Eigen::Index row = 0; row < A.rows(); ++row) {
429 storm::expressions::Expression lhs = manager->rational(A(row, 0)) * variables[0].getExpression();
430 for (Eigen::Index col = 1; col < A.cols(); ++col) {
431 lhs = lhs + manager->rational(A(row, col)) * variables[col].getExpression();
432 }
433 solver->push();
434 solver->add(lhs >= manager->rational(b(row)));
435 switch (solver->check()) {
437 keptConstraints.set(row, true);
439 break;
440 default:
441 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected result of SMT solver during emptyness-check of Polytope.");
442 break;
443 }
444 solver->pop();
445 }
446 std::vector<Halfspace<ValueType>> newHalfspaces;
447 newHalfspaces.reserve(keptConstraints.getNumberOfSetBits());
448 for (auto row : keptConstraints) {
449 newHalfspaces.emplace_back(storm::adapters::EigenAdapter::toStdVector(EigenVector(A.row(row))), b(row));
450 }
451 return create(newHalfspaces, boost::none);
452}
453
454template class NativePolytope<double>;
455#ifdef STORM_HAVE_CARL
457#endif
458} // namespace geometry
459} // namespace storage
460} // namespace storm
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.
Definition LpSolver.cpp:131
Variable addUnboundedContinuousVariable(std::string const &name, ValueType objectiveFunctionCoefficient=0)
Registers a unbounded continuous variable, i.e.
Definition LpSolver.cpp:57
A class that implements the LpSolver interface using Z3.
Definition Z3LpSolver.h:24
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.
Definition BitVector.h:18
void set(uint_fast64_t index, bool value=true)
Sets the given truth value at the given index.
uint_fast64_t getNumberOfSetBits() const
Returns the number of bits that are set to true in this bit vector.
std::vector< ValueType > const & normalVector() const
Definition Halfspace.h:112
ValueType const & offset() const
Definition Halfspace.h:120
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 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)
Definition QuickHull.cpp:21
virtual std::unique_ptr< storm::solver::SmtSolver > create(storm::expressions::ExpressionManager &manager) const
Creates a new SMT solver instance.
Definition solver.cpp:124
#define STORM_LOG_WARN_COND(cond, message)
Definition macros.h:38
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
#define STORM_LOG_WARN_COND_DEBUG(cond, message)
Definition macros.h:18
LabParser.cpp.
Definition cli.cpp:18