Skip to content

Commit 70fb946

Browse files
committed
add basic polynomial ops
1 parent 6cebd35 commit 70fb946

File tree

6 files changed

+302
-9
lines changed

6 files changed

+302
-9
lines changed

mlir/include/mlir/Dialect/Polynomial/IR/Polynomial.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ class Polynomial {
102102

103103
unsigned getDegree() const;
104104

105+
ArrayRef<Monomial> getTerms() const { return terms; }
106+
105107
friend ::llvm::hash_code hash_value(const Polynomial &arg);
106108

107109
private:

mlir/include/mlir/Dialect/Polynomial/IR/Polynomial.td

Lines changed: 121 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -131,23 +131,137 @@ def Polynomial_PolynomialType : Polynomial_Type<"Polynomial", "polynomial"> {
131131
let assemblyFormat = "`<` $ring `>`";
132132
}
133133

134+
def PolynomialLike: TypeOrContainer<Polynomial_PolynomialType, "polynomial-like">;
135+
134136
class Polynomial_Op<string mnemonic, list<Trait> traits = []> :
135-
Op<Polynomial_Dialect, mnemonic, traits # [Pure]>;
137+
Op<Polynomial_Dialect, mnemonic, traits # [Pure]> {
138+
let assemblyFormat = [{
139+
operands attr-dict `:` `(` qualified(type(operands)) `)` `->` qualified(type(results))
140+
}];
141+
}
136142

137143
class Polynomial_UnaryOp<string mnemonic, list<Trait> traits = []> :
138144
Polynomial_Op<mnemonic, traits # [SameOperandsAndResultType]> {
139145
let arguments = (ins Polynomial_PolynomialType:$operand);
140146
let results = (outs Polynomial_PolynomialType:$result);
141-
142-
let assemblyFormat = "$operand attr-dict `:` qualified(type($result))";
143147
}
144148

145149
class Polynomial_BinaryOp<string mnemonic, list<Trait> traits = []> :
146-
Polynomial_Op<mnemonic, traits # [SameOperandsAndResultType]> {
147-
let arguments = (ins Polynomial_PolynomialType:$lhs, Polynomial_PolynomialType:$rhs);
148-
let results = (outs Polynomial_PolynomialType:$result);
150+
Polynomial_Op<mnemonic, !listconcat(traits, [Pure, SameOperandsAndResultType, ElementwiseMappable])> {
151+
let arguments = (ins PolynomialLike:$lhs, PolynomialLike:$rhs);
152+
let results = (outs PolynomialLike:$result);
153+
let assemblyFormat = "operands attr-dict `:` qualified(type($result))";
154+
}
155+
156+
def Polynomial_AddOp : Polynomial_BinaryOp<"add", [Commutative]> {
157+
let summary = "Addition operation between polynomials.";
158+
}
159+
160+
def Polynomial_SubOp : Polynomial_BinaryOp<"sub"> {
161+
let summary = "Subtraction operation between polynomials.";
162+
}
163+
164+
def Polynomial_MulOp : Polynomial_BinaryOp<"mul", [Commutative]> {
165+
let summary = "Multiplication operation between polynomials.";
166+
}
167+
168+
def Polynomial_MulScalarOp : Polynomial_Op<"mul_scalar", [
169+
ElementwiseMappable, AllTypesMatch<["polynomial", "output"]>]> {
170+
let summary = "Multiplication by a scalar of the field.";
171+
172+
let arguments = (ins
173+
PolynomialLike:$polynomial,
174+
AnyInteger:$scalar
175+
);
176+
177+
let results = (outs
178+
PolynomialLike:$output
179+
);
180+
181+
let assemblyFormat = "operands attr-dict `:` qualified(type($polynomial)) `,` type($scalar)";
182+
}
183+
184+
def Polynomial_LeadingTermOp: Polynomial_Op<"leading_term"> {
185+
let summary = "Compute the leading term of the polynomial.";
186+
let description = [{
187+
The degree of a polynomial is the largest $k$ for which the coefficient
188+
$a_k$ of $x^k$ is nonzero. The leading term is the term $a_k x^k$, which
189+
this op represents as a pair of results.
190+
}];
191+
let arguments = (ins Polynomial_PolynomialType:$input);
192+
let results = (outs Index:$degree, AnyInteger:$coefficient);
193+
let assemblyFormat = "operands attr-dict `:` qualified(type($input)) `->` `(` type($degree) `,` type($coefficient) `)`";
194+
}
195+
196+
def Polynomial_MonomialOp: Polynomial_Op<"monomial"> {
197+
let summary = "Create a polynomial that consists of a single monomial.";
198+
let arguments = (ins AnyInteger:$coefficient, Index:$degree);
199+
let results = (outs Polynomial_PolynomialType:$output);
200+
}
201+
202+
def Polynomial_MonomialMulOp: Polynomial_Op<"monomial_mul", [AllTypesMatch<["input", "output"]>]> {
203+
let summary = "Multiply a polynomial by a monic monomial.";
204+
let description = [{
205+
In the ring of polynomials mod $x^n - 1$, `monomial_mul` can be interpreted
206+
as a cyclic shift of the coefficients of the polynomial. For some rings,
207+
this results in optimized lowerings that involve rotations and rescaling
208+
of the coefficients of the input.
209+
}];
210+
let arguments = (ins Polynomial_PolynomialType:$input, Index:$monomialDegree);
211+
let results = (outs Polynomial_PolynomialType:$output);
212+
let hasVerifier = 1;
213+
}
214+
215+
def Polynomial_FromTensorOp : Polynomial_Op<"from_tensor", [Pure]> {
216+
let summary = "Creates a polynomial from integer coefficients stored in a tensor.";
217+
let description = [{
218+
`polynomial.from_tensor` creates a polynomial value from a tensor of coefficients.
219+
The input tensor must list the coefficients in degree-increasing order.
220+
221+
The input one-dimensional tensor may have size at most the degree of the
222+
ring's ideal generator polynomial, with smaller dimension implying that
223+
all higher-degree terms have coefficient zero.
224+
}];
225+
let arguments = (ins RankedTensorOf<[AnyInteger]>:$input);
226+
let results = (outs Polynomial_PolynomialType:$output);
227+
228+
let assemblyFormat = "$input attr-dict `:` type($input) `->` qualified(type($output))";
229+
230+
let builders = [
231+
// Builder that infers coefficient modulus from tensor bit width,
232+
// and uses whatever input ring is provided by the caller.
233+
OpBuilder<(ins "::mlir::Value":$input, "RingAttr":$ring)>
234+
];
235+
let hasVerifier = 1;
236+
}
237+
238+
def Polynomial_ToTensorOp : Polynomial_Op<"to_tensor", [Pure]> {
239+
let summary = "Creates a tensor containing the coefficients of a polynomial.";
240+
let description = [{
241+
`polynomial.to_tensor` creates a tensor value containing the coefficients of the
242+
input polynomial. The output tensor contains the coefficients in
243+
degree-increasing order.
244+
245+
Operations that act on the coefficients of a polynomial, such as extracting
246+
a specific coefficient or extracting a range of coefficients, should be
247+
implemented by composing `to_tensor` with the relevant `tensor` dialect
248+
ops.
249+
250+
The output tensor has shape equal to the degree of the ring's ideal
251+
generator polynomial, including zeroes.
252+
}];
253+
let arguments = (ins Polynomial_PolynomialType:$input);
254+
let results = (outs RankedTensorOf<[AnyInteger]>:$output);
255+
let assemblyFormat = "$input attr-dict `:` qualified(type($input)) `->` type($output)";
256+
257+
let hasVerifier = 1;
258+
}
149259

150-
let assemblyFormat = "$lhs `,` $rhs attr-dict `:` qualified(type($result))";
260+
def Polynomial_ConstantOp : Polynomial_Op<"constant", [Pure]> {
261+
let summary = "Define a constant polynomial via an attribute.";
262+
let arguments = (ins Polynomial_PolynomialAttr:$input);
263+
let results = (outs Polynomial_PolynomialType:$output);
264+
let assemblyFormat = "$input attr-dict `:` qualified(type($output))";
151265
}
152266

153267
#endif // POLYNOMIAL_OPS

mlir/lib/Dialect/Polynomial/IR/PolynomialDialect.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,18 @@
88

99
#include "mlir/Dialect/Polynomial/IR/Polynomial.h"
1010

11+
#include "mlir/Dialect/Arith/IR/Arith.h"
1112
#include "mlir/Dialect/Polynomial/IR/PolynomialAttributes.h"
1213
#include "mlir/Dialect/Polynomial/IR/PolynomialOps.h"
1314
#include "mlir/Dialect/Polynomial/IR/PolynomialTypes.h"
15+
#include "mlir/IR/Builders.h"
16+
#include "mlir/IR/BuiltinOps.h"
17+
#include "mlir/IR/BuiltinTypes.h"
18+
#include "mlir/IR/Dialect.h"
19+
#include "mlir/IR/PatternMatch.h"
20+
#include "mlir/Interfaces/InferTypeOpInterface.h"
21+
#include "mlir/Support/LogicalResult.h"
22+
#include "llvm/ADT/APInt.h"
1423
#include "llvm/ADT/TypeSwitch.h"
1524

1625
using namespace mlir;

mlir/lib/Dialect/Polynomial/IR/PolynomialOps.cpp

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,90 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9+
#include "mlir/Dialect/Polynomial/IR/PolynomialOps.h"
910
#include "mlir/Dialect/Polynomial/IR/Polynomial.h"
11+
#include "mlir/Dialect/Polynomial/IR/PolynomialAttributes.h"
12+
#include "mlir/Dialect/Polynomial/IR/PolynomialTypes.h"
13+
#include "mlir/IR/Builders.h"
14+
#include "mlir/IR/BuiltinTypes.h"
15+
#include "mlir/IR/Dialect.h"
16+
#include "mlir/Support/LogicalResult.h"
17+
#include "llvm/ADT/APInt.h"
1018

1119
using namespace mlir;
1220
using namespace mlir::polynomial;
1321

14-
#define GET_OP_CLASSES
15-
#include "mlir/Dialect/Polynomial/IR/Polynomial.cpp.inc"
22+
void FromTensorOp::build(OpBuilder &builder, OperationState &result,
23+
Value input, RingAttr ring) {
24+
TensorType tensorType = dyn_cast<TensorType>(input.getType());
25+
auto bitWidth = tensorType.getElementTypeBitWidth();
26+
APInt cmod(1 + bitWidth, 1);
27+
cmod = cmod << bitWidth;
28+
Type resultType = PolynomialType::get(builder.getContext(), ring);
29+
build(builder, result, resultType, input);
30+
}
31+
32+
LogicalResult FromTensorOp::verify() {
33+
auto tensorShape = getInput().getType().getShape();
34+
auto ring = getOutput().getType().getRing();
35+
auto polyDegree = ring.getPolynomialModulus().getPolynomial().getDegree();
36+
bool compatible = tensorShape.size() == 1 && tensorShape[0] <= polyDegree;
37+
if (!compatible) {
38+
return emitOpError()
39+
<< "input type " << getInput().getType()
40+
<< " does not match output type " << getOutput().getType()
41+
<< ". The input type must be a tensor of shape [d] where d "
42+
"is at most the degree of the polynomialModulus of "
43+
"the output type's ring attribute.";
44+
}
45+
46+
APInt coefficientModulus = ring.getCoefficientModulus().getValue();
47+
unsigned cmodBitWidth = coefficientModulus.ceilLogBase2();
48+
unsigned inputBitWidth = getInput().getType().getElementTypeBitWidth();
49+
50+
if (inputBitWidth > cmodBitWidth) {
51+
return emitOpError() << "input tensor element type "
52+
<< getInput().getType().getElementType()
53+
<< " is too large to fit in the coefficients of "
54+
<< getOutput().getType()
55+
<< ". The input tensor's elements must be rescaled"
56+
" to fit before using from_tensor.";
57+
}
58+
59+
return success();
60+
}
61+
62+
LogicalResult ToTensorOp::verify() {
63+
auto tensorShape = getOutput().getType().getShape();
64+
auto polyDegree = getInput()
65+
.getType()
66+
.getRing()
67+
.getPolynomialModulus()
68+
.getPolynomial()
69+
.getDegree();
70+
bool compatible = tensorShape.size() == 1 && tensorShape[0] == polyDegree;
71+
72+
return compatible
73+
? success()
74+
: emitOpError()
75+
<< "input type " << getInput().getType()
76+
<< " does not match output type " << getOutput().getType()
77+
<< ". The input type must be a tensor of shape [d] where d "
78+
"is exactly the degree of the polynomialModulus of "
79+
"the output type's ring attribute.";
80+
}
81+
82+
LogicalResult MonomialMulOp::verify() {
83+
auto ring = getInput().getType().getRing();
84+
auto idealTerms = ring.getPolynomialModulus().getPolynomial().getTerms();
85+
bool compatible =
86+
idealTerms.size() == 2 &&
87+
(idealTerms[0].coefficient == -1 && idealTerms[0].exponent == 0) &&
88+
(idealTerms[1].coefficient == 1);
89+
90+
return compatible ? success()
91+
: emitOpError()
92+
<< "ring type " << ring
93+
<< " is not supported yet. The ring "
94+
"must be of the form (x^n - 1) for some n";
95+
}

mlir/test/Dialect/Polynomial/ops.mlir

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// RUN: mlir-opt %s | FileCheck %s
2+
3+
// This simply tests for syntax.
4+
5+
#my_poly = #polynomial.polynomial<1 + x**1024>
6+
#my_poly_2 = #polynomial.polynomial<2>
7+
#my_poly_3 = #polynomial.polynomial<3x>
8+
#my_poly_4 = #polynomial.polynomial<t**3 + 4t + 2>
9+
#ring1 = #polynomial.ring<coefficientType=i32, coefficientModulus=2837465, polynomialModulus=#my_poly>
10+
#one_plus_x_squared = #polynomial.polynomial<1 + x**2>
11+
12+
#ideal = #polynomial.polynomial<-1 + x**1024>
13+
#ring = #polynomial.ring<coefficientType=i32, coefficientModulus=18, polynomialModulus=#ideal>
14+
!poly_ty = !polynomial.polynomial<#ring>
15+
16+
module {
17+
func.func @test_multiply() -> !polynomial.polynomial<#ring1> {
18+
%c0 = arith.constant 0 : index
19+
%two = arith.constant 2 : i16
20+
%five = arith.constant 5 : i16
21+
%coeffs1 = tensor.from_elements %two, %two, %five : tensor<3xi16>
22+
%coeffs2 = tensor.from_elements %five, %five, %two : tensor<3xi16>
23+
24+
%poly1 = polynomial.from_tensor %coeffs1 : tensor<3xi16> -> !polynomial.polynomial<#ring1>
25+
%poly2 = polynomial.from_tensor %coeffs2 : tensor<3xi16> -> !polynomial.polynomial<#ring1>
26+
27+
%3 = polynomial.mul %poly1, %poly2 : !polynomial.polynomial<#ring1>
28+
29+
return %3 : !polynomial.polynomial<#ring1>
30+
}
31+
32+
func.func @test_elementwise(%p0 : !polynomial.polynomial<#ring1>, %p1: !polynomial.polynomial<#ring1>) {
33+
%tp0 = tensor.from_elements %p0, %p1 : tensor<2x!polynomial.polynomial<#ring1>>
34+
%tp1 = tensor.from_elements %p1, %p0 : tensor<2x!polynomial.polynomial<#ring1>>
35+
36+
%c = arith.constant 2 : i32
37+
%mul_const_sclr = polynomial.mul_scalar %tp0, %c : tensor<2x!polynomial.polynomial<#ring1>>, i32
38+
39+
%add = polynomial.add %tp0, %tp1 : tensor<2x!polynomial.polynomial<#ring1>>
40+
%sub = polynomial.sub %tp0, %tp1 : tensor<2x!polynomial.polynomial<#ring1>>
41+
%mul = polynomial.mul %tp0, %tp1 : tensor<2x!polynomial.polynomial<#ring1>>
42+
43+
return
44+
}
45+
46+
func.func @test_to_from_tensor(%p0 : !polynomial.polynomial<#ring1>) {
47+
%c0 = arith.constant 0 : index
48+
%two = arith.constant 2 : i16
49+
%coeffs1 = tensor.from_elements %two, %two : tensor<2xi16>
50+
// CHECK: from_tensor
51+
%poly = polynomial.from_tensor %coeffs1 : tensor<2xi16> -> !polynomial.polynomial<#ring1>
52+
// CHECK: to_tensor
53+
%tensor = polynomial.to_tensor %poly : !polynomial.polynomial<#ring1> -> tensor<1024xi16>
54+
55+
return
56+
}
57+
58+
func.func @test_degree(%p0 : !polynomial.polynomial<#ring1>) {
59+
%0, %1 = polynomial.leading_term %p0 : !polynomial.polynomial<#ring1> -> (index, i32)
60+
return
61+
}
62+
63+
func.func @test_monomial() {
64+
%deg = arith.constant 1023 : index
65+
%five = arith.constant 5 : i16
66+
%0 = polynomial.monomial %five, %deg : (i16, index) -> !polynomial.polynomial<#ring1>
67+
return
68+
}
69+
70+
func.func @test_constant() {
71+
%0 = polynomial.constant #one_plus_x_squared : !polynomial.polynomial<#ring1>
72+
%1 = polynomial.constant <1 + x**2> : !polynomial.polynomial<#ring1>
73+
return
74+
}
75+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: mlir-opt --verify-diagnostics %s
2+
3+
#my_poly = #polynomial.polynomial<1 + x**1024>
4+
#ring = #polynomial.ring<coefficientType=i16, coefficientModulus=256, polynomialModulus=#my_poly>
5+
module {
6+
func.func @test_from_tensor_too_large_coeffs() {
7+
%two = arith.constant 2 : i32
8+
%coeffs1 = tensor.from_elements %two, %two : tensor<2xi32>
9+
// expected-error@below {{is too large to fit in the coefficients}}
10+
%poly = polynomial.from_tensor %coeffs1 : tensor<2xi32> -> !polynomial.polynomial<#ring>
11+
return
12+
}
13+
}

0 commit comments

Comments
 (0)