1*d5a29442SArjun P //===- PWMAFunction.cpp - MLIR PWMAFunction Class -------------------------===//
2*d5a29442SArjun P //
3*d5a29442SArjun P // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*d5a29442SArjun P // See https://llvm.org/LICENSE.txt for license information.
5*d5a29442SArjun P // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*d5a29442SArjun P //
7*d5a29442SArjun P //===----------------------------------------------------------------------===//
8*d5a29442SArjun P 
9*d5a29442SArjun P #include "mlir/Analysis/Presburger/PWMAFunction.h"
10*d5a29442SArjun P #include "mlir/Analysis/Presburger/Simplex.h"
11*d5a29442SArjun P 
12*d5a29442SArjun P using namespace mlir;
13*d5a29442SArjun P 
14*d5a29442SArjun P // Return the result of subtracting the two given vectors pointwise.
15*d5a29442SArjun P // The vectors must be of the same size.
16*d5a29442SArjun P // e.g., [3, 4, 6] - [2, 5, 1] = [1, -1, 5].
17*d5a29442SArjun P static SmallVector<int64_t, 8> subtract(ArrayRef<int64_t> vecA,
18*d5a29442SArjun P                                         ArrayRef<int64_t> vecB) {
19*d5a29442SArjun P   assert(vecA.size() == vecB.size() &&
20*d5a29442SArjun P          "Cannot subtract vectors of differing lengths!");
21*d5a29442SArjun P   SmallVector<int64_t, 8> result;
22*d5a29442SArjun P   result.reserve(vecA.size());
23*d5a29442SArjun P   for (unsigned i = 0, e = vecA.size(); i < e; ++i)
24*d5a29442SArjun P     result.push_back(vecA[i] - vecB[i]);
25*d5a29442SArjun P   return result;
26*d5a29442SArjun P }
27*d5a29442SArjun P 
28*d5a29442SArjun P PresburgerSet PWMAFunction::getDomain() const {
29*d5a29442SArjun P   PresburgerSet domain =
30*d5a29442SArjun P       PresburgerSet::getEmptySet(getNumDimIds(), getNumSymbolIds());
31*d5a29442SArjun P   for (const MultiAffineFunction &piece : pieces)
32*d5a29442SArjun P     domain.unionPolyInPlace(piece.getDomain());
33*d5a29442SArjun P   return domain;
34*d5a29442SArjun P }
35*d5a29442SArjun P 
36*d5a29442SArjun P Optional<SmallVector<int64_t, 8>>
37*d5a29442SArjun P MultiAffineFunction::valueAt(ArrayRef<int64_t> point) const {
38*d5a29442SArjun P   assert(getNumLocalIds() == 0 && "Local ids are not yet supported!");
39*d5a29442SArjun P   assert(point.size() == getNumIds() && "Point has incorrect dimensionality!");
40*d5a29442SArjun P 
41*d5a29442SArjun P   if (!getDomain().containsPoint(point))
42*d5a29442SArjun P     return {};
43*d5a29442SArjun P 
44*d5a29442SArjun P   // The point lies in the domain, so we need to compute the output value.
45*d5a29442SArjun P   // The matrix `output` has an affine expression in the ith row, corresponding
46*d5a29442SArjun P   // to the expression for the ith value in the output vector. The last column
47*d5a29442SArjun P   // of the matrix contains the constant term. Let v be the input point with
48*d5a29442SArjun P   // a 1 appended at the end. We can see that output * v gives the desired
49*d5a29442SArjun P   // output vector.
50*d5a29442SArjun P   SmallVector<int64_t, 8> pointHomogenous{llvm::to_vector(point)};
51*d5a29442SArjun P   pointHomogenous.push_back(1);
52*d5a29442SArjun P   SmallVector<int64_t, 8> result =
53*d5a29442SArjun P       output.postMultiplyWithColumn(pointHomogenous);
54*d5a29442SArjun P   assert(result.size() == getNumOutputs());
55*d5a29442SArjun P   return result;
56*d5a29442SArjun P }
57*d5a29442SArjun P 
58*d5a29442SArjun P Optional<SmallVector<int64_t, 8>>
59*d5a29442SArjun P PWMAFunction::valueAt(ArrayRef<int64_t> point) const {
60*d5a29442SArjun P   assert(point.size() == getNumInputs() &&
61*d5a29442SArjun P          "Point has incorrect dimensionality!");
62*d5a29442SArjun P   for (const MultiAffineFunction &piece : pieces)
63*d5a29442SArjun P     if (Optional<SmallVector<int64_t, 8>> output = piece.valueAt(point))
64*d5a29442SArjun P       return output;
65*d5a29442SArjun P   return {};
66*d5a29442SArjun P }
67*d5a29442SArjun P 
68*d5a29442SArjun P void MultiAffineFunction::print(raw_ostream &os) const {
69*d5a29442SArjun P   os << "Domain:";
70*d5a29442SArjun P   IntegerPolyhedron::print(os);
71*d5a29442SArjun P   os << "Output:\n";
72*d5a29442SArjun P   output.print(os);
73*d5a29442SArjun P   os << "\n";
74*d5a29442SArjun P }
75*d5a29442SArjun P 
76*d5a29442SArjun P void MultiAffineFunction::dump() const { print(llvm::errs()); }
77*d5a29442SArjun P 
78*d5a29442SArjun P bool MultiAffineFunction::isEqual(const MultiAffineFunction &other) const {
79*d5a29442SArjun P   return hasCompatibleDimensions(other) &&
80*d5a29442SArjun P          getDomain().isEqual(other.getDomain()) &&
81*d5a29442SArjun P          isEqualWhereDomainsOverlap(other);
82*d5a29442SArjun P }
83*d5a29442SArjun P 
84*d5a29442SArjun P unsigned MultiAffineFunction::insertId(IdKind kind, unsigned pos,
85*d5a29442SArjun P                                        unsigned num) {
86*d5a29442SArjun P   unsigned absolutePos = getIdKindOffset(kind) + pos;
87*d5a29442SArjun P   output.insertColumns(absolutePos, num);
88*d5a29442SArjun P   return IntegerPolyhedron::insertId(kind, pos, num);
89*d5a29442SArjun P }
90*d5a29442SArjun P 
91*d5a29442SArjun P void MultiAffineFunction::swapId(unsigned posA, unsigned posB) {
92*d5a29442SArjun P   output.swapColumns(posA, posB);
93*d5a29442SArjun P   IntegerPolyhedron::swapId(posA, posB);
94*d5a29442SArjun P }
95*d5a29442SArjun P 
96*d5a29442SArjun P void MultiAffineFunction::removeIdRange(unsigned idStart, unsigned idLimit) {
97*d5a29442SArjun P   output.removeColumns(idStart, idLimit - idStart);
98*d5a29442SArjun P   IntegerPolyhedron::removeIdRange(idStart, idLimit);
99*d5a29442SArjun P }
100*d5a29442SArjun P 
101*d5a29442SArjun P void MultiAffineFunction::eliminateRedundantLocalId(unsigned posA,
102*d5a29442SArjun P                                                     unsigned posB) {
103*d5a29442SArjun P   output.addToColumn(posB, posA, /*scale=*/1);
104*d5a29442SArjun P   IntegerPolyhedron::eliminateRedundantLocalId(posA, posB);
105*d5a29442SArjun P }
106*d5a29442SArjun P 
107*d5a29442SArjun P bool MultiAffineFunction::isEqualWhereDomainsOverlap(
108*d5a29442SArjun P     MultiAffineFunction other) const {
109*d5a29442SArjun P   if (!hasCompatibleDimensions(other))
110*d5a29442SArjun P     return false;
111*d5a29442SArjun P 
112*d5a29442SArjun P   // `commonFunc` has the same output as `this`.
113*d5a29442SArjun P   MultiAffineFunction commonFunc = *this;
114*d5a29442SArjun P   // After this merge, `commonFunc` and `other` have the same local ids; they
115*d5a29442SArjun P   // are merged.
116*d5a29442SArjun P   commonFunc.mergeLocalIds(other);
117*d5a29442SArjun P   // After this, the domain of `commonFunc` will be the intersection of the
118*d5a29442SArjun P   // domains of `this` and `other`.
119*d5a29442SArjun P   commonFunc.IntegerPolyhedron::append(other);
120*d5a29442SArjun P 
121*d5a29442SArjun P   // `commonDomainMatching` contains the subset of the common domain
122*d5a29442SArjun P   // where the outputs of `this` and `other` match.
123*d5a29442SArjun P   //
124*d5a29442SArjun P   // We want to add constraints equating the outputs of `this` and `other`.
125*d5a29442SArjun P   // However, `this` may have difference local ids from `other`, whereas we
126*d5a29442SArjun P   // need both to have the same locals. Accordingly, we use `commonFunc.output`
127*d5a29442SArjun P   // in place of `this->output`, since `commonFunc` has the same output but also
128*d5a29442SArjun P   // has its locals merged.
129*d5a29442SArjun P   IntegerPolyhedron commonDomainMatching = commonFunc.getDomain();
130*d5a29442SArjun P   for (unsigned row = 0, e = getNumOutputs(); row < e; ++row)
131*d5a29442SArjun P     commonDomainMatching.addEquality(
132*d5a29442SArjun P         subtract(commonFunc.output.getRow(row), other.output.getRow(row)));
133*d5a29442SArjun P 
134*d5a29442SArjun P   // If the whole common domain is a subset of commonDomainMatching, then they
135*d5a29442SArjun P   // are equal and the two functions match on the whole common domain.
136*d5a29442SArjun P   return commonFunc.getDomain().isSubsetOf(commonDomainMatching);
137*d5a29442SArjun P }
138*d5a29442SArjun P 
139*d5a29442SArjun P /// Two PWMAFunctions are equal if they have the same dimensionalities,
140*d5a29442SArjun P /// the same domain, and take the same value at every point in the domain.
141*d5a29442SArjun P bool PWMAFunction::isEqual(const PWMAFunction &other) const {
142*d5a29442SArjun P   if (!hasCompatibleDimensions(other))
143*d5a29442SArjun P     return false;
144*d5a29442SArjun P 
145*d5a29442SArjun P   if (!this->getDomain().isEqual(other.getDomain()))
146*d5a29442SArjun P     return false;
147*d5a29442SArjun P 
148*d5a29442SArjun P   // Check if, whenever the domains of a piece of `this` and a piece of `other`
149*d5a29442SArjun P   // overlap, they take the same output value. If `this` and `other` have the
150*d5a29442SArjun P   // same domain (checked above), then this check passes iff the two functions
151*d5a29442SArjun P   // have the same output at every point in the domain.
152*d5a29442SArjun P   for (const MultiAffineFunction &aPiece : this->pieces)
153*d5a29442SArjun P     for (const MultiAffineFunction &bPiece : other.pieces)
154*d5a29442SArjun P       if (!aPiece.isEqualWhereDomainsOverlap(bPiece))
155*d5a29442SArjun P         return false;
156*d5a29442SArjun P   return true;
157*d5a29442SArjun P }
158*d5a29442SArjun P 
159*d5a29442SArjun P void PWMAFunction::addPiece(const MultiAffineFunction &piece) {
160*d5a29442SArjun P   assert(hasCompatibleDimensions(piece) &&
161*d5a29442SArjun P          "Piece to be added is not compatible with this PWMAFunction!");
162*d5a29442SArjun P   assert(piece.isConsistent() && "Piece is internally inconsistent!");
163*d5a29442SArjun P   assert(this->getDomain()
164*d5a29442SArjun P              .intersect(PresburgerSet(piece.getDomain()))
165*d5a29442SArjun P              .isIntegerEmpty() &&
166*d5a29442SArjun P          "New piece's domain overlaps with that of existing pieces!");
167*d5a29442SArjun P   pieces.push_back(piece);
168*d5a29442SArjun P }
169*d5a29442SArjun P 
170*d5a29442SArjun P void PWMAFunction::addPiece(const IntegerPolyhedron &domain,
171*d5a29442SArjun P                             const Matrix &output) {
172*d5a29442SArjun P   addPiece(MultiAffineFunction(domain, output));
173*d5a29442SArjun P }
174*d5a29442SArjun P 
175*d5a29442SArjun P void PWMAFunction::print(raw_ostream &os) const {
176*d5a29442SArjun P   os << pieces.size() << " pieces:\n";
177*d5a29442SArjun P   for (const MultiAffineFunction &piece : pieces)
178*d5a29442SArjun P     piece.print(os);
179*d5a29442SArjun P }
180*d5a29442SArjun P 
181*d5a29442SArjun P /// The hasCompatibleDimensions functions don't check the number of local ids;
182*d5a29442SArjun P /// functions are still compatible if they have differing number of locals.
183*d5a29442SArjun P bool MultiAffineFunction::hasCompatibleDimensions(
184*d5a29442SArjun P     const MultiAffineFunction &f) const {
185*d5a29442SArjun P   return getNumDimIds() == f.getNumDimIds() &&
186*d5a29442SArjun P          getNumSymbolIds() == f.getNumSymbolIds() &&
187*d5a29442SArjun P          getNumOutputs() == f.getNumOutputs();
188*d5a29442SArjun P }
189*d5a29442SArjun P bool PWMAFunction::hasCompatibleDimensions(const MultiAffineFunction &f) const {
190*d5a29442SArjun P   return getNumDimIds() == f.getNumDimIds() &&
191*d5a29442SArjun P          getNumSymbolIds() == f.getNumSymbolIds() &&
192*d5a29442SArjun P          getNumOutputs() == f.getNumOutputs();
193*d5a29442SArjun P }
194*d5a29442SArjun P bool PWMAFunction::hasCompatibleDimensions(const PWMAFunction &f) const {
195*d5a29442SArjun P   return getNumDimIds() == f.getNumDimIds() &&
196*d5a29442SArjun P          getNumSymbolIds() == f.getNumSymbolIds() &&
197*d5a29442SArjun P          getNumOutputs() == f.getNumOutputs();
198*d5a29442SArjun P }
199