Skip to content

Commit 4aa2e83

Browse files
Merge pull request #3303 from google:mdgrs/layoutPropConvFixes
PiperOrigin-RevId: 960967691
2 parents cbf0016 + ef5798c commit 4aa2e83

12 files changed

Lines changed: 641 additions & 61 deletions

File tree

lib/Kernel/KernelImplementationTest.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include <cstdint>
22
#include <functional>
33
#include <map>
4+
#include <string>
45
#include <vector>
56

67
#include "gtest/gtest.h" // from @googletest
@@ -737,6 +738,74 @@ TEST_P(KernelImplementationTest, TestConv1dCwFcwStride2) {
737738
EXPECT_EQ(actualUnpacked, expected);
738739
}
739740

741+
// End-to-end Halevi-Shoup matvec for a padded strided 1-D multichannel conv, in
742+
// the packing production uses when LayoutPropagation folds a zero tensor.pad on
743+
// the width dim into the conv's own `padding` parameter: the data ciphertext is
744+
// packed row-major at the *unpadded* width, and the Toeplitz matrix is built
745+
// with padding = p so that a window reaching into the padding contributes no
746+
// column. `expected` is the convolution of the zero-padded data.
747+
void checkPaddedConv1dCwFcw(int64_t padding, const tensor3d& expected,
748+
bool unroll, bool interchangeRows) {
749+
SCOPED_TRACE("padding = " + std::to_string(padding));
750+
MLIRContext context;
751+
// 1x2x6 input data, 2x2x3 filter, stride 2.
752+
tensor3d data = {{{0, 1, 2, 3, 4, 5}, {6, 7, 8, 9, 10, 11}}};
753+
tensor3d filter = {{{3, 4, 1}, {1, 5, 2}}, {{1, 2, 3}, {2, 2, 2}}};
754+
int64_t stride = 2;
755+
int numSlots = 16;
756+
757+
RankedTensorType dataType =
758+
RankedTensorType::get({1, 2, 6}, mlir::IndexType::get(&context));
759+
RankedTensorType filterType =
760+
RankedTensorType::get({2, 2, 3}, mlir::IndexType::get(&context));
761+
762+
auto dataLayout = getRowMajorLayoutRelation(dataType, numSlots);
763+
std::vector<std::vector<int>> packedData =
764+
evaluateLayout(dataLayout, getDataValueFn3D(data));
765+
766+
auto filterLayout = get1dConvCwFcwFilterDiagonalizedRelation(
767+
filterType, dataType, stride, padding, numSlots, interchangeRows);
768+
ASSERT_TRUE(succeeded(filterLayout));
769+
std::function<int(const std::vector<int64_t>&)> getFilterValueFn =
770+
[&](const std::vector<int64_t>& domainPoint) -> int {
771+
return filter[domainPoint[0]][domainPoint[1]][domainPoint[2]];
772+
};
773+
std::vector<std::vector<int>> packedFilter =
774+
evaluateLayout(filterLayout.value(), getFilterValueFn);
775+
// The matrix shape must be derived the same way the filter was diagonalized,
776+
// i.e. against the unpadded data type with padding = p: implementHaleviShoup
777+
// sizes the squat-diagonal collapse from nextPowerOfTwo of these dims.
778+
auto expandedFilterShape =
779+
get1dConvCwFcwFilterExpandedType(filterType, dataType, stride, padding);
780+
781+
auto dag = implementHaleviShoup(
782+
LiteralValue(packedData[0]), LiteralValue(packedFilter),
783+
expandedFilterShape.getShape(), DagType::intTensor(32, {numSlots}),
784+
/*zeroDiagonals=*/{}, unroll);
785+
auto actual = std::get<std::vector<int>>(evalKernel(dag)[0].get());
786+
787+
int64_t outputWidth = expected[0][0].size();
788+
RankedTensorType outputType = RankedTensorType::get(
789+
{1, 2, outputWidth}, mlir::IndexType::get(&context));
790+
auto resultLayout = get1dConvResultRelation(outputType, stride, /*padding=*/0,
791+
numSlots, interchangeRows);
792+
793+
EXPECT_EQ(
794+
unpackLayoutTo3DTensor<int>(resultLayout, {actual}, {1, 2, outputWidth}),
795+
expected);
796+
}
797+
798+
TEST_P(KernelImplementationTest, TestConv1dCwFcwStride2WithPadding) {
799+
// Same shape family as TestConv1dCwFcwStride2, but with padding != 0
800+
// padding 1 keeps the unpadded and padded column counts in the same
801+
// power-of-two bucket (2*6=12 and 2*8=16 both round to 16); padding 2 does
802+
// not (2*6=12 rounds to 16, 2*10=20 rounds to 32).
803+
checkPaddedConv1dCwFcw(/*padding=*/1, {{{45, 79, 111}, {29, 62, 86}}},
804+
std::get<0>(GetParam()), std::get<1>(GetParam()));
805+
checkPaddedConv1dCwFcw(/*padding=*/2, {{{12, 63, 95, 97}, {12, 50, 74, 56}}},
806+
std::get<0>(GetParam()), std::get<1>(GetParam()));
807+
}
808+
740809
TEST_P(KernelImplementationTest,
741810
TestConv2dNchwFchwStride2InterchangedLargeSlots) {
742811
MLIRContext context;

lib/Transforms/ConvertToCiphertextSemantics/ConvertToCiphertextSemantics.cpp

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,7 +1156,29 @@ struct ConvertLinalgConv1DNcwFcw
11561156
return isPowerOfTwoDims && isConv1dAsMatvec;
11571157
}
11581158

1159-
void haleviShoupKernel(
1159+
// Rebuild the operand shape LayoutPropagation diagonalized the filter
1160+
// against, honoring any zero tensor.pad it folded into the conv's `padding`
1161+
// parameter.
1162+
FailureOr<RankedTensorType> expandedFilterShape(
1163+
linalg::Conv1DNcwFcwOp op) const {
1164+
auto filterType = cast<RankedTensorType>(op.getInputs()[1].getType());
1165+
auto dataType = cast<RankedTensorType>(op.getInputs()[0].getType());
1166+
int64_t stride =
1167+
llvm::to_vector(op.getStrides().getValues<int64_t>()).front();
1168+
1169+
int64_t padding = getConvFoldedPadding(op);
1170+
std::optional<ConvMatrixOperand> matrixOperand =
1171+
foldConvWidthPadding(dataType, padding);
1172+
if (!matrixOperand) {
1173+
return op.emitError()
1174+
<< kConvFoldedPaddingAttrName << " of " << padding
1175+
<< " does not fit this conv's data operand " << dataType;
1176+
}
1177+
return get1dConvCwFcwFilterExpandedType(filterType, matrixOperand->dataType,
1178+
stride, matrixOperand->padding);
1179+
}
1180+
1181+
LogicalResult haleviShoupKernel(
11601182
linalg::Conv1DNcwFcwOp op, OpAdaptor adaptor,
11611183
ContextAwareConversionPatternRewriter& rewriter) const {
11621184
LLVM_DEBUG(
@@ -1171,13 +1193,8 @@ struct ConvertLinalgConv1DNcwFcw
11711193
cast<TypedValue<RankedTensorType>>(adaptor.getInputs()[1]);
11721194
SSAValue matrixLeaf(matrix);
11731195

1174-
// The original matrix shape is the shape of the expanded filter before
1175-
// diagonalization.
1176-
RankedTensorType expandedMatrixType = get1dConvCwFcwFilterExpandedType(
1177-
cast<RankedTensorType>(op.getInputs()[1].getType()),
1178-
cast<RankedTensorType>(op.getInputs()[0].getType()),
1179-
llvm::to_vector(op.getStrides().getValues<int64_t>()).front(),
1180-
/*padding=*/0);
1196+
FailureOr<RankedTensorType> expandedMatrixType = expandedFilterShape(op);
1197+
if (failed(expandedMatrixType)) return failure();
11811198
// Collect any zero diagonals of the filter matrix.
11821199
LayoutAttr filterLayout = getLayoutAttr(adaptor.getInputs()[1]);
11831200
auto filterRelation = filterLayout.getIntegerRelation();
@@ -1196,7 +1213,7 @@ struct ConvertLinalgConv1DNcwFcw
11961213
data.getType().getShape().back());
11971214
std::shared_ptr<ArithmeticDagNode<SSAValue>> implementedKernel =
11981215
implementHaleviShoup(vectorLeaf, matrixLeaf,
1199-
expandedMatrixType.getShape(), dagType,
1216+
expandedMatrixType->getShape(), dagType,
12001217
zeroDiagonals,
12011218
/*unroll=*/unrollKernels);
12021219

@@ -1210,6 +1227,7 @@ struct ConvertLinalgConv1DNcwFcw
12101227
// Add the initial accumulator value.
12111228
Value result = adaptor.getOutputs()[0];
12121229
addBiasAndReplace(rewriter, op, finalOutput, result, layoutAttr);
1230+
return success();
12131231
}
12141232

12151233
LogicalResult matchAndRewrite(
@@ -1226,8 +1244,7 @@ struct ConvertLinalgConv1DNcwFcw
12261244
}
12271245

12281246
if (supportsExpandedHaleviShoup(op, adaptor)) {
1229-
haleviShoupKernel(op, adaptor, rewriter);
1230-
return success();
1247+
return haleviShoupKernel(op, adaptor, rewriter);
12311248
}
12321249

12331250
return op.emitError() << "unsupported layout for 1d conv";
@@ -1303,6 +1320,10 @@ struct ConvertLinalgConv2DNchwFchw
13031320

13041321
// The original matrix shape is the shape of the expanded filter before
13051322
// diagonalization.
1323+
// NOTE: `padding=0` is only correct because nothing folds a `tensor.pad`
1324+
// into a 2-D conv's padding parameter. If that changes, go through
1325+
// foldConvWidthPadding the way ConvertLinalgConv1DNcwFcw does; see
1326+
// ConvMatrixOperand.
13061327
RankedTensorType expandedMatrixType = get2dConvChwFchwFilterExpandedType(
13071328
cast<RankedTensorType>(op.getInputs()[1].getType()), dataType,
13081329
/*padding=*/0, llvm::to_vector(op.getStrides().getValues<int64_t>()));

0 commit comments

Comments
 (0)