CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
313 lines
12 KiB
C++
313 lines
12 KiB
C++
#include "mlir/Analysis/Presburger/Barvinok.h"
|
|
#include "./Utils.h"
|
|
#include "Parser.h"
|
|
#include <gmock/gmock.h>
|
|
#include <gtest/gtest.h>
|
|
|
|
using namespace mlir;
|
|
using namespace presburger;
|
|
using namespace mlir::presburger::detail;
|
|
|
|
/// The following are 3 randomly generated vectors with 4
|
|
/// entries each and define a cone's H-representation
|
|
/// using these numbers. We check that the dual contains
|
|
/// the same numbers.
|
|
/// We do the same in the reverse case.
|
|
TEST(BarvinokTest, getDual) {
|
|
ConeH cone1 = defineHRep(4);
|
|
cone1.addInequality({1, 2, 3, 4, 0});
|
|
cone1.addInequality({3, 4, 2, 5, 0});
|
|
cone1.addInequality({6, 2, 6, 1, 0});
|
|
|
|
ConeV dual1 = getDual(cone1);
|
|
|
|
EXPECT_EQ_INT_MATRIX(
|
|
dual1, makeIntMatrix(3, 4, {{1, 2, 3, 4}, {3, 4, 2, 5}, {6, 2, 6, 1}}));
|
|
|
|
ConeV cone2 = makeIntMatrix(3, 4, {{3, 6, 1, 5}, {3, 1, 7, 2}, {9, 3, 2, 7}});
|
|
|
|
ConeH dual2 = getDual(cone2);
|
|
|
|
ConeH expected = defineHRep(4);
|
|
expected.addInequality({3, 6, 1, 5, 0});
|
|
expected.addInequality({3, 1, 7, 2, 0});
|
|
expected.addInequality({9, 3, 2, 7, 0});
|
|
|
|
EXPECT_TRUE(dual2.isEqual(expected));
|
|
}
|
|
|
|
/// We randomly generate a nxn matrix to use as a cone
|
|
/// with n inequalities in n variables and check for
|
|
/// the determinant being equal to the index.
|
|
TEST(BarvinokTest, getIndex) {
|
|
ConeV cone = makeIntMatrix(3, 3, {{4, 2, 1}, {5, 2, 7}, {4, 1, 6}});
|
|
EXPECT_EQ(getIndex(cone), cone.determinant());
|
|
|
|
cone = makeIntMatrix(
|
|
4, 4, {{4, 2, 5, 1}, {4, 1, 3, 6}, {8, 2, 5, 6}, {5, 2, 5, 7}});
|
|
EXPECT_EQ(getIndex(cone), cone.determinant());
|
|
}
|
|
|
|
// The following cones and vertices are randomly generated
|
|
// (s.t. the cones are unimodular) and the generating functions
|
|
// are computed. We check that the results contain the correct
|
|
// matrices.
|
|
TEST(BarvinokTest, unimodularConeGeneratingFunction) {
|
|
ConeH cone = defineHRep(2);
|
|
cone.addInequality({0, -1, 0});
|
|
cone.addInequality({-1, -2, 0});
|
|
|
|
ParamPoint vertex =
|
|
makeFracMatrix(2, 3, {{2, 2, 0}, {-1, -Fraction(1, 2), 1}});
|
|
|
|
GeneratingFunction gf =
|
|
computeUnimodularConeGeneratingFunction(vertex, 1, cone);
|
|
|
|
EXPECT_EQ_REPR_GENERATINGFUNCTION(
|
|
gf, GeneratingFunction(
|
|
2, {1},
|
|
{makeFracMatrix(3, 2, {{-1, 0}, {-Fraction(1, 2), 1}, {1, 2}})},
|
|
{{{2, -1}, {-1, 0}}}));
|
|
|
|
cone = defineHRep(3);
|
|
cone.addInequality({7, 1, 6, 0});
|
|
cone.addInequality({9, 1, 7, 0});
|
|
cone.addInequality({8, -1, 1, 0});
|
|
|
|
vertex = makeFracMatrix(3, 2, {{5, 2}, {6, 2}, {7, 1}});
|
|
|
|
gf = computeUnimodularConeGeneratingFunction(vertex, 1, cone);
|
|
|
|
EXPECT_EQ_REPR_GENERATINGFUNCTION(
|
|
gf,
|
|
GeneratingFunction(
|
|
1, {1}, {makeFracMatrix(2, 3, {{-83, -100, -41}, {-22, -27, -15}})},
|
|
{{{8, 47, -17}, {-7, -41, 15}, {1, 5, -2}}}));
|
|
}
|
|
|
|
// The following vectors are randomly generated.
|
|
// We then check that the output of the function has non-zero
|
|
// dot product with all non-null vectors.
|
|
TEST(BarvinokTest, getNonOrthogonalVector) {
|
|
std::vector<Point> vectors = {Point({1, 2, 3, 4}), Point({-1, 0, 1, 1}),
|
|
Point({2, 7, 0, 0}), Point({0, 0, 0, 0})};
|
|
Point nonOrth = getNonOrthogonalVector(vectors);
|
|
|
|
for (unsigned i = 0; i < 3; i++)
|
|
EXPECT_NE(dotProduct(nonOrth, vectors[i]), 0);
|
|
|
|
vectors = {Point({0, 1, 3}), Point({-2, -1, 1}), Point({6, 3, 0}),
|
|
Point({0, 0, -3}), Point({5, 0, -1})};
|
|
nonOrth = getNonOrthogonalVector(vectors);
|
|
|
|
for (const Point &vector : vectors)
|
|
EXPECT_NE(dotProduct(nonOrth, vector), 0);
|
|
}
|
|
|
|
// The following polynomials are randomly generated and the
|
|
// coefficients are computed by hand.
|
|
// Although the function allows the coefficients of the numerator
|
|
// to be arbitrary quasipolynomials, we stick to constants for simplicity,
|
|
// as the relevant arithmetic operations on quasipolynomials
|
|
// are tested separately.
|
|
TEST(BarvinokTest, getCoefficientInRationalFunction) {
|
|
std::vector<QuasiPolynomial> numerator = {
|
|
QuasiPolynomial(0, 2), QuasiPolynomial(0, 3), QuasiPolynomial(0, 5)};
|
|
std::vector<Fraction> denominator = {Fraction(1), Fraction(0), Fraction(4),
|
|
Fraction(3)};
|
|
QuasiPolynomial coeff =
|
|
getCoefficientInRationalFunction(1, numerator, denominator);
|
|
EXPECT_EQ(coeff.getConstantTerm(), 3);
|
|
|
|
numerator = {QuasiPolynomial(0, -1), QuasiPolynomial(0, 4),
|
|
QuasiPolynomial(0, -2), QuasiPolynomial(0, 5),
|
|
QuasiPolynomial(0, 6)};
|
|
denominator = {Fraction(8), Fraction(4), Fraction(0), Fraction(-2)};
|
|
coeff = getCoefficientInRationalFunction(3, numerator, denominator);
|
|
EXPECT_EQ(coeff.getConstantTerm(), Fraction(55, 64));
|
|
}
|
|
|
|
TEST(BarvinokTest, computeNumTermsCone) {
|
|
// The following test is taken from
|
|
// Verdoolaege, Sven, et al. "Counting integer points in parametric
|
|
// polytopes using Barvinok's rational functions." Algorithmica 48 (2007):
|
|
// 37-66.
|
|
// It represents a right-angled triangle with right angle at the origin,
|
|
// with height and base lengths (p/2).
|
|
GeneratingFunction gf(
|
|
1, {1, 1, 1},
|
|
{makeFracMatrix(2, 2, {{0, Fraction(1, 2)}, {0, 0}}),
|
|
makeFracMatrix(2, 2, {{0, Fraction(1, 2)}, {0, 0}}),
|
|
makeFracMatrix(2, 2, {{0, 0}, {0, 0}})},
|
|
{{{-1, 1}, {-1, 0}}, {{1, -1}, {0, -1}}, {{1, 0}, {0, 1}}});
|
|
|
|
QuasiPolynomial numPoints = computeNumTerms(gf).collectTerms();
|
|
|
|
// First, we make sure that all the affine functions are of the form ⌊p/2⌋.
|
|
for (const std::vector<SmallVector<Fraction>> &term : numPoints.getAffine()) {
|
|
for (const SmallVector<Fraction> &aff : term) {
|
|
EXPECT_EQ(aff.size(), 2u);
|
|
EXPECT_EQ(aff[0], Fraction(1, 2));
|
|
EXPECT_EQ(aff[1], Fraction(0, 1));
|
|
}
|
|
}
|
|
|
|
// Now, we can gather the like terms because we know there's only
|
|
// either ⌊p/2⌋^2, ⌊p/2⌋, or constants.
|
|
// The total coefficient of ⌊p/2⌋^2 is the sum of coefficients of all
|
|
// terms with 2 affine functions, and
|
|
// the coefficient of total ⌊p/2⌋ is the sum of coefficients of all
|
|
// terms with 1 affine function,
|
|
Fraction pSquaredCoeff = 0, pCoeff = 0, constantTerm = 0;
|
|
SmallVector<Fraction> coefficients = numPoints.getCoefficients();
|
|
for (unsigned i = 0; i < numPoints.getCoefficients().size(); i++)
|
|
if (numPoints.getAffine()[i].size() == 2)
|
|
pSquaredCoeff = pSquaredCoeff + coefficients[i];
|
|
else if (numPoints.getAffine()[i].size() == 1)
|
|
pCoeff = pCoeff + coefficients[i];
|
|
|
|
// We expect the answer to be (1/2)⌊p/2⌋^2 + (3/2)⌊p/2⌋ + 1.
|
|
EXPECT_EQ(pSquaredCoeff, Fraction(1, 2));
|
|
EXPECT_EQ(pCoeff, Fraction(3, 2));
|
|
EXPECT_EQ(numPoints.getConstantTerm(), Fraction(1, 1));
|
|
|
|
// The following generating function corresponds to a cuboid
|
|
// with length M (x-axis), width N (y-axis), and height P (z-axis).
|
|
// There are eight terms.
|
|
gf = GeneratingFunction(
|
|
3, {1, 1, 1, 1, 1, 1, 1, 1},
|
|
{makeFracMatrix(4, 3, {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}),
|
|
makeFracMatrix(4, 3, {{1, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}),
|
|
makeFracMatrix(4, 3, {{0, 0, 0}, {0, 1, 0}, {0, 0, 0}, {0, 0, 0}}),
|
|
makeFracMatrix(4, 3, {{0, 0, 0}, {0, 0, 0}, {0, 0, 1}, {0, 0, 0}}),
|
|
makeFracMatrix(4, 3, {{1, 0, 0}, {0, 1, 0}, {0, 0, 0}, {0, 0, 0}}),
|
|
makeFracMatrix(4, 3, {{1, 0, 0}, {0, 0, 0}, {0, 0, 1}, {0, 0, 0}}),
|
|
makeFracMatrix(4, 3, {{0, 0, 0}, {0, 1, 0}, {0, 0, 1}, {0, 0, 0}}),
|
|
makeFracMatrix(4, 3, {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}, {0, 0, 0}})},
|
|
{{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}},
|
|
{{-1, 0, 0}, {0, 1, 0}, {0, 0, 1}},
|
|
{{1, 0, 0}, {0, -1, 0}, {0, 0, 1}},
|
|
{{1, 0, 0}, {0, 1, 0}, {0, 0, -1}},
|
|
{{-1, 0, 0}, {0, -1, 0}, {0, 0, 1}},
|
|
{{-1, 0, 0}, {0, 1, 0}, {0, 0, -1}},
|
|
{{1, 0, 0}, {0, -1, 0}, {0, 0, -1}},
|
|
{{-1, 0, 0}, {0, -1, 0}, {0, 0, -1}}});
|
|
|
|
numPoints = computeNumTerms(gf);
|
|
numPoints = numPoints.collectTerms().simplify();
|
|
|
|
// First, we make sure all the affine functions are either
|
|
// M, N, P, or 1.
|
|
for (const std::vector<SmallVector<Fraction>> &term : numPoints.getAffine()) {
|
|
for (const SmallVector<Fraction> &aff : term) {
|
|
// First, ensure that the coefficients are all nonnegative integers.
|
|
for (const Fraction &c : aff) {
|
|
EXPECT_TRUE(c >= 0);
|
|
EXPECT_EQ(c, c.getAsInteger());
|
|
}
|
|
// Now, if the coefficients add up to 1, we can be sure the term is
|
|
// either M, N, P, or 1.
|
|
EXPECT_EQ(aff[0] + aff[1] + aff[2] + aff[3], 1);
|
|
}
|
|
}
|
|
|
|
// We store the coefficients of M, N and P in this array.
|
|
Fraction count[2][2][2];
|
|
coefficients = numPoints.getCoefficients();
|
|
for (unsigned i = 0, e = coefficients.size(); i < e; i++) {
|
|
unsigned mIndex = 0, nIndex = 0, pIndex = 0;
|
|
for (const SmallVector<Fraction> &aff : numPoints.getAffine()[i]) {
|
|
if (aff[0] == 1)
|
|
mIndex = 1;
|
|
if (aff[1] == 1)
|
|
nIndex = 1;
|
|
if (aff[2] == 1)
|
|
pIndex = 1;
|
|
EXPECT_EQ(aff[3], 0);
|
|
}
|
|
count[mIndex][nIndex][pIndex] += coefficients[i];
|
|
}
|
|
|
|
// We expect the answer to be
|
|
// (⌊M⌋ + 1)(⌊N⌋ + 1)(⌊P⌋ + 1) =
|
|
// ⌊M⌋⌊N⌋⌊P⌋ + ⌊M⌋⌊N⌋ + ⌊N⌋⌊P⌋ + ⌊M⌋⌊P⌋ + ⌊M⌋ + ⌊N⌋ + ⌊P⌋ + 1.
|
|
for (auto &i : count)
|
|
for (unsigned j = 0; j < 2; j++)
|
|
for (unsigned k = 0; k < 2; k++)
|
|
EXPECT_EQ(i[j][k], 1);
|
|
}
|
|
|
|
/// We define some simple polyhedra with unimodular tangent cones and verify
|
|
/// that the returned generating functions correspond to those calculated by
|
|
/// hand.
|
|
TEST(BarvinokTest, computeNumTermsPolytope) {
|
|
// A cube of side 1.
|
|
PolyhedronH poly =
|
|
parseRelationFromSet("(x, y, z) : (x >= 0, y >= 0, z >= 0, -x + 1 >= 0, "
|
|
"-y + 1 >= 0, -z + 1 >= 0)",
|
|
0);
|
|
|
|
std::vector<std::pair<PresburgerSet, GeneratingFunction>> count =
|
|
computePolytopeGeneratingFunction(poly);
|
|
// There is only one chamber, as it is non-parametric.
|
|
EXPECT_EQ(count.size(), 9u);
|
|
|
|
GeneratingFunction gf = count[0].second;
|
|
EXPECT_EQ_REPR_GENERATINGFUNCTION(
|
|
gf,
|
|
GeneratingFunction(
|
|
0, {1, 1, 1, 1, 1, 1, 1, 1},
|
|
{makeFracMatrix(1, 3, {{1, 1, 1}}), makeFracMatrix(1, 3, {{0, 1, 1}}),
|
|
makeFracMatrix(1, 3, {{0, 1, 1}}), makeFracMatrix(1, 3, {{0, 0, 1}}),
|
|
makeFracMatrix(1, 3, {{0, 1, 1}}), makeFracMatrix(1, 3, {{0, 0, 1}}),
|
|
makeFracMatrix(1, 3, {{0, 0, 1}}),
|
|
makeFracMatrix(1, 3, {{0, 0, 0}})},
|
|
{{{-1, 0, 0}, {0, -1, 0}, {0, 0, -1}},
|
|
{{0, 0, 1}, {-1, 0, 0}, {0, -1, 0}},
|
|
{{0, 1, 0}, {-1, 0, 0}, {0, 0, -1}},
|
|
{{0, 1, 0}, {0, 0, 1}, {-1, 0, 0}},
|
|
{{1, 0, 0}, {0, -1, 0}, {0, 0, -1}},
|
|
{{1, 0, 0}, {0, 0, 1}, {0, -1, 0}},
|
|
{{1, 0, 0}, {0, 1, 0}, {0, 0, -1}},
|
|
{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}}));
|
|
|
|
// A right-angled triangle with side p.
|
|
poly =
|
|
parseRelationFromSet("(x, y)[N] : (x >= 0, y >= 0, -x - y + N >= 0)", 0);
|
|
|
|
count = computePolytopeGeneratingFunction(poly);
|
|
// There is only one chamber: p ≥ 0
|
|
EXPECT_EQ(count.size(), 4u);
|
|
|
|
gf = count[0].second;
|
|
EXPECT_EQ_REPR_GENERATINGFUNCTION(
|
|
gf, GeneratingFunction(
|
|
1, {1, 1, 1},
|
|
{makeFracMatrix(2, 2, {{0, 1}, {0, 0}}),
|
|
makeFracMatrix(2, 2, {{0, 1}, {0, 0}}),
|
|
makeFracMatrix(2, 2, {{0, 0}, {0, 0}})},
|
|
{{{-1, 1}, {-1, 0}}, {{1, -1}, {0, -1}}, {{1, 0}, {0, 1}}}));
|
|
|
|
// Cartesian product of a cube with side M and a right triangle with side N.
|
|
poly = parseRelationFromSet(
|
|
"(x, y, z, w, a)[M, N] : (x >= 0, y >= 0, z >= 0, -x + M >= 0, -y + M >= "
|
|
"0, -z + M >= 0, w >= 0, a >= 0, -w - a + N >= 0)",
|
|
0);
|
|
|
|
count = computePolytopeGeneratingFunction(poly);
|
|
|
|
EXPECT_EQ(count.size(), 25u);
|
|
|
|
gf = count[0].second;
|
|
EXPECT_EQ(gf.getNumerators().size(), 24u);
|
|
}
|
|
|
|
TEST(BarvinokTest, solveParametricEquations) {
|
|
FracMatrix equations = makeFracMatrix(2, 3, {{2, 3, -4}, {2, 6, -7}});
|
|
auto maybeSolution = solveParametricEquations(equations);
|
|
ASSERT_TRUE(maybeSolution.has_value());
|
|
FracMatrix solution = *maybeSolution;
|
|
EXPECT_EQ(solution.at(0, 0), Fraction(1, 2));
|
|
EXPECT_EQ(solution.at(1, 0), 1);
|
|
}
|