4#include <unordered_map>
18template<
typename ValueType>
20 STORM_LOG_ASSERT(!points.empty(),
"Invoked QuickHull with empty set of points.");
22 const uint_fast64_t dimension = points.front().rows();
25 handle1DPoints(points, generateRelevantVerticesAndVertexSets);
28 std::vector<uint_fast64_t> vertexIndices;
29 if (this->findInitialVertices(points, vertexIndices)) {
31 EigenVector insidePoint(EigenVector::Zero(dimension));
32 for (uint_fast64_t vertexIndex : vertexIndices) {
33 insidePoint += points[vertexIndex];
35 insidePoint /= storm::utility::convertNumber<ValueType>(
static_cast<uint_fast64_t
>(vertexIndices.size()));
38 std::vector<Facet> facets = computeInitialFacets(points, vertexIndices, insidePoint);
42 this->extendMesh(points, facets, currentFacets, insidePoint);
45 this->getPolytopeFromMesh(points, facets, currentFacets, generateRelevantVerticesAndVertexSets);
46 STORM_LOG_DEBUG(
"QuickHull invokation yielded " << resultMatrix.rows() <<
" constraints");
49 handleAffineDependentPoints(points, generateRelevantVerticesAndVertexSets);
54template<
typename ValueType>
56 return this->resultMatrix;
59template<
typename ValueType>
61 return this->resultVector;
64template<
typename ValueType>
66 return this->relevantVertices;
69template<
typename ValueType>
71 return this->vertexSets;
74template<
typename ValueType>
76 ValueType minValue = points.front()(0);
77 ValueType maxValue = points.front()(0);
78 uint_fast64_t minIndex = 0;
79 uint_fast64_t maxIndex = 0;
80 for (uint_fast64_t pointIndex = 1; pointIndex < points.size(); ++pointIndex) {
81 ValueType
const& pointValue = points[pointIndex](0);
82 if (pointValue < minValue) {
83 minValue = pointValue;
84 minIndex = pointIndex;
86 if (pointValue > maxValue) {
87 maxValue = pointValue;
88 maxIndex = pointIndex;
91 resultMatrix = EigenMatrix(2, 1);
92 resultMatrix(0, 0) = -storm::utility::one<ValueType>();
93 resultMatrix(1, 0) = storm::utility::one<ValueType>();
94 resultVector = EigenVector(2);
95 resultVector(0) = -minValue;
96 resultVector(1) = maxValue;
97 if (generateRelevantVerticesAndVertexSets) {
98 relevantVertices.push_back(points[minIndex]);
99 std::vector<uint_fast64_t> minVertexSet(1, 0);
100 vertexSets.push_back(std::move(minVertexSet));
101 if (minIndex != maxIndex && points[minIndex] != points[maxIndex]) {
102 relevantVertices.push_back(points[maxIndex]);
103 std::vector<uint_fast64_t> maxVertexSet(1, 1);
104 vertexSets.push_back(std::move(maxVertexSet));
106 vertexSets.push_back(vertexSets.front());
111template<
typename ValueType>
112bool QuickHull<ValueType>::affineFilter(std::vector<uint_fast64_t>
const& subset, uint_fast64_t
const& item, std::vector<EigenVector>
const& points) {
113 EigenMatrix vectorMatrix(points[item].rows() + 1, subset.size() + 1);
114 for (uint_fast64_t
i = 0;
i < subset.size(); ++
i) {
115 vectorMatrix.col(
i) << points[subset[
i]], storm::utility::one<ValueType>();
117 vectorMatrix.col(subset.size()) << points[item], storm::utility::one<ValueType>();
118 return (vectorMatrix.fullPivLu().rank() > (Eigen::Index)subset.size());
121template<
typename ValueType>
122void QuickHull<ValueType>::handleAffineDependentPoints(std::vector<EigenVector>& points,
bool generateRelevantVerticesAndVertexSets) {
123 bool pointsAffineDependent =
true;
124 std::vector<std::pair<EigenVector, ValueType>> additionalConstraints;
125 while (pointsAffineDependent) {
127 const uint_fast64_t dimension = points.front().rows();
128 EigenVector refPoint = points.front();
130 if (points.size() == 1) {
131 normal.resize(dimension);
132 normal(0) = storm::utility::one<ValueType>();
134 EigenMatrix constraints(points.size() - 1, dimension);
135 for (
unsigned row = 1; row < points.size(); ++row) {
136 constraints.row(row - 1) = points[row] - refPoint;
138 normal = constraints.fullPivLu().kernel().col(0);
142 if (normal.isZero()) {
143 pointsAffineDependent =
false;
145 points.push_back(refPoint + normal);
147 additionalConstraints.emplace_back(std::move(normal), std::move(offset));
151 generateHalfspacesFromPoints(points, generateRelevantVerticesAndVertexSets);
154 uint_fast64_t numOfAdditionalConstraints = additionalConstraints.size();
155 uint_fast64_t numOfRegularConstraints = resultMatrix.rows();
156 EigenMatrix newA(numOfRegularConstraints + numOfAdditionalConstraints, resultMatrix.cols());
157 EigenVector newb(
static_cast<unsigned long int>(numOfRegularConstraints + numOfAdditionalConstraints));
158 uint_fast64_t row = 0;
159 for (; row < numOfRegularConstraints; ++row) {
160 newA.row(row) = resultMatrix.row(row);
161 newb(row) = resultVector(row);
163 for (; row < numOfRegularConstraints + numOfAdditionalConstraints; ++row) {
164 newA.row(row) = std::move(additionalConstraints[row - numOfRegularConstraints].first);
165 newb(row) = additionalConstraints[row - numOfRegularConstraints].second;
167 resultMatrix = std::move(newA);
168 resultVector = std::move(newb);
172 for (uint_fast64_t pointIndex = 0; pointIndex < points.size(); ++pointIndex) {
173 for (Eigen::Index row = 0; row < resultMatrix.rows(); ++row) {
174 if ((resultMatrix.row(row) * points[pointIndex])(0) > resultVector(row)) {
175 keptPoints.set(pointIndex,
false);
182 if (generateRelevantVerticesAndVertexSets) {
184 for (uint_fast64_t vertexIndex = 0; vertexIndex < relevantVertices.size(); ++vertexIndex) {
185 for (Eigen::Index row = 0; row < resultMatrix.rows(); ++row) {
186 if ((resultMatrix.row(row) * relevantVertices[vertexIndex])(0) > resultVector(row)) {
187 keptVertices.set(vertexIndex,
false);
194 STORM_LOG_WARN(
"Can not retrieve vertex sets for degenerated polytope (not implemented)");
199template<
typename ValueType>
200bool QuickHull<ValueType>::findInitialVertices(std::vector<EigenVector>& points, std::vector<uint_fast64_t>& verticesOfInitialPolytope)
const {
201 const uint_fast64_t dimension = points.front().rows();
202 if (points.size() < dimension + 1) {
209 for (uint_fast64_t dim = 0; dim < dimension; ++dim) {
210 if (!notGoodCandidates.empty()) {
211 uint_fast64_t minIndex = *notGoodCandidates.begin();
212 uint_fast64_t maxIndex = *notGoodCandidates.begin();
213 for (uint_fast64_t pointIndex : notGoodCandidates) {
214 if (points[minIndex](dim) > points[pointIndex](dim)) {
215 minIndex = pointIndex;
217 if (points[maxIndex](dim) < points[pointIndex](dim)) {
218 maxIndex = pointIndex;
221 notGoodCandidates.set(minIndex,
false);
222 notGoodCandidates.set(maxIndex,
false);
229 for (
auto goodCandidate : goodCandidates) {
230 if (goodCandidate >= numOfGoodCandidates) {
231 uint_fast64_t notGoodCandidate = *notGoodCandidates.begin();
232 assert(notGoodCandidate < numOfGoodCandidates);
233 std::swap(points[notGoodCandidate], points[goodCandidate]);
234 notGoodCandidates.set(notGoodCandidate,
false);
239 if (subsetEnum.setToFirstSubset()) {
240 verticesOfInitialPolytope = subsetEnum.getCurrentSubset();
247template<
typename ValueType>
248std::vector<typename QuickHull<ValueType>::Facet> QuickHull<ValueType>::computeInitialFacets(std::vector<EigenVector>
const& points,
249 std::vector<uint_fast64_t>
const& verticesOfInitialPolytope,
250 EigenVector
const& insidePoint)
const {
251 const uint_fast64_t dimension = points.front().rows();
252 assert(verticesOfInitialPolytope.size() == dimension + 1);
253 std::vector<Facet> result;
254 result.reserve(dimension + 1);
256 if (!subsetEnum.setToFirstSubset()) {
257 STORM_LOG_THROW(
false, storm::exceptions::UnexpectedException,
"Could not find an initial subset.");
262 std::vector<uint_fast64_t>
const& subset(subsetEnum.getCurrentSubset());
263 newFacet.points.reserve(subset.size());
264 for (uint_fast64_t
i : subset) {
265 newFacet.points.push_back(verticesOfInitialPolytope[
i]);
268 newFacet.neighbors.reserve(dimension);
269 for (uint_fast64_t
i = 0;
i < dimension + 1; ++
i) {
270 if (
i != result.size()) {
271 newFacet.neighbors.push_back(
i);
275 computeNormalAndOffsetOfFacet(points, insidePoint, newFacet);
277 result.push_back(std::move(newFacet));
278 }
while (subsetEnum.incrementSubset());
279 assert(result.size() == dimension + 1);
283template<
typename ValueType>
284void QuickHull<ValueType>::computeNormalAndOffsetOfFacet(std::vector<EigenVector>
const& points, EigenVector
const& insidePoint, Facet& facet)
const {
285 const uint_fast64_t dimension = points.front().rows();
286 assert(facet.points.size() == dimension);
287 EigenVector
const& refPoint = points[facet.points.back()];
288 EigenMatrix constraints(dimension - 1, dimension);
289 for (
unsigned row = 0; row < dimension - 1; ++row) {
290 constraints.row(row) = (points[facet.points[row]] - refPoint);
292 facet.normal = constraints.fullPivLu().kernel().col(0);
293 facet.offset = facet.normal.dot(refPoint);
296 if (facet.normal.dot(insidePoint) > facet.offset) {
297 facet.normal = -facet.normal;
298 facet.offset = -facet.offset;
302template<
typename ValueType>
303void QuickHull<ValueType>::extendMesh(std::vector<EigenVector>& points, std::vector<Facet>& facets,
storm::storage::BitVector& currentFacets,
304 EigenVector& insidePoint)
const {
307 for (uint_fast64_t facetIndex : currentFacets) {
308 computeOutsideSetOfFacet(facets[facetIndex], currentOutsidePoints, points);
311 for (uint_fast64_t facetCount = currentFacets.
getNextSetIndex(0); facetCount != currentFacets.
size();
314 currentOutsidePoints.clear();
316 if (!facets[facetCount].outsideSet.empty()) {
317 uint_fast64_t numberOfNewFacets = 0;
319 uint_fast64_t farAwayPointIndex = facets[facetCount].maxOutsidePointIndex;
321 std::set<uint_fast64_t> visibleSet = getVisibleSet(facets, facetCount, points[farAwayPointIndex]);
322 std::set<uint_fast64_t> invisibleSet = getInvisibleNeighbors(facets, visibleSet);
323 for (
auto invisFacetIt = invisibleSet.begin(); invisFacetIt != invisibleSet.end(); ++invisFacetIt) {
324 for (
auto visFacetIt = visibleSet.begin(); visFacetIt != visibleSet.end(); ++visFacetIt) {
325 if (std::find(facets[*invisFacetIt].neighbors.begin(), facets[*invisFacetIt].neighbors.end(), *visFacetIt) !=
326 facets[*invisFacetIt].neighbors.end()) {
329 newFacet.points = getCommonPoints(facets[*invisFacetIt], facets[*visFacetIt]);
330 newFacet.points.push_back(farAwayPointIndex);
332 replaceFacetNeighbor(facets, *visFacetIt, facets.size(), *invisFacetIt);
333 newFacet.neighbors.push_back(*invisFacetIt);
335 computeNormalAndOffsetOfFacet(points, insidePoint, newFacet);
338 facets.push_back(newFacet);
339 currentFacets.
resize(currentFacets.
size() + 1,
true);
346 for (
auto visibleFacet : visibleSet) {
347 for (uint_fast64_t outsidePoint : facets[visibleFacet].outsideSet) {
348 currentOutsidePoints.set(outsidePoint,
true);
350 currentFacets.
set(visibleFacet,
false);
353 for (uint_fast64_t facetIndex : currentFacets) {
354 computeOutsideSetOfFacet(facets[facetIndex], currentOutsidePoints, points);
358 setNeighborhoodOfNewFacets(facets, facets.size() - numberOfNewFacets, points.front().rows());
363template<
typename ValueType>
364void QuickHull<ValueType>::getPolytopeFromMesh(std::vector<EigenVector>
const& points, std::vector<Facet>
const& facets,
367 for (
auto facet : currentFacets) {
368 hyperplaneCollector.
insert(std::move(facets[facet].normal), std::move(facets[facet].offset),
369 generateRelevantVerticesAndVertexSets ? &facets[facet].points : nullptr);
372 if (generateRelevantVerticesAndVertexSets) {
376 std::vector<uint_fast64_t> hyperplanesOnVertexCounter(points.size(), 0);
377 for (
auto& vertexVector : vertexSets) {
378 std::set<uint_fast64_t> vertexSet;
379 for (
auto const&
i : vertexVector) {
380 if (vertexSet.insert(
i).second) {
381 ++hyperplanesOnVertexCounter[
i];
384 vertexVector.assign(vertexSet.begin(), vertexSet.end());
389 std::unordered_map<EigenVector, std::vector<uint_fast64_t>> relevantVerticesMap;
390 relevantVerticesMap.reserve(points.size());
391 for (uint_fast64_t vertexIndex = 0; vertexIndex < hyperplanesOnVertexCounter.size(); ++vertexIndex) {
392 if ((Eigen::Index)hyperplanesOnVertexCounter[vertexIndex] >= points.front().rows()) {
393 auto mapEntry = relevantVerticesMap
394 .insert(
typename std::unordered_map<EigenVector, std::vector<uint_fast64_t>>::value_type(points[vertexIndex],
395 std::vector<uint_fast64_t>()))
397 mapEntry->second.push_back(vertexIndex);
401 std::vector<uint_fast64_t> oldToNewIndexMapping(points.size(), points.size());
402 relevantVertices.clear();
403 relevantVertices.reserve(relevantVerticesMap.size());
404 for (
auto const& mapEntry : relevantVerticesMap) {
405 for (
auto const& oldIndex : mapEntry.second) {
406 oldToNewIndexMapping[oldIndex] = relevantVertices.size();
408 relevantVertices.push_back(mapEntry.first);
411 for (
auto& vertexVector : vertexSets) {
412 std::set<uint_fast64_t> vertexSet;
413 for (
auto const& oldIndex : vertexVector) {
414 if ((Eigen::Index)hyperplanesOnVertexCounter[oldIndex] >= points.front().rows()) {
415 vertexSet.insert(oldToNewIndexMapping[oldIndex]);
418 vertexVector.assign(vertexSet.begin(), vertexSet.end());
421 relevantVertices.clear();
425 resultMatrix = std::move(matrixVector.first);
426 resultVector = std::move(matrixVector.second);
429template<
typename ValueType>
430std::set<uint_fast64_t> QuickHull<ValueType>::getVisibleSet(std::vector<Facet>
const& facets, uint_fast64_t
const& startIndex, EigenVector
const& point)
const {
431 std::set<uint_fast64_t> facetsToCheck;
432 std::set<uint_fast64_t> facetsChecked;
433 std::set<uint_fast64_t> visibleSet;
434 facetsChecked.insert(startIndex);
435 visibleSet.insert(startIndex);
436 for (uint_fast64_t
i = 0;
i < facets[startIndex].neighbors.size(); ++
i) {
437 facetsToCheck.insert(facets[startIndex].neighbors[
i]);
439 while (!facetsToCheck.empty()) {
440 auto elementIt = facetsToCheck.begin();
441 if (point.dot(facets[*elementIt].normal) > facets[*elementIt].offset) {
442 visibleSet.insert(*elementIt);
443 for (uint_fast64_t
i = 0;
i < facets[*elementIt].neighbors.size(); ++
i) {
444 if (facetsChecked.find(facets[*elementIt].neighbors[
i]) == facetsChecked.end()) {
445 facetsToCheck.insert(facets[*elementIt].neighbors[
i]);
449 facetsChecked.insert(*elementIt);
450 facetsToCheck.erase(elementIt);
455template<
typename ValueType>
456void QuickHull<ValueType>::setNeighborhoodOfNewFacets(std::vector<Facet>& facets, uint_fast64_t firstNewFacet, uint_fast64_t dimension)
const {
457 for (uint_fast64_t currentFacet = firstNewFacet; currentFacet < facets.size(); ++currentFacet) {
458 for (uint_fast64_t otherFacet = currentFacet + 1; otherFacet < facets.size(); ++otherFacet) {
459 if (getCommonPoints(facets[currentFacet], facets[otherFacet]).size() >= dimension - 1) {
460 facets[currentFacet].neighbors.push_back(otherFacet);
461 facets[otherFacet].neighbors.push_back(currentFacet);
467template<
typename ValueType>
468void QuickHull<ValueType>::replaceFacetNeighbor(std::vector<Facet>& facets, uint_fast64_t oldFacetIndex, uint_fast64_t newFacetIndex,
469 uint_fast64_t neighborIndex)
const {
470 auto facetInNeighborListIt = std::find(facets[neighborIndex].neighbors.begin(), facets[neighborIndex].neighbors.end(), oldFacetIndex);
471 if (facetInNeighborListIt != facets[neighborIndex].neighbors.end()) {
472 *facetInNeighborListIt = newFacetIndex;
476template<
typename ValueType>
478 std::vector<EigenVector>
const& points)
const {
479 ValueType maxMultiplicationResult = facet.offset;
480 for (uint_fast64_t pointIndex : currentOutsidePoints) {
481 ValueType multiplicationResult = points[pointIndex].dot(facet.normal);
482 if (multiplicationResult > facet.offset) {
483 currentOutsidePoints.
set(pointIndex,
false);
484 facet.outsideSet.push_back(pointIndex);
485 if (multiplicationResult > maxMultiplicationResult) {
486 maxMultiplicationResult = multiplicationResult;
487 facet.maxOutsidePointIndex = pointIndex;
493template<
typename ValueType>
494std::vector<uint_fast64_t> QuickHull<ValueType>::getCommonPoints(Facet
const& lhs, Facet
const& rhs)
const {
495 std::vector<uint_fast64_t> commonPoints;
496 for (uint_fast64_t lhsPoint : lhs.points) {
497 for (uint_fast64_t rhsPoint : rhs.points) {
498 if (lhsPoint == rhsPoint) {
499 commonPoints.push_back(lhsPoint);
506template<
typename ValueType>
507std::set<uint_fast64_t> QuickHull<ValueType>::getInvisibleNeighbors(std::vector<Facet>& facets, std::set<uint_fast64_t>
const& visibleSet)
const {
508 std::set<uint_fast64_t> invisibleNeighbors;
509 for (
auto currentFacetIt = visibleSet.begin(); currentFacetIt != visibleSet.end(); ++currentFacetIt) {
510 for (uint_fast64_t currentNeighbor = 0; currentNeighbor < facets[*currentFacetIt].neighbors.size(); ++currentNeighbor) {
511 if (visibleSet.find(facets[*currentFacetIt].neighbors[currentNeighbor]) == visibleSet.end()) {
512 invisibleNeighbors.insert(facets[*currentFacetIt].neighbors[currentNeighbor]);
516 return invisibleNeighbors;
519template class QuickHull<double>;
520template class QuickHull<storm::RationalNumber>;
A bit vector that is internally represented as a vector of 64-bit values.
uint64_t getNextSetIndex(uint64_t startingIndex) const
Retrieves the index of the bit that is the next bit set to true in the bit vector.
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.
size_t size() const
Retrieves the number of bits this bit vector can store.
void resize(uint64_t newLength, bool init=false)
Resizes the bit vector to hold the given new number of bits.
This class can be used to collect a set of hyperplanes (without duplicates).
bool insert(EigenVector const &normal, ValueType const &offset, std::vector< uint_fast64_t > const *indexList=nullptr)
std::vector< std::vector< uint_fast64_t > > getIndexLists() const
std::pair< EigenMatrix, EigenVector > getCollectedHyperplanesAsMatrixVector() const
Eigen::Matrix< ValueType, Eigen::Dynamic, Eigen::Dynamic > EigenMatrix
std::vector< EigenVector > & getRelevantVertices()
Returns the set of vertices which are not redundant.
Eigen::Matrix< ValueType, Eigen::Dynamic, 1 > EigenVector
void generateHalfspacesFromPoints(std::vector< EigenVector > &points, bool generateRelevantVerticesAndVertexSets)
std::vector< std::vector< std::uint_fast64_t > > & getVertexSets()
Returns for each hyperplane the set of vertices that lie on that hyperplane.
EigenMatrix & getResultMatrix()
EigenVector & getResultVector()
This class can be used to enumerate all k-sized subsets of {0,...,n-1}.
#define STORM_LOG_WARN(message)
#define STORM_LOG_DEBUG(message)
#define STORM_LOG_ASSERT(cond, message)
#define STORM_LOG_THROW(cond, exception, message)
SFTBDDChecker::ValueType ValueType
void filterVectorInPlace(std::vector< Type > &v, storm::storage::BitVector const &filter)