1 //===- SimplexTest.cpp - Tests for Simplex --------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "mlir/Analysis/Presburger/Simplex.h"
10 
11 #include <gmock/gmock.h>
12 #include <gtest/gtest.h>
13 
14 namespace mlir {
15 
16 /// Take a snapshot, add constraints making the set empty, and rollback.
17 /// The set should not be empty after rolling back.
18 TEST(SimplexTest, emptyRollback) {
19   Simplex simplex(2);
20   // (u - v) >= 0
21   simplex.addInequality({1, -1, 0});
22   EXPECT_FALSE(simplex.isEmpty());
23 
24   unsigned snapshot = simplex.getSnapshot();
25   // (u - v) <= -1
26   simplex.addInequality({-1, 1, -1});
27   EXPECT_TRUE(simplex.isEmpty());
28   simplex.rollback(snapshot);
29   EXPECT_FALSE(simplex.isEmpty());
30 }
31 
32 /// Check that the set gets marked as empty when we add contradictory
33 /// constraints.
34 TEST(SimplexTest, addEquality_separate) {
35   Simplex simplex(1);
36   simplex.addInequality({1, -1}); // x >= 1.
37   ASSERT_FALSE(simplex.isEmpty());
38   simplex.addEquality({1, 0}); // x == 0.
39   EXPECT_TRUE(simplex.isEmpty());
40 }
41 
42 void expectInequalityMakesSetEmpty(Simplex &simplex, ArrayRef<int64_t> coeffs,
43                                    bool expect) {
44   ASSERT_FALSE(simplex.isEmpty());
45   unsigned snapshot = simplex.getSnapshot();
46   simplex.addInequality(coeffs);
47   EXPECT_EQ(simplex.isEmpty(), expect);
48   simplex.rollback(snapshot);
49 }
50 
51 TEST(SimplexTest, addInequality_rollback) {
52   Simplex simplex(3);
53   SmallVector<int64_t, 4> coeffs[]{{1, 0, 0, 0},   // u >= 0.
54                                    {-1, 0, 0, 0},  // u <= 0.
55                                    {1, -1, 1, 0},  // u - v + w >= 0.
56                                    {1, 1, -1, 0}}; // u + v - w >= 0.
57   // The above constraints force u = 0 and v = w.
58   // The constraints below violate v = w.
59   SmallVector<int64_t, 4> checkCoeffs[]{{0, 1, -1, -1},  // v - w >= 1.
60                                         {0, -1, 1, -1}}; // v - w <= -1.
61 
62   for (int run = 0; run < 4; run++) {
63     unsigned snapshot = simplex.getSnapshot();
64 
65     expectInequalityMakesSetEmpty(simplex, checkCoeffs[0], false);
66     expectInequalityMakesSetEmpty(simplex, checkCoeffs[1], false);
67 
68     for (int i = 0; i < 4; i++)
69       simplex.addInequality(coeffs[(run + i) % 4]);
70 
71     expectInequalityMakesSetEmpty(simplex, checkCoeffs[0], true);
72     expectInequalityMakesSetEmpty(simplex, checkCoeffs[1], true);
73 
74     simplex.rollback(snapshot);
75     EXPECT_EQ(simplex.numConstraints(), 0u);
76 
77     expectInequalityMakesSetEmpty(simplex, checkCoeffs[0], false);
78     expectInequalityMakesSetEmpty(simplex, checkCoeffs[1], false);
79   }
80 }
81 
82 Simplex simplexFromConstraints(unsigned nDim,
83                                SmallVector<SmallVector<int64_t, 8>, 8> ineqs,
84                                SmallVector<SmallVector<int64_t, 8>, 8> eqs) {
85   Simplex simplex(nDim);
86   for (const auto &ineq : ineqs)
87     simplex.addInequality(ineq);
88   for (const auto &eq : eqs)
89     simplex.addEquality(eq);
90   return simplex;
91 }
92 
93 TEST(SimplexTest, isUnbounded) {
94   EXPECT_FALSE(simplexFromConstraints(
95                    2, {{1, 1, 0}, {-1, -1, 0}, {1, -1, 5}, {-1, 1, -5}}, {})
96                    .isUnbounded());
97 
98   EXPECT_TRUE(
99       simplexFromConstraints(2, {{1, 1, 0}, {1, -1, 5}, {-1, 1, -5}}, {})
100           .isUnbounded());
101 
102   EXPECT_TRUE(
103       simplexFromConstraints(2, {{-1, -1, 0}, {1, -1, 5}, {-1, 1, -5}}, {})
104           .isUnbounded());
105 
106   EXPECT_TRUE(simplexFromConstraints(2, {}, {}).isUnbounded());
107 
108   EXPECT_FALSE(simplexFromConstraints(3,
109                                       {
110                                           {2, 0, 0, -1},
111                                           {-2, 0, 0, 1},
112                                           {0, 2, 0, -1},
113                                           {0, -2, 0, 1},
114                                           {0, 0, 2, -1},
115                                           {0, 0, -2, 1},
116                                       },
117                                       {})
118                    .isUnbounded());
119 
120   EXPECT_TRUE(simplexFromConstraints(3,
121                                      {
122                                          {2, 0, 0, -1},
123                                          {-2, 0, 0, 1},
124                                          {0, 2, 0, -1},
125                                          {0, -2, 0, 1},
126                                          {0, 0, -2, 1},
127                                      },
128                                      {})
129                   .isUnbounded());
130 
131   EXPECT_TRUE(simplexFromConstraints(3,
132                                      {
133                                          {2, 0, 0, -1},
134                                          {-2, 0, 0, 1},
135                                          {0, 2, 0, -1},
136                                          {0, -2, 0, 1},
137                                          {0, 0, 2, -1},
138                                      },
139                                      {})
140                   .isUnbounded());
141 
142   // Bounded set with equalities.
143   EXPECT_FALSE(simplexFromConstraints(2,
144                                       {{1, 1, 1},    // x + y >= -1.
145                                        {-1, -1, 1}}, // x + y <=  1.
146                                       {{1, -1, 0}}   // x = y.
147                                       )
148                    .isUnbounded());
149 
150   // Unbounded set with equalities.
151   EXPECT_TRUE(simplexFromConstraints(3,
152                                      {{1, 1, 1, 1},     // x + y + z >= -1.
153                                       {-1, -1, -1, 1}}, // x + y + z <=  1.
154                                      {{1, -1, -1, 0}}   // x = y + z.
155                                      )
156                   .isUnbounded());
157 
158   // Rational empty set.
159   EXPECT_FALSE(simplexFromConstraints(3,
160                                       {
161                                           {2, 0, 0, -1},
162                                           {-2, 0, 0, 1},
163                                           {0, 2, 2, -1},
164                                           {0, -2, -2, 1},
165                                           {3, 3, 3, -4},
166                                       },
167                                       {})
168                    .isUnbounded());
169 }
170 
171 TEST(SimplexTest, getSamplePointIfIntegral) {
172   // Empty set.
173   EXPECT_FALSE(simplexFromConstraints(3,
174                                       {
175                                           {2, 0, 0, -1},
176                                           {-2, 0, 0, 1},
177                                           {0, 2, 2, -1},
178                                           {0, -2, -2, 1},
179                                           {3, 3, 3, -4},
180                                       },
181                                       {})
182                    .getSamplePointIfIntegral()
183                    .hasValue());
184 
185   auto maybeSample = simplexFromConstraints(2,
186                                             {// x = y - 2.
187                                              {1, -1, 2},
188                                              {-1, 1, -2},
189                                              // x + y = 2.
190                                              {1, 1, -2},
191                                              {-1, -1, 2}},
192                                             {})
193                          .getSamplePointIfIntegral();
194 
195   EXPECT_TRUE(maybeSample.hasValue());
196   EXPECT_THAT(*maybeSample, testing::ElementsAre(0, 2));
197 
198   auto maybeSample2 = simplexFromConstraints(2,
199                                              {
200                                                  {1, 0, 0},  // x >= 0.
201                                                  {-1, 0, 0}, // x <= 0.
202                                              },
203                                              {
204                                                  {0, 1, -2} // y = 2.
205                                              })
206                           .getSamplePointIfIntegral();
207   EXPECT_TRUE(maybeSample2.hasValue());
208   EXPECT_THAT(*maybeSample2, testing::ElementsAre(0, 2));
209 
210   EXPECT_FALSE(simplexFromConstraints(1,
211                                       {// 2x = 1. (no integer solutions)
212                                        {2, -1},
213                                        {-2, +1}},
214                                       {})
215                    .getSamplePointIfIntegral()
216                    .hasValue());
217 }
218 
219 } // namespace mlir
220