Storm 1.11.1.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
NativePolytope.cpp
Go to the documentation of this file.
2
13
14namespace storm {
15namespace storage {
16namespace geometry {
17
18template<typename ValueType>
20 if (halfspaces.empty()) {
21 // The polytope is universal
22 emptyStatus = EmptyStatus::Nonempty;
23 } else {
24 Eigen::Index maxCol = halfspaces.front().normalVector().size();
25 Eigen::Index maxRow = halfspaces.size();
26 A = EigenMatrix(maxRow, maxCol);
27 b = EigenVector(maxRow);
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();
31 A.row(row) = storm::adapters::EigenAdapter::toEigenVector(halfspaces[row].normalVector());
32 }
33 emptyStatus = EmptyStatus::Unknown;
34 }
35}
36
37template<typename ValueType>
38NativePolytope<ValueType>::NativePolytope(std::vector<Point> const& points) {
39 if (points.empty()) {
40 emptyStatus = EmptyStatus::Empty;
41 } else {
42 std::vector<EigenVector> eigenPoints;
43 eigenPoints.reserve(points.size());
44 for (auto const& p : points) {
45 eigenPoints.emplace_back(storm::adapters::EigenAdapter::toEigenVector(p));
46 }
47
49 qh.generateHalfspacesFromPoints(eigenPoints, false);
50 A = std::move(qh.getResultMatrix());
51 b = std::move(qh.getResultVector());
52 emptyStatus = EmptyStatus::Nonempty;
53 }
54}
55
56template<typename ValueType>
57std::shared_ptr<Polytope<ValueType>> NativePolytope<ValueType>::create(boost::optional<std::vector<Halfspace<ValueType>>> const& halfspaces,
58 boost::optional<std::vector<Point>> const& points) {
59 if (halfspaces) {
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);
62 } else if (points) {
63 return std::make_shared<NativePolytope<ValueType>>(*points);
64 }
65 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Creating a NativePolytope but no representation was given.");
66 return nullptr;
67}
68
69template<typename ValueType>
70NativePolytope<ValueType>::NativePolytope(NativePolytope<ValueType> const& other) : emptyStatus(other.emptyStatus), A(other.A), b(other.b) {
71 // Intentionally left empty
72}
73
74template<typename ValueType>
76 : emptyStatus(std::move(other.emptyStatus)), A(std::move(other.A)), b(std::move(other.b)) {
77 // Intentionally left empty
78}
79
80template<typename ValueType>
81NativePolytope<ValueType>::NativePolytope(EmptyStatus const& emptyStatus, EigenMatrix const& halfspaceMatrix, EigenVector const& halfspaceVector)
82 : emptyStatus(emptyStatus), A(halfspaceMatrix), b(halfspaceVector) {
83 // Intentionally left empty
84}
85
86template<typename ValueType>
87NativePolytope<ValueType>::NativePolytope(EmptyStatus&& emptyStatus, EigenMatrix&& halfspaceMatrix, EigenVector&& halfspaceVector)
88 : emptyStatus(emptyStatus), A(halfspaceMatrix), b(halfspaceVector) {
89 // Intentionally left empty
90}
91
92template<typename ValueType>
94 // Intentionally left empty
95}
96
97template<typename ValueType>
98std::vector<typename Polytope<ValueType>::Point> NativePolytope<ValueType>::getVertices() const {
99 std::vector<EigenVector> eigenVertices = getEigenVertices();
100 std::vector<Point> result;
101 result.reserve(eigenVertices.size());
102 for (auto const& p : eigenVertices) {
104 }
105 return result;
106}
107
108template<typename ValueType>
109std::vector<Halfspace<ValueType>> NativePolytope<ValueType>::getHalfspaces() const {
110 std::vector<Halfspace<ValueType>> result;
111 result.reserve(A.rows());
112
113 for (Eigen::Index row = 0; row < A.rows(); ++row) {
114 result.emplace_back(storm::adapters::EigenAdapter::toStdVector(EigenVector(A.row(row))), b(row));
115 }
116 return result;
117}
118
119template<typename ValueType>
121 if (emptyStatus == EmptyStatus::Unknown) {
122 std::shared_ptr<storm::expressions::ExpressionManager> manager(new storm::expressions::ExpressionManager());
123 std::unique_ptr<storm::solver::SmtSolver> solver = storm::utility::solver::SmtSolverFactory().create(*manager);
124 std::vector<storm::expressions::Expression> constraints = getConstraints(*manager, declareVariables(*manager, "x"));
125 for (auto const& constraint : constraints) {
126 solver->add(constraint);
127 }
128 switch (solver->check()) {
130 emptyStatus = EmptyStatus::Nonempty;
131 break;
133 emptyStatus = EmptyStatus::Empty;
134 break;
135 default:
136 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected result of SMT solver during emptyness-check of Polytope.");
137 break;
138 }
139 }
140 return emptyStatus == EmptyStatus::Empty;
141}
142
143template<typename ValueType>
145 return A.rows() == 0;
146}
147
148template<typename ValueType>
151 for (Eigen::Index row = 0; row < A.rows(); ++row) {
152 if ((A.row(row) * x)(0) > b(row)) {
153 return false;
154 }
155 }
156 return true;
157}
158
159template<typename ValueType>
160bool NativePolytope<ValueType>::contains(std::shared_ptr<Polytope<ValueType>> const& other) const {
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()) {
164 return true;
165 } else if (other->isUniversal()) {
166 return false;
167 } else {
168 // Check whether there is one point in other that is not in this
169 std::shared_ptr<storm::expressions::ExpressionManager> manager(new storm::expressions::ExpressionManager());
170 std::unique_ptr<storm::solver::SmtSolver> solver = storm::utility::solver::SmtSolverFactory().create(*manager);
171 std::vector<storm::expressions::Variable> variables = declareVariables(*manager, "x");
172 std::vector<storm::expressions::Expression> constraints = getConstraints(*manager, variables);
173 storm::expressions::Expression constraintsThis = manager->boolean(true);
174 for (auto const& constraint : constraints) {
175 constraintsThis = constraintsThis && constraint;
176 }
177 solver->add(!constraintsThis);
178 constraints = dynamic_cast<NativePolytope<ValueType> const&>(*other).getConstraints(*manager, variables);
179 for (auto const& constraint : constraints) {
180 solver->add(constraint);
181 }
182 switch (solver->check()) {
184 return false;
186 return true;
187 default:
188 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected result of SMT solver during containment check of two polytopes.");
189 return false;
190 }
191 }
192}
193
194template<typename ValueType>
195std::shared_ptr<Polytope<ValueType>> NativePolytope<ValueType>::intersection(std::shared_ptr<Polytope<ValueType>> const& rhs) const {
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()) {
199 return rhs;
200 } else if (rhs->isUniversal()) {
201 return std::make_shared<NativePolytope<ValueType>>(*this);
202 } else {
203 NativePolytope<ValueType> const& nativeRhs = dynamic_cast<NativePolytope<ValueType> const&>(*rhs);
204 EigenMatrix resultA(A.rows() + nativeRhs.A.rows(), A.cols());
205 resultA << A, nativeRhs.A;
206 EigenVector resultb(resultA.rows());
207 resultb << b, nativeRhs.b;
208 return std::make_shared<NativePolytope<ValueType>>(EmptyStatus::Unknown, std::move(resultA), std::move(resultb));
209 }
210}
211
212template<typename ValueType>
213std::shared_ptr<Polytope<ValueType>> NativePolytope<ValueType>::intersection(Halfspace<ValueType> const& halfspace) const {
214 if (A.rows() == 0) {
215 // No constraints yet
217 EigenVector resultb(1);
218 resultb(0) = halfspace.offset();
219 return std::make_shared<NativePolytope<ValueType>>(EmptyStatus::Unknown, std::move(resultA), std::move(resultb));
220 }
221 EigenMatrix resultA(A.rows() + 1, A.cols());
222 resultA << A, storm::adapters::EigenAdapter::toEigenVector(halfspace.normalVector()).transpose();
223 EigenVector resultb(resultA.rows());
224 resultb << b, halfspace.offset();
225 return std::make_shared<NativePolytope<ValueType>>(EmptyStatus::Unknown, std::move(resultA), std::move(resultb));
226}
227
228template<typename ValueType>
229std::shared_ptr<Polytope<ValueType>> NativePolytope<ValueType>::convexUnion(std::shared_ptr<Polytope<ValueType>> const& rhs) const {
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()) {
233 return std::make_shared<NativePolytope<ValueType>>(dynamic_cast<NativePolytope<ValueType> const&>(*rhs));
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>>());
238 }
239
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.");
241
242 std::vector<EigenVector> rhsVertices = dynamic_cast<NativePolytope<ValueType> const&>(*rhs).getEigenVertices();
243 std::vector<EigenVector> resultVertices = this->getEigenVertices();
244 resultVertices.insert(resultVertices.end(), std::make_move_iterator(rhsVertices.begin()), std::make_move_iterator(rhsVertices.end()));
245
247 qh.generateHalfspacesFromPoints(resultVertices, false);
248 return std::make_shared<NativePolytope<ValueType>>(EmptyStatus::Nonempty, std::move(qh.getResultMatrix()), std::move(qh.getResultVector()));
249}
250
251template<typename ValueType>
252std::shared_ptr<Polytope<ValueType>> NativePolytope<ValueType>::minkowskiSum(std::shared_ptr<Polytope<ValueType>> const& rhs) const {
253 STORM_LOG_THROW(rhs->isNativePolytope(), storm::exceptions::InvalidArgumentException,
254 "Invoked operation between a NativePolytope and a different polytope implementation. This is not supported");
255 NativePolytope<ValueType> const& nativeRhs = dynamic_cast<NativePolytope<ValueType> const&>(*rhs);
256
257 if (this->isEmpty() || nativeRhs.isEmpty()) {
258 return std::make_shared<NativePolytope<ValueType>>(std::vector<Point>());
259 }
260
261 std::vector<std::pair<EigenVector, ValueType>> resultConstraints;
262 resultConstraints.reserve(A.rows() + nativeRhs.A.rows());
263
264 // evaluation of rhs in directions of lhs
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));
269 }
270 // If optimizationRes.second is false, it means that rhs is unbounded in this direction, i.e., the current constraint is not inserted
271 }
272
273 // evaluation of lhs in directions of rhs
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));
278 }
279 // If optimizationRes.second is false, it means that rhs is unbounded in this direction, i.e., the current constraint is not inserted
280 }
281
282 if (resultConstraints.empty()) {
283 return std::make_shared<NativePolytope<ValueType>>(std::vector<Halfspace<ValueType>>());
284 } else {
285 EigenMatrix newA(resultConstraints.size(), resultConstraints.front().first.rows());
286 EigenVector newb(resultConstraints.size());
287 for (Eigen::Index i = 0; i < newA.rows(); ++i) {
288 newA.row(i) = resultConstraints[i].first;
289 newb(i) = resultConstraints[i].second;
290 }
291 return std::make_shared<NativePolytope<ValueType>>(EmptyStatus::Nonempty, std::move(newA), std::move(newb));
292 }
293}
294
295template<typename ValueType>
296std::shared_ptr<Polytope<ValueType>> NativePolytope<ValueType>::affineTransformation(std::vector<Point> const& matrix, Point const& vector) const {
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();
300 EigenMatrix eigenMatrix(rows, columns);
301 for (Eigen::Index row = 0; row < rows; ++row) {
302 eigenMatrix.row(row) = storm::adapters::EigenAdapter::toEigenVector(matrix[row]);
303 }
305
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");
309 if (isUniversal()) {
310 return std::make_shared<NativePolytope<ValueType>>(std::vector<Halfspace<ValueType>>());
311 }
312 EigenMatrix newA = A * luMatrix.inverse();
313 EigenVector newb = b + (newA * eigenVector);
314 return std::make_shared<NativePolytope<ValueType>>(emptyStatus, std::move(newA), std::move(newb));
315}
316
317template<typename ValueType>
318std::pair<typename NativePolytope<ValueType>::Point, bool> NativePolytope<ValueType>::optimize(Point const& direction) const {
319 if (isUniversal()) {
320 return std::make_pair(Point(), false);
321 }
322
323 storm::solver::Z3LpSolver<ValueType> solver(storm::solver::OptimizationDirection::Maximize);
324 std::vector<storm::expressions::Variable> variables;
325 variables.reserve(A.cols());
326 for (Eigen::Index i = 0; i < A.cols(); ++i) {
327 variables.push_back(solver.addUnboundedContinuousVariable("x" + std::to_string(i), direction[i]));
328 }
329 std::vector<storm::expressions::Expression> constraints = getConstraints(solver.getManager(), variables);
330 for (auto const& constraint : constraints) {
331 solver.addConstraint("", constraint);
332 }
333 solver.update();
334 solver.optimize();
335 if (solver.isOptimal()) {
336 auto result = std::make_pair(Point(), true);
337 result.first.reserve(variables.size());
338 for (auto const& var : variables) {
339 result.first.push_back(solver.getContinuousValue(var));
340 }
341 return result;
342 } else {
343 // solution is infinity or infeasible
344 return std::make_pair(Point(), false);
345 }
346}
347
348template<typename ValueType>
349std::pair<typename NativePolytope<ValueType>::EigenVector, bool> NativePolytope<ValueType>::optimize(EigenVector const& direction) const {
350 if (isUniversal()) {
351 return std::make_pair(EigenVector(), false);
352 }
353
354 storm::solver::Z3LpSolver<ValueType> solver(storm::solver::OptimizationDirection::Maximize);
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))));
359 }
360 std::vector<storm::expressions::Expression> constraints = getConstraints(solver.getManager(), variables);
361 for (auto const& constraint : constraints) {
362 solver.addConstraint("", constraint);
363 }
364 solver.update();
365 solver.optimize();
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]);
370 }
371 return result;
372 } else {
373 // solution is infinity or infeasible
374 return std::make_pair(EigenVector(), false);
375 }
376}
377
378template<typename ValueType>
380 return true;
381}
382template<typename ValueType>
383std::vector<typename NativePolytope<ValueType>::EigenVector> NativePolytope<ValueType>::getEigenVertices() const {
385 he.generateVerticesFromConstraints(A, b, false);
386 return he.getResultVertices();
387}
388
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()));
396 }
397 return result;
398}
399
400template<typename ValueType>
401std::vector<storm::expressions::Expression> NativePolytope<ValueType>::getConstraints(storm::expressions::ExpressionManager const& manager,
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) {
405 storm::expressions::Expression lhs = manager.rational(A(row, 0)) * variables[0].getExpression();
406 for (Eigen::Index col = 1; col < A.cols(); ++col) {
407 lhs = lhs + manager.rational(A(row, col)) * variables[col].getExpression();
408 }
409 result.push_back(lhs <= manager.rational(b(row)));
410 }
411 return result;
412}
413
414template<typename ValueType>
415std::shared_ptr<Polytope<ValueType>> NativePolytope<ValueType>::clean() {
416 if (isEmpty()) {
417 return create(boost::none, {});
418 }
419 std::shared_ptr<storm::expressions::ExpressionManager> manager(new storm::expressions::ExpressionManager());
420 std::unique_ptr<storm::solver::SmtSolver> solver = storm::utility::solver::SmtSolverFactory().create(*manager);
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);
425 }
426 storm::storage::BitVector keptConstraints(A.rows(), false);
427 for (Eigen::Index row = 0; row < A.rows(); ++row) {
428 storm::expressions::Expression lhs = manager->rational(A(row, 0)) * variables[0].getExpression();
429 for (Eigen::Index col = 1; col < A.cols(); ++col) {
430 lhs = lhs + manager->rational(A(row, col)) * variables[col].getExpression();
431 }
432 solver->push();
433 solver->add(lhs >= manager->rational(b(row)));
434 switch (solver->check()) {
436 keptConstraints.set(row, true);
438 break;
439 default:
440 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected result of SMT solver during emptyness-check of Polytope.");
441 break;
442 }
443 solver->pop();
444 }
445 std::vector<Halfspace<ValueType>> newHalfspaces;
446 newHalfspaces.reserve(keptConstraints.getNumberOfSetBits());
447 for (auto row : keptConstraints) {
448 newHalfspaces.emplace_back(storm::adapters::EigenAdapter::toStdVector(EigenVector(A.row(row))), b(row));
449 }
450 return create(newHalfspaces, boost::none);
451}
452
453template class NativePolytope<double>;
455} // namespace geometry
456} // namespace storage
457} // 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:130
Variable addUnboundedContinuousVariable(std::string const &name, ValueType objectiveFunctionCoefficient=0)
Registers a unbounded continuous variable, i.e.
Definition LpSolver.cpp:56
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:16
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
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:19
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