Skip to content

Commit 8e79958

Browse files
committed
[MLIR][Presburger] Introduce MaybeOptimum type to represent computed optima
This allows to differentiate between the cases where the optimum does not exist due to being unbounded and due to the polytope being empty. Reviewed By: Groverkss Differential Revision: https://reviews.llvm.org/D120127
1 parent c141d15 commit 8e79958

File tree

7 files changed

+210
-89
lines changed

7 files changed

+210
-89
lines changed

mlir/include/mlir/Analysis/Presburger/IntegerPolyhedron.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,8 @@ class IntegerPolyhedron : public PresburgerLocalSpace {
209209
/// constraints. Returns an empty optional if the polyhedron is empty or if
210210
/// the lexmin is unbounded. Symbols are not supported and will result in
211211
/// assert-failure.
212-
Optional<SmallVector<Fraction, 8>> getRationalLexMin() const;
212+
presburger_utils::MaybeOptimum<SmallVector<Fraction, 8>>
213+
getRationalLexMin() const;
213214

214215
/// Swap the posA^th identifier with the posB^th identifier.
215216
virtual void swapId(unsigned posA, unsigned posB);

mlir/include/mlir/Analysis/Presburger/Simplex.h

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "mlir/Analysis/Presburger/Fraction.h"
1919
#include "mlir/Analysis/Presburger/IntegerPolyhedron.h"
2020
#include "mlir/Analysis/Presburger/Matrix.h"
21+
#include "mlir/Analysis/Presburger/Utils.h"
2122
#include "mlir/Support/LogicalResult.h"
2223
#include "llvm/ADT/ArrayRef.h"
2324
#include "llvm/ADT/Optional.h"
@@ -202,14 +203,6 @@ class SimplexBase {
202203
/// Add all the constraints from the given IntegerPolyhedron.
203204
void intersectIntegerPolyhedron(const IntegerPolyhedron &poly);
204205

205-
/// Returns the current sample point, which may contain non-integer (rational)
206-
/// coordinates. Returns an empty optional when the tableau is empty.
207-
///
208-
/// Also returns empty when the big M parameter is used and a variable
209-
/// has a non-zero big M coefficient, meaning its value is infinite or
210-
/// unbounded.
211-
Optional<SmallVector<Fraction, 8>> getRationalSample() const;
212-
213206
/// Print the tableau's internal state.
214207
void print(raw_ostream &os) const;
215208
void dump() const;
@@ -441,9 +434,18 @@ class LexSimplex : public SimplexBase {
441434
unsigned getSnapshot() { return SimplexBase::getSnapshotBasis(); }
442435

443436
/// Return the lexicographically minimum rational solution to the constraints.
444-
Optional<SmallVector<Fraction, 8>> getRationalLexMin();
437+
presburger_utils::MaybeOptimum<SmallVector<Fraction, 8>> getRationalLexMin();
445438

446439
protected:
440+
/// Returns the current sample point, which may contain non-integer (rational)
441+
/// coordinates. Returns an empty optimum when the tableau is empty.
442+
///
443+
/// Returns an unbounded optimum when the big M parameter is used and a
444+
/// variable has a non-zero big M coefficient, meaning its value is infinite
445+
/// or unbounded.
446+
presburger_utils::MaybeOptimum<SmallVector<Fraction, 8>>
447+
getRationalSample() const;
448+
447449
/// Undo the addition of the last constraint. This is only called while
448450
/// rolling back.
449451
void undoLastConstraint() final;
@@ -510,15 +512,16 @@ class Simplex : public SimplexBase {
510512
///
511513
/// Returns a Fraction denoting the optimum, or a null value if no optimum
512514
/// exists, i.e., if the expression is unbounded in this direction.
513-
Optional<Fraction> computeRowOptimum(Direction direction, unsigned row);
515+
presburger_utils::MaybeOptimum<Fraction>
516+
computeRowOptimum(Direction direction, unsigned row);
514517

515518
/// Compute the maximum or minimum value of the given expression, depending on
516519
/// direction. Should not be called when the Simplex is empty.
517520
///
518521
/// Returns a Fraction denoting the optimum, or a null value if no optimum
519522
/// exists, i.e., if the expression is unbounded in this direction.
520-
Optional<Fraction> computeOptimum(Direction direction,
521-
ArrayRef<int64_t> coeffs);
523+
presburger_utils::MaybeOptimum<Fraction>
524+
computeOptimum(Direction direction, ArrayRef<int64_t> coeffs);
522525

523526
/// Returns whether the perpendicular of the specified constraint is a
524527
/// is a direction along which the polytope is bounded.
@@ -537,10 +540,10 @@ class Simplex : public SimplexBase {
537540
void detectRedundant();
538541

539542
/// Returns a (min, max) pair denoting the minimum and maximum integer values
540-
/// of the given expression. If either of the values is unbounded, an empty
541-
/// optional is returned in its place. If the result has min > max then no
542-
/// integer value exists.
543-
std::pair<Optional<int64_t>, Optional<int64_t>>
543+
/// of the given expression. If no integer value exists, both results will be
544+
/// of kind Empty.
545+
std::pair<presburger_utils::MaybeOptimum<int64_t>,
546+
presburger_utils::MaybeOptimum<int64_t>>
544547
computeIntegerBounds(ArrayRef<int64_t> coeffs);
545548

546549
/// Returns true if the polytope is unbounded, i.e., extends to infinity in
@@ -569,6 +572,10 @@ class Simplex : public SimplexBase {
569572
/// None.
570573
Optional<SmallVector<int64_t, 8>> getSamplePointIfIntegral() const;
571574

575+
/// Returns the current sample point, which may contain non-integer (rational)
576+
/// coordinates. Returns an empty optional when the tableau is empty.
577+
Optional<SmallVector<Fraction, 8>> getRationalSample() const;
578+
572579
private:
573580
friend class GBRSimplex;
574581

@@ -610,7 +617,8 @@ class Simplex : public SimplexBase {
610617
///
611618
/// Returns a Fraction denoting the optimum, or a null value if no optimum
612619
/// exists, i.e., if the expression is unbounded in this direction.
613-
Optional<Fraction> computeOptimum(Direction direction, Unknown &u);
620+
presburger_utils::MaybeOptimum<Fraction> computeOptimum(Direction direction,
621+
Unknown &u);
614622

615623
/// Mark the specified unknown redundant. This operation is added to the undo
616624
/// log and will be undone by rollbacks. The specified unknown must be in row

mlir/include/mlir/Analysis/Presburger/Utils.h

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,67 @@ class IntegerPolyhedron;
2222

2323
namespace presburger_utils {
2424

25+
/// This class represents the result of operations optimizing something subject
26+
/// to some constraints. If the constraints were not satisfiable the, kind will
27+
/// be Empty. If the optimum is unbounded, the kind is Unbounded, and if the
28+
/// optimum is bounded, the kind will be Bounded and `optimum` holds the optimal
29+
/// value.
30+
enum class OptimumKind { Empty, Unbounded, Bounded };
31+
template <typename T>
32+
class MaybeOptimum {
33+
public:
34+
private:
35+
OptimumKind kind = OptimumKind::Empty;
36+
T optimum;
37+
38+
public:
39+
MaybeOptimum() = default;
40+
MaybeOptimum(OptimumKind kind) : kind(kind) {
41+
assert(kind != OptimumKind::Bounded &&
42+
"Bounded optima should be constructed by specifying the optimum!");
43+
}
44+
MaybeOptimum(const T &optimum)
45+
: kind(OptimumKind::Bounded), optimum(optimum) {}
46+
47+
OptimumKind getKind() const { return kind; }
48+
bool isBounded() const { return kind == OptimumKind::Bounded; }
49+
bool isUnbounded() const { return kind == OptimumKind::Unbounded; }
50+
bool isEmpty() const { return kind == OptimumKind::Empty; }
51+
52+
Optional<T> getOptimumIfBounded() const { return optimum; }
53+
const T &getBoundedOptimum() const {
54+
assert(kind == OptimumKind::Bounded &&
55+
"This should be called only for bounded optima");
56+
return optimum;
57+
}
58+
T &getBoundedOptimum() {
59+
assert(kind == OptimumKind::Bounded &&
60+
"This should be called only for bounded optima");
61+
return optimum;
62+
}
63+
const T &operator*() const { return getBoundedOptimum(); }
64+
T &operator*() { return getBoundedOptimum(); }
65+
const T *operator->() const { return &getBoundedOptimum(); }
66+
T *operator->() { return &getBoundedOptimum(); }
67+
bool operator==(const MaybeOptimum<T> &other) const {
68+
if (kind != other.kind)
69+
return false;
70+
if (kind != OptimumKind::Bounded)
71+
return true;
72+
return optimum == other.optimum;
73+
}
74+
75+
// Given f that takes a T and returns a U, convert this `MaybeOptimum<T>` to
76+
// a `MaybeOptimum<U>` by applying `f` to the bounded optimum if it exists, or
77+
// returning a MaybeOptimum of the same kind otherwise.
78+
template <class Function>
79+
auto map(const Function &f) const & -> MaybeOptimum<decltype(f(optimum))> {
80+
if (kind == OptimumKind::Bounded)
81+
return f(optimum);
82+
return kind;
83+
}
84+
};
85+
2586
/// `ReprKind` enum is used to set the constraint type in `MaybeLocalRepr`.
2687
enum class ReprKind { Inequality, Equality, None };
2788

mlir/lib/Analysis/Presburger/IntegerPolyhedron.cpp

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,14 @@ bool IntegerPolyhedron::isSubsetOf(const IntegerPolyhedron &other) const {
7272
return PresburgerSet(*this).isSubsetOf(PresburgerSet(other));
7373
}
7474

75-
Optional<SmallVector<Fraction, 8>>
75+
MaybeOptimum<SmallVector<Fraction, 8>>
7676
IntegerPolyhedron::getRationalLexMin() const {
7777
assert(getNumSymbolIds() == 0 && "Symbols are not supported!");
78-
Optional<SmallVector<Fraction, 8>> maybeLexMin =
78+
MaybeOptimum<SmallVector<Fraction, 8>> maybeLexMin =
7979
LexSimplex(*this).getRationalLexMin();
8080

81-
if (!maybeLexMin)
82-
return {};
81+
if (!maybeLexMin.isBounded())
82+
return maybeLexMin;
8383

8484
// The Simplex returns the lexmin over all the variables including locals. But
8585
// locals are not actually part of the space and should not be returned in the
@@ -1032,20 +1032,23 @@ Optional<uint64_t> IntegerPolyhedron::computeVolume() const {
10321032
bool hasUnboundedId = false;
10331033
for (unsigned i = 0, e = getNumDimAndSymbolIds(); i < e; ++i) {
10341034
dim[i] = 1;
1035-
Optional<int64_t> min, max;
1035+
MaybeOptimum<int64_t> min, max;
10361036
std::tie(min, max) = simplex.computeIntegerBounds(dim);
10371037
dim[i] = 0;
10381038

1039+
assert((!min.isEmpty() && !max.isEmpty()) &&
1040+
"Polytope should be rationally non-empty!");
1041+
10391042
// One of the dimensions is unbounded. Note this fact. We will return
10401043
// unbounded if none of the other dimensions makes the volume zero.
1041-
if (!min || !max) {
1044+
if (min.isUnbounded() || max.isUnbounded()) {
10421045
hasUnboundedId = true;
10431046
continue;
10441047
}
10451048

10461049
// In this case there are no valid integer points and the volume is
10471050
// definitely zero.
1048-
if (*min > *max)
1051+
if (min.getBoundedOptimum() > max.getBoundedOptimum())
10491052
return 0;
10501053

10511054
count *= (*max - *min + 1);

0 commit comments

Comments
 (0)