llvm-project/mlir/unittests/Analysis/PresburgerSetTest.cpp
Arjun P 9f32f1d6fb [MLIR] Support checking if two FlatAffineConstraints are equal
This patch adds support for checking if two PresburgerSets are equal. In particular, one can check if two FlatAffineConstraints are equal by constructing PrebsurgerSets from them and comparing these.

Reviewed By: ftynse

Differential Revision: https://reviews.llvm.org/D94915
2021-01-18 21:46:01 +05:30

595 lines
25 KiB
C++

//===- SetTest.cpp - Tests for PresburgerSet ------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains tests for PresburgerSet. The tests for union,
// intersection, subtract, and complement work by computing the operation on
// two sets and checking, for a set of points, that the resulting set contains
// the point iff the result is supposed to contain it. The test for isEqual just
// checks if the result for two sets matches the expected result.
//
//===----------------------------------------------------------------------===//
#include "mlir/Analysis/PresburgerSet.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
namespace mlir {
/// Compute the union of s and t, and check that each of the given points
/// belongs to the union iff it belongs to at least one of s and t.
static void testUnionAtPoints(PresburgerSet s, PresburgerSet t,
ArrayRef<SmallVector<int64_t, 4>> points) {
PresburgerSet unionSet = s.unionSet(t);
for (const SmallVector<int64_t, 4> &point : points) {
bool inS = s.containsPoint(point);
bool inT = t.containsPoint(point);
bool inUnion = unionSet.containsPoint(point);
EXPECT_EQ(inUnion, inS || inT);
}
}
/// Compute the intersection of s and t, and check that each of the given points
/// belongs to the intersection iff it belongs to both s and t.
static void testIntersectAtPoints(PresburgerSet s, PresburgerSet t,
ArrayRef<SmallVector<int64_t, 4>> points) {
PresburgerSet intersection = s.intersect(t);
for (const SmallVector<int64_t, 4> &point : points) {
bool inS = s.containsPoint(point);
bool inT = t.containsPoint(point);
bool inIntersection = intersection.containsPoint(point);
EXPECT_EQ(inIntersection, inS && inT);
}
}
/// Compute the set difference s \ t, and check that each of the given points
/// belongs to the difference iff it belongs to s and does not belong to t.
static void testSubtractAtPoints(PresburgerSet s, PresburgerSet t,
ArrayRef<SmallVector<int64_t, 4>> points) {
PresburgerSet diff = s.subtract(t);
for (const SmallVector<int64_t, 4> &point : points) {
bool inS = s.containsPoint(point);
bool inT = t.containsPoint(point);
bool inDiff = diff.containsPoint(point);
if (inT)
EXPECT_FALSE(inDiff);
else
EXPECT_EQ(inDiff, inS);
}
}
/// Compute the complement of s, and check that each of the given points
/// belongs to the complement iff it does not belong to s.
static void testComplementAtPoints(PresburgerSet s,
ArrayRef<SmallVector<int64_t, 4>> points) {
PresburgerSet complement = s.complement();
complement.complement();
for (const SmallVector<int64_t, 4> &point : points) {
bool inS = s.containsPoint(point);
bool inComplement = complement.containsPoint(point);
if (inS)
EXPECT_FALSE(inComplement);
else
EXPECT_TRUE(inComplement);
}
}
/// Construct a FlatAffineConstraints from a set of inequality and
/// equality constraints.
static FlatAffineConstraints
makeFACFromConstraints(unsigned dims, ArrayRef<SmallVector<int64_t, 4>> ineqs,
ArrayRef<SmallVector<int64_t, 4>> eqs) {
FlatAffineConstraints fac(ineqs.size(), eqs.size(), dims + 1, dims);
for (const SmallVector<int64_t, 4> &eq : eqs)
fac.addEquality(eq);
for (const SmallVector<int64_t, 4> &ineq : ineqs)
fac.addInequality(ineq);
return fac;
}
static FlatAffineConstraints
makeFACFromIneqs(unsigned dims, ArrayRef<SmallVector<int64_t, 4>> ineqs) {
return makeFACFromConstraints(dims, ineqs, {});
}
static PresburgerSet makeSetFromFACs(unsigned dims,
ArrayRef<FlatAffineConstraints> facs) {
PresburgerSet set = PresburgerSet::getEmptySet(dims);
for (const FlatAffineConstraints &fac : facs)
set.unionFACInPlace(fac);
return set;
}
TEST(SetTest, containsPoint) {
PresburgerSet setA =
makeSetFromFACs(1, {
makeFACFromIneqs(1, {{1, -2}, // x >= 2.
{-1, 8}}), // x <= 8.
makeFACFromIneqs(1, {{1, -10}, // x >= 10.
{-1, 20}}), // x <= 20.
});
for (unsigned x = 0; x <= 21; ++x) {
if ((2 <= x && x <= 8) || (10 <= x && x <= 20))
EXPECT_TRUE(setA.containsPoint({x}));
else
EXPECT_FALSE(setA.containsPoint({x}));
}
// A parallelogram with vertices {(3, 1), (10, -6), (24, 8), (17, 15)} union
// a square with opposite corners (2, 2) and (10, 10).
PresburgerSet setB =
makeSetFromFACs(2, {makeFACFromIneqs(2,
{
{1, 1, -2}, // x + y >= 4.
{-1, -1, 30}, // x + y <= 32.
{1, -1, 0}, // x - y >= 2.
{-1, 1, 10}, // x - y <= 16.
}),
makeFACFromIneqs(2, {
{1, 0, -2}, // x >= 2.
{0, 1, -2}, // y >= 2.
{-1, 0, 10}, // x <= 10.
{0, -1, 10} // y <= 10.
})});
for (unsigned x = 1; x <= 25; ++x) {
for (unsigned y = -6; y <= 16; ++y) {
if (4 <= x + y && x + y <= 32 && 2 <= x - y && x - y <= 16)
EXPECT_TRUE(setB.containsPoint({x, y}));
else if (2 <= x && x <= 10 && 2 <= y && y <= 10)
EXPECT_TRUE(setB.containsPoint({x, y}));
else
EXPECT_FALSE(setB.containsPoint({x, y}));
}
}
}
TEST(SetTest, Union) {
PresburgerSet set =
makeSetFromFACs(1, {
makeFACFromIneqs(1, {{1, -2}, // x >= 2.
{-1, 8}}), // x <= 8.
makeFACFromIneqs(1, {{1, -10}, // x >= 10.
{-1, 20}}), // x <= 20.
});
// Universe union set.
testUnionAtPoints(PresburgerSet::getUniverse(1), set,
{{1}, {2}, {8}, {9}, {10}, {20}, {21}});
// empty set union set.
testUnionAtPoints(PresburgerSet::getEmptySet(1), set,
{{1}, {2}, {8}, {9}, {10}, {20}, {21}});
// empty set union Universe.
testUnionAtPoints(PresburgerSet::getEmptySet(1),
PresburgerSet::getUniverse(1), {{1}, {2}, {0}, {-1}});
// Universe union empty set.
testUnionAtPoints(PresburgerSet::getUniverse(1),
PresburgerSet::getEmptySet(1), {{1}, {2}, {0}, {-1}});
// empty set union empty set.
testUnionAtPoints(PresburgerSet::getEmptySet(1),
PresburgerSet::getEmptySet(1), {{1}, {2}, {0}, {-1}});
}
TEST(SetTest, Intersect) {
PresburgerSet set =
makeSetFromFACs(1, {
makeFACFromIneqs(1, {{1, -2}, // x >= 2.
{-1, 8}}), // x <= 8.
makeFACFromIneqs(1, {{1, -10}, // x >= 10.
{-1, 20}}), // x <= 20.
});
// Universe intersection set.
testIntersectAtPoints(PresburgerSet::getUniverse(1), set,
{{1}, {2}, {8}, {9}, {10}, {20}, {21}});
// empty set intersection set.
testIntersectAtPoints(PresburgerSet::getEmptySet(1), set,
{{1}, {2}, {8}, {9}, {10}, {20}, {21}});
// empty set intersection Universe.
testIntersectAtPoints(PresburgerSet::getEmptySet(1),
PresburgerSet::getUniverse(1), {{1}, {2}, {0}, {-1}});
// Universe intersection empty set.
testIntersectAtPoints(PresburgerSet::getUniverse(1),
PresburgerSet::getEmptySet(1), {{1}, {2}, {0}, {-1}});
// Universe intersection Universe.
testIntersectAtPoints(PresburgerSet::getUniverse(1),
PresburgerSet::getUniverse(1), {{1}, {2}, {0}, {-1}});
}
TEST(SetTest, Subtract) {
// The interval [2, 8] minus
// the interval [10, 20].
testSubtractAtPoints(
makeSetFromFACs(1, {makeFACFromIneqs(1, {})}),
makeSetFromFACs(1,
{
makeFACFromIneqs(1, {{1, -2}, // x >= 2.
{-1, 8}}), // x <= 8.
makeFACFromIneqs(1, {{1, -10}, // x >= 10.
{-1, 20}}), // x <= 20.
}),
{{1}, {2}, {8}, {9}, {10}, {20}, {21}});
// ((-infinity, 0] U [3, 4] U [6, 7]) - ([2, 3] U [5, 6])
testSubtractAtPoints(
makeSetFromFACs(1,
{
makeFACFromIneqs(1,
{
{-1, 0} // x <= 0.
}),
makeFACFromIneqs(1,
{
{1, -3}, // x >= 3.
{-1, 4} // x <= 4.
}),
makeFACFromIneqs(1,
{
{1, -6}, // x >= 6.
{-1, 7} // x <= 7.
}),
}),
makeSetFromFACs(1, {makeFACFromIneqs(1,
{
{1, -2}, // x >= 2.
{-1, 3}, // x <= 3.
}),
makeFACFromIneqs(1,
{
{1, -5}, // x >= 5.
{-1, 6} // x <= 6.
})}),
{{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}});
// Expected result is {[x, y] : x > y}, i.e., {[x, y] : x >= y + 1}.
testSubtractAtPoints(
makeSetFromFACs(2, {makeFACFromIneqs(2,
{
{1, -1, 0} // x >= y.
})}),
makeSetFromFACs(2, {makeFACFromIneqs(2,
{
{1, 1, 0} // x >= -y.
})}),
{{0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}});
// A rectangle with corners at (2, 2) and (10, 10), minus
// a rectangle with corners at (5, -10) and (7, 100).
// This splits the former rectangle into two halves, (2, 2) to (5, 10) and
// (7, 2) to (10, 10).
testSubtractAtPoints(
makeSetFromFACs(2, {makeFACFromIneqs(2,
{
{1, 0, -2}, // x >= 2.
{0, 1, -2}, // y >= 2.
{-1, 0, 10}, // x <= 10.
{0, -1, 10} // y <= 10.
})}),
makeSetFromFACs(2, {makeFACFromIneqs(2,
{
{1, 0, -5}, // x >= 5.
{0, 1, 10}, // y >= -10.
{-1, 0, 7}, // x <= 7.
{0, -1, 100}, // y <= 100.
})}),
{{1, 2}, {2, 2}, {4, 2}, {5, 2}, {7, 2}, {8, 2}, {11, 2},
{1, 1}, {2, 1}, {4, 1}, {5, 1}, {7, 1}, {8, 1}, {11, 1},
{1, 10}, {2, 10}, {4, 10}, {5, 10}, {7, 10}, {8, 10}, {11, 10},
{1, 11}, {2, 11}, {4, 11}, {5, 11}, {7, 11}, {8, 11}, {11, 11}});
// A rectangle with corners at (2, 2) and (10, 10), minus
// a rectangle with corners at (5, 4) and (7, 8).
// This creates a hole in the middle of the former rectangle, and the
// resulting set can be represented as a union of four rectangles.
testSubtractAtPoints(
makeSetFromFACs(2, {makeFACFromIneqs(2,
{
{1, 0, -2}, // x >= 2.
{0, 1, -2}, // y >= 2.
{-1, 0, 10}, // x <= 10.
{0, -1, 10} // y <= 10.
})}),
makeSetFromFACs(2, {makeFACFromIneqs(2,
{
{1, 0, -5}, // x >= 5.
{0, 1, -4}, // y >= 4.
{-1, 0, 7}, // x <= 7.
{0, -1, 8}, // y <= 8.
})}),
{{1, 1},
{2, 2},
{10, 10},
{11, 11},
{5, 4},
{7, 4},
{5, 8},
{7, 8},
{4, 4},
{8, 4},
{4, 8},
{8, 8}});
// The second set is a superset of the first one, since on the line x + y = 0,
// y <= 1 is equivalent to x >= -1. So the result is empty.
testSubtractAtPoints(
makeSetFromFACs(2, {makeFACFromConstraints(2,
{
{1, 0, 0} // x >= 0.
},
{
{1, 1, 0} // x + y = 0.
})}),
makeSetFromFACs(2, {makeFACFromConstraints(2,
{
{0, -1, 1} // y <= 1.
},
{
{1, 1, 0} // x + y = 0.
})}),
{{0, 0},
{1, -1},
{2, -2},
{-1, 1},
{-2, 2},
{1, 1},
{-1, -1},
{-1, 1},
{1, -1}});
// The result should be {0} U {2}.
testSubtractAtPoints(
makeSetFromFACs(1,
{
makeFACFromIneqs(1, {{1, 0}, // x >= 0.
{-1, 2}}), // x <= 2.
}),
makeSetFromFACs(1,
{
makeFACFromConstraints(1, {},
{
{1, -1} // x = 1.
}),
}),
{{-1}, {0}, {1}, {2}, {3}});
// Sets with lots of redundant inequalities to test the redundancy heuristic.
// (the heuristic is for the subtrahend, the second set which is the one being
// subtracted)
// A parallelogram with vertices {(3, 1), (10, -6), (24, 8), (17, 15)} minus
// a triangle with vertices {(2, 2), (10, 2), (10, 10)}.
testSubtractAtPoints(
makeSetFromFACs(2, {makeFACFromIneqs(2,
{
{1, 1, -2}, // x + y >= 4.
{-1, -1, 30}, // x + y <= 32.
{1, -1, 0}, // x - y >= 2.
{-1, 1, 10}, // x - y <= 16.
})}),
makeSetFromFACs(
2, {makeFACFromIneqs(2,
{
{1, 0, -2}, // x >= 2. [redundant]
{0, 1, -2}, // y >= 2.
{-1, 0, 10}, // x <= 10.
{0, -1, 10}, // y <= 10. [redundant]
{1, 1, -2}, // x + y >= 2. [redundant]
{-1, -1, 30}, // x + y <= 30. [redundant]
{1, -1, 0}, // x - y >= 0.
{-1, 1, 10}, // x - y <= 10.
})}),
{{1, 2}, {2, 2}, {3, 2}, {4, 2}, {1, 1}, {2, 1}, {3, 1},
{4, 1}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {10, 2}, {11, 2},
{10, 1}, {10, 10}, {10, 11}, {10, 9}, {11, 10}, {10, -6}, {11, -6},
{24, 8}, {24, 7}, {17, 15}, {16, 15}});
testSubtractAtPoints(
makeSetFromFACs(2, {makeFACFromIneqs(2,
{
{1, 1, -2}, // x + y >= 4.
{-1, -1, 30}, // x + y <= 32.
{1, -1, 0}, // x - y >= 2.
{-1, 1, 10}, // x - y <= 16.
})}),
makeSetFromFACs(
2, {makeFACFromIneqs(2,
{
{1, 0, -2}, // x >= 2. [redundant]
{0, 1, -2}, // y >= 2.
{-1, 0, 10}, // x <= 10.
{0, -1, 10}, // y <= 10. [redundant]
{1, 1, -2}, // x + y >= 2. [redundant]
{-1, -1, 30}, // x + y <= 30. [redundant]
{1, -1, 0}, // x - y >= 0.
{-1, 1, 10}, // x - y <= 10.
})}),
{{1, 2}, {2, 2}, {3, 2}, {4, 2}, {1, 1}, {2, 1}, {3, 1},
{4, 1}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {10, 2}, {11, 2},
{10, 1}, {10, 10}, {10, 11}, {10, 9}, {11, 10}, {10, -6}, {11, -6},
{24, 8}, {24, 7}, {17, 15}, {16, 15}});
// ((-infinity, -5] U [3, 3] U [4, 4] U [5, 5]) - ([-2, -10] U [3, 4] U [6,
// 7])
testSubtractAtPoints(
makeSetFromFACs(1,
{
makeFACFromIneqs(1,
{
{-1, -5}, // x <= -5.
}),
makeFACFromConstraints(1, {},
{
{1, -3} // x = 3.
}),
makeFACFromConstraints(1, {},
{
{1, -4} // x = 4.
}),
makeFACFromConstraints(1, {},
{
{1, -5} // x = 5.
}),
}),
makeSetFromFACs(
1,
{
makeFACFromIneqs(1,
{
{-1, -2}, // x <= -2.
{1, -10}, // x >= -10.
{-1, 0}, // x <= 0. [redundant]
{-1, 10}, // x <= 10. [redundant]
{1, -100}, // x >= -100. [redundant]
{1, -50} // x >= -50. [redundant]
}),
makeFACFromIneqs(1,
{
{1, -3}, // x >= 3.
{-1, 4}, // x <= 4.
{1, 1}, // x >= -1. [redundant]
{1, 7}, // x >= -7. [redundant]
{-1, 10} // x <= 10. [redundant]
}),
makeFACFromIneqs(1,
{
{1, -6}, // x >= 6.
{-1, 7}, // x <= 7.
{1, 1}, // x >= -1. [redundant]
{1, -3}, // x >= -3. [redundant]
{-1, 5} // x <= 5. [redundant]
}),
}),
{{-6},
{-5},
{-4},
{-9},
{-10},
{-11},
{0},
{1},
{2},
{3},
{4},
{5},
{6},
{7},
{8}});
}
TEST(SetTest, Complement) {
// Complement of universe.
testComplementAtPoints(
PresburgerSet::getUniverse(1),
{{-1}, {-2}, {-8}, {1}, {2}, {8}, {9}, {10}, {20}, {21}});
// Complement of empty set.
testComplementAtPoints(
PresburgerSet::getEmptySet(1),
{{-1}, {-2}, {-8}, {1}, {2}, {8}, {9}, {10}, {20}, {21}});
testComplementAtPoints(
makeSetFromFACs(2, {makeFACFromIneqs(2,
{
{1, 0, -2}, // x >= 2.
{0, 1, -2}, // y >= 2.
{-1, 0, 10}, // x <= 10.
{0, -1, 10} // y <= 10.
})}),
{{1, 1},
{2, 1},
{1, 2},
{2, 2},
{2, 3},
{3, 2},
{10, 10},
{10, 11},
{11, 10},
{2, 10},
{2, 11},
{1, 10}});
}
TEST(SetTest, isEqual) {
// set = [2, 8] U [10, 20].
PresburgerSet universe = PresburgerSet::getUniverse(1);
PresburgerSet emptySet = PresburgerSet::getEmptySet(1);
PresburgerSet set =
makeSetFromFACs(1, {
makeFACFromIneqs(1, {{1, -2}, // x >= 2.
{-1, 8}}), // x <= 8.
makeFACFromIneqs(1, {{1, -10}, // x >= 10.
{-1, 20}}), // x <= 20.
});
// universe != emptySet.
EXPECT_FALSE(universe.isEqual(emptySet));
// emptySet != universe.
EXPECT_FALSE(emptySet.isEqual(universe));
// emptySet == emptySet.
EXPECT_TRUE(emptySet.isEqual(emptySet));
// universe == universe.
EXPECT_TRUE(universe.isEqual(universe));
// universe U emptySet == universe.
EXPECT_TRUE(universe.unionSet(emptySet).isEqual(universe));
// universe U universe == universe.
EXPECT_TRUE(universe.unionSet(universe).isEqual(universe));
// emptySet U emptySet == emptySet.
EXPECT_TRUE(emptySet.unionSet(emptySet).isEqual(emptySet));
// universe U emptySet != emptySet.
EXPECT_FALSE(universe.unionSet(emptySet).isEqual(emptySet));
// universe U universe != emptySet.
EXPECT_FALSE(universe.unionSet(universe).isEqual(emptySet));
// emptySet U emptySet != universe.
EXPECT_FALSE(emptySet.unionSet(emptySet).isEqual(universe));
// set \ set == emptySet.
EXPECT_TRUE(set.subtract(set).isEqual(emptySet));
// set == set.
EXPECT_TRUE(set.isEqual(set));
// set U (universe \ set) == universe.
EXPECT_TRUE(set.unionSet(set.complement()).isEqual(universe));
// set U (universe \ set) != set.
EXPECT_FALSE(set.unionSet(set.complement()).isEqual(set));
// set != set U (universe \ set).
EXPECT_FALSE(set.isEqual(set.unionSet(set.complement())));
// square is one unit taller than rect.
PresburgerSet square =
makeSetFromFACs(2, {makeFACFromIneqs(2, {
{1, 0, -2}, // x >= 2.
{0, 1, -2}, // y >= 2.
{-1, 0, 9}, // x <= 9.
{0, -1, 9} // y <= 9.
})});
PresburgerSet rect =
makeSetFromFACs(2, {makeFACFromIneqs(2, {
{1, 0, -2}, // x >= 2.
{0, 1, -2}, // y >= 2.
{-1, 0, 9}, // x <= 9.
{0, -1, 8} // y <= 8.
})});
EXPECT_FALSE(square.isEqual(rect));
PresburgerSet universeRect = square.unionSet(square.complement());
PresburgerSet universeSquare = rect.unionSet(rect.complement());
EXPECT_TRUE(universeRect.isEqual(universeSquare));
EXPECT_FALSE(universeRect.isEqual(rect));
EXPECT_FALSE(universeSquare.isEqual(square));
EXPECT_FALSE(rect.complement().isEqual(square.complement()));
}
} // namespace mlir