← HomOps Reference
HomConv
Run a 2D convolution on packed encrypted feature maps.
CvML
RunsServer
Changes shapeYes - channels:
C_in → C_out; spatial length stays H*WChanges scaleYes - packed kernel multiply
Levels spentTypically 1 - packed mul with mod-switch
RotatesYes - kernel tap alignments
KeysRotation key
What it does
HomConv is a first-class 2D convolution (OP_TYPE.Conv). It is not an Unfold → Linear → Reshape composite. The backend aligns kernel taps with HomRotateSum, multiplies by packed plaintext kernel masks, sums over rotations and in-channels, optionally adds bias, and applies a stride gate when stride != (1, 1).Reach for it when you need encrypted Conv2d-style layers on square spatial maps, or grouped / depthwise conv via
groups (see HomAvgPool). For a fused conv + batch-norm path, see HomConvBnFused.Signature
HomConv( in_channels, # required out_channels, # required kernel_size, # int | (h, w) stride, # int | (h, w) padding, # int | (h, w) | None → (kH, kW) groups=1, bias=False, dilation=(1, 1), image_hw=None, )
Kernel weights (and bias when enabled) come in through
set_data. The stored kernel shape is (out_channels, in_channels // groups, kH, kW).Parameters
ParameterTypeDefaultDescription
in_channelsintrequiredInput channels C_in.out_channelsintrequiredOutput channels C_out.kernel_sizeint | (h, w)requiredNormalized to (kH, kW).strideint | (h, w)requiredNormalized to a 2-tuple. (1, 1) takes the non-strided path.paddingint | (h, w) | Nonerequired argIf None, defaults to (kH, kW). Otherwise normalized to a 2-tuple.groupsint1Grouped convolution. Must be greater than 0 and divide both channel counts.biasboolFalseIf True, set_data expects weight plus a 1D bias of length C_out.dilationint | (h, w)(1, 1)Spatial dilation of the kernel, as a scalar or (dh, dw).image_hw(h, w) | NoneNoneOptional input image spatial size hint used when the packed layout needs an explicit (H, W).Rules that depend on the value
SettingRuleIf you break it
groupsMust be > 0 and divide both in_channels and out_channels.ValueError.set_data (bias=False)set_data(weight) with weight.shape == kernel_shape.Shape check fails.set_data (bias=True)set_data(weight, bias); bias is 1D with length C_out.Shape check fails.Requirements
set_datacalled. Weights must be attached before prep (AvgPool sets them in the constructor).- Rotation key entries. Every non-identity kernel tap rotation needs an entry in the evaluation key. The compiler derives them; you don't list them yourself.
- Square packing. Feature maps are packed as
(C, H*W)withH == W. Ring dimension must satisfyinternal_n ≥ H*W. - Identity tap. Padding must keep the identity kernel tap present:
0 ≤ pad_h < kHand0 ≤ pad_w < kW.
Typical packing: reshape
(C, H, W) → (C, H*W) before encrypt (see HomReshape), with n_axis on the spatial slots axis. These surface as compilation errors; see Compilation errors.Shape effect - yes
Rule: deploy infers
tensor_shape = (C_out, H*W) with n_axis = -1. Spatial length H*W is preserved in metadata; strided conv zeros inactive slots via a stride gate (values live on a coarser grid inside the same length).Stagept_shape
client_pre
HomReshape((C_in, H*W))(C_in, H*W)After
HomConv(C_out, H*W)Scale effect - yes
The packed kernel multiply updates scale the same way a ConstMul / vector packing multiply does. Bias add does not multiply scale. How scale moves through a pipeline is on Ciphertext state - Scale.
Level budget
StepLevels spent
RotateSum (stack)0
Packed kernel mulTypically 1 (deploy models mod-switch)
AxisModSum0
Bias add0
How primes are organized in the chain is explained on Level budget - The chain.
Keys
Rotation key. Needed for kernel tap alignments. No square key: the encrypted operand is multiplied only by plaintexts.
Example
A biased 3×3 conv on a 32×32 RGB map, packed then unpacked around the encrypted step:
pipeline.pyPYTHON
from lattica_build.operators import HomConv, HomReshape from lattica_build.base_classes.hom_pipeline import HomomorphicPipeline import torch C_in, C_out, H = 3, 8, 32 conv = HomConv( in_channels=C_in, out_channels=C_out, kernel_size=3, stride=1, padding=1, groups=1, bias=True, ) conv.set_data( torch.randn(C_out, C_in, 3, 3), torch.randn(C_out), ) pipeline = HomomorphicPipeline( client_pre=[HomReshape((C_in, H * H))], hom=conv, client_post=[HomReshape((C_out, H, H))], input_shape=(C_in, H, H), n_axis=1, )
See also
- HomAvgPool - depthwise avg-pool built as
HomConv - HomConvBnFused - fuse Conv2d + BatchNorm2d into one encrypted conv
- HomRotateSum - rotate-and-add primitive used for kernel taps
- HomReshape - pack
(C, H, W)↔(C, H*W)