Skip to content

Commit d13bf8e

Browse files
lwe: per-level encode level attr on LWE_RLWEEncodeOp
Thread the modulus-chain level onto LWE_RLWEEncodeOp (as an optional attr, set from the ciphertext type's modulus chain in the secret->ckks encode lowering and the client-side trivial encryption) so backends with canonical per-level scales can encode at the scale of the target level (e.g. GetScale(level)). TODO(#1643): LWEPlaintextType should carry RNS ring info instead.
1 parent 66e8f26 commit d13bf8e

4 files changed

Lines changed: 30 additions & 17 deletions

File tree

lib/Dialect/LWE/IR/LWEOps.td

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,8 @@ def LWE_RLWEEncodeOp : LWE_Op<"rlwe_encode", [
211211
let arguments = (ins
212212
SignlessIntegerOrFloatLike:$input,
213213
AnyPlaintextEncodingAttr:$encoding,
214-
Polynomial_RingAttr:$ring
214+
Polynomial_RingAttr:$ring,
215+
OptionalAttr<I64Attr>:$level
215216
);
216217

217218
let results = (outs LWEPlaintextLike:$output);

lib/Dialect/LWE/Transforms/ImplementTrivialEncryptionAsAddition.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,8 @@ func::FuncOp getOrCreateEncryptionOfZerosFunc(func::FuncOp parentFunc,
163163
auto plaintextSpace = plaintextType.getPlaintextSpace();
164164
auto encodeOp = RLWEEncodeOp::create(
165165
builder, plaintextType, constantOp.getResult(),
166-
plaintextSpace.getEncoding(), plaintextSpace.getRing());
166+
plaintextSpace.getEncoding(), plaintextSpace.getRing(),
167+
/*level=*/nullptr);
167168
auto encrypted = RLWEEncryptOp::create(
168169
builder, ciphertextType, encodeOp.getResult(), encFuncOp.getArgument(0));
169170
encrypted->setAttrs(originalOp->getAttrs());

lib/Dialect/Secret/Conversions/Patterns.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,13 @@ LogicalResult ConvertClientConceal::lowerToTrivialEncryption(
8686

8787
// Intentionally use op.getCleartext() because we don't want to type-convert
8888
// the input to a ciphertext.
89+
IntegerAttr levelAttr;
90+
if (auto mc = ctTy.getModulusChain())
91+
levelAttr = rewriter.getI64IntegerAttr(mc.getCurrent());
8992
auto encoded = lwe::RLWEEncodeOp::create(
9093
rewriter, op.getLoc(), encodeOpResultTy, op.getCleartext(),
9194
ctTy.getPlaintextSpace().getEncoding(),
92-
ctTy.getPlaintextSpace().getRing());
95+
ctTy.getPlaintextSpace().getRing(), levelAttr);
9396
auto newOp =
9497
lwe::TrivialEncryptOp::create(rewriter, op.getLoc(), resultTy, encoded);
9598
newOp->setAttrs(op->getAttrs());
@@ -145,11 +148,15 @@ LogicalResult ConvertClientConceal::matchAndRewrite(
145148
auto plaintextTy = lwe::LWEPlaintextType::get(op.getContext(),
146149
resultCtTy.getPlaintextSpace());
147150

151+
IntegerAttr encLevelAttr;
152+
if (auto mc = resultCtTy.getModulusChain())
153+
encLevelAttr = rewriter.getI64IntegerAttr(mc.getCurrent());
154+
148155
auto encryptFn = [&](Value cleartext) -> lwe::RLWEEncryptOp {
149-
auto encoded =
150-
lwe::RLWEEncodeOp::create(rewriter, op.getLoc(), plaintextTy, cleartext,
151-
resultCtTy.getPlaintextSpace().getEncoding(),
152-
resultCtTy.getPlaintextSpace().getRing());
156+
auto encoded = lwe::RLWEEncodeOp::create(
157+
rewriter, op.getLoc(), plaintextTy, cleartext,
158+
resultCtTy.getPlaintextSpace().getEncoding(),
159+
resultCtTy.getPlaintextSpace().getRing(), encLevelAttr);
153160
auto encryptOp = lwe::RLWEEncryptOp::create(
154161
rewriter, op.getLoc(), resultCtTy, encoded.getResult(), keyBlockArg);
155162
// Copy attributes from the original op to preserve any mgmt attrs needed by

lib/Utils/ContextAwareConversionUtils.cpp

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,23 +53,27 @@ FailureOr<Value> encodeCleartextAsPlaintext(
5353
return failure();
5454
}
5555

56-
// TODO(#1643): inherit level information to plaintext type from init-op
57-
// mgmt attr. This actually needs to make LWEPlaintextType RNS aware.
56+
// TODO(#1643): LWEPlaintextType should carry RNS ring info. For now,
57+
// the level is passed as an attribute on the encode op.
5858
auto plaintextTy = lwe::LWEPlaintextType::get(
5959
ctx, lwe::PlaintextSpaceAttr::get(ctx, plaintextSpace.getRing(),
6060
plaintextEncoding));
6161

62+
IntegerAttr levelAttr;
63+
if (auto mc = ciphertextElementType.getModulusChain())
64+
levelAttr = builder.getI64IntegerAttr(mc.getCurrent());
65+
6266
// cleartext is a ciphertext-semantic tensor, so it
6367
// could be a tensor<Nxty>, tensor<k x N x ty>, (where k=1 is possible).
6468
// For rank 1, it's a single ciphertext and can be encoded directly.
6569
auto cleartextTensorTy = cast<RankedTensorType>(cleartext.getType());
6670
int64_t numSlots =
6771
cleartextTensorTy.getDimSize(cleartextTensorTy.getRank() - 1);
6872
if (cleartextTensorTy.getRank() == 1) {
69-
Value encodeOp =
70-
lwe::RLWEEncodeOp::create(builder, plaintextTy, cleartext,
71-
plaintextEncoding, plaintextSpace.getRing())
72-
.getResult();
73+
Value encodeOp = lwe::RLWEEncodeOp::create(
74+
builder, plaintextTy, cleartext, plaintextEncoding,
75+
plaintextSpace.getRing(), levelAttr)
76+
.getResult();
7377
return encodeOp;
7478
}
7579

@@ -87,10 +91,10 @@ FailureOr<Value> encodeCleartextAsPlaintext(
8791
SmallVector<OpFoldResult> strides(2, builder.getIndexAttr(1));
8892
auto slice = tensor::ExtractSliceOp::create(builder, sliceTy, cleartext,
8993
offsets, sizes, strides);
90-
Value encodedSlice =
91-
lwe::RLWEEncodeOp::create(builder, plaintextTy, slice,
92-
plaintextEncoding, plaintextSpace.getRing())
93-
.getResult();
94+
Value encodedSlice = lwe::RLWEEncodeOp::create(
95+
builder, plaintextTy, slice, plaintextEncoding,
96+
plaintextSpace.getRing(), levelAttr)
97+
.getResult();
9498
encodedSlices.push_back(encodedSlice);
9599
}
96100

0 commit comments

Comments
 (0)