← HomOps Reference
HomMatMul
Multiply an encrypted value by a plaintext weight matrix along one axis.
MmML
RunsServer
Changes shapeYes - the multiplied axis:
in_dim becomes out_dimChanges scaleYes - plaintext encoding scale
pt_scaleLevels spentPath-dependent; mod-switch enabled by default
RotatesYes - internal slot sum
KeysRotation key
What it does
HomMatMul computes weight @ input along one axis of the ciphertext: multiply by a plaintext weight matrix, then sum along that axis. The weights are fixed data you attach with set_data; the input is encrypted.It is the weight step of a linear layer. HomLinear is
HomMatMul plus a bias (HomConstAdd); use HomMatMul directly when there is no bias, or when the multiply is not a layer at all (a fixed projection, a pooling mask, RGB to grayscale).The backend selects a Simple, Diags, or Masks path from
mul_axis, the packing axis, and new_n_axis. Slot-axis reductions can rotate internally and require rotation-key entries; simpler layouts avoid that work.Signature
HomMatMul( dims, # required: weight / plaintext factor shape mul_axis=-1, new_n_axis=None, with_modswitch=True, rows_budget=None, pt_scale=None, # defaults to context / scheme num_steps=None, )
The weights come in through
set_data:pipeline.pyPYTHON
pipeline.set_data(op_index, weight_tensor) # shape must equal dims
Parameters
ParameterTypeDefaultDescription
dims(out, in)requiredThe weight matrix shape, same layout as weight.shape for out = weight @ input.mul_axisint-1The ciphertext axis to multiply and reduce.new_n_axisint | NoneNoneWhen mul_axis is the packing axis, selects the output axis that becomes the new packing axis. The backend chooses one when unset.with_modswitchboolTrueEnable path-dependent mod-switching after packed multiplies.rows_budgetsequence | NoneNoneAbsolute rows allowed for automatic mod-switches.pt_scaleintcontext pt_scaleEncoding scale for the plaintext weights. If unset, the backend uses the context or scheme default.num_stepsint | NoneNoneRotation decomposition steps for the Diags/Masks paths; this affects staging, not the mathematical result.Rules that depend on the value
SettingRuleIf you break it
dimsThe weight dimension being reduced must equal the ciphertext size along mul_axis, and the tensor passed to set_data must have shape dims.Compilation fails.mul_axis / new_n_axisThese packing choices select Simple, Diags, or Masks. When the selected path reduces across slots, rotation-key entries are required.-Requirements
set_datacalled. The weights must be attached before the pipeline compiles.- Rotation key entries. Diags and Masks paths may require internal rotations. The compiler derives the offsets for the evaluation key; you don't list them yourself.
These surface as compilation errors; see Compilation errors.
Shape effect - yes
Rule: the multiplied axis changes size from
in_dim to out_dim; every other axis is preserved.shape.pyPYTHON
# input shape: (..., in_dim, ...) with in_dim at mul_axis # output shape: (..., out_dim, ...) dims = (out_dim, in_dim) for 2D weights
Input shapedimsmul_axisOutput shape
(128,)(10, 128)-1(10,)
(128, 512)(16, 128)-1(16, 512)
Scale effect - yes
Rule: plaintext factors are encoded at
pt_scale. The resulting scale depends on the selected Simple, Diags, or Masks path and its internal plaintext multiplies.Use
pt_scale to control plaintext encoding precision; inspect the compiled pipeline for the resulting output scale rather than assuming one fixed multiplication factor.pt_scaleOutput scale
2^30 (context default)Path-dependent compiled output scale
2^20Lower plaintext encoding scale; inspect compilation output
MatMul can grow scale through its plaintext multiplications. The compiler reports the exact outcome for the selected path; use that report when planning the next operation.
Level budget
with_modswitch=True by default permits path-dependent mod-switching after packed multiplies. Set rows_budget to constrain the rows it may use; inspect the compiled plan for the exact level cost.Keys
Rotation key. Required by Diags/Masks paths that rotate packed ciphertext slots; offsets are derived at compile time. No square key: the encrypted operand is multiplied only by plaintexts.
Example
Minimal matmul
pipeline.pyPYTHON
from lattica_build.operators import HomMatMul from lattica_build.base_classes.hom_pipeline import HomomorphicPipeline from lattica_build.operators.composite.sequential import SequentialHomOp import torch mat_dims = (16, 128) pipeline = HomomorphicPipeline( hom=SequentialHomOp( HomMatMul(mat_dims, mul_axis=0), ), input_shape=(128, 512), ) pipeline.set_data(0, torch.rand(mat_dims)) # output shape: (16, 512)
The FC tail of a classifier
After a quadratic activation, project 128 features to 10 classes:
Stagept_shapept_scale
After
HomSquare(128,)2^60After
HomMatMul((10, 128))(10,)Depends on pt_scale and the selected backend pathWith bias, write HomLinear instead; it chains the same matmul plus a
HomConstAdd.See also
- HomLinear - matmul plus bias in one op
- HomSumSlots - the slot sum that runs inside
- HomConstMul - element-wise plaintext multiply, no summation
- Ciphertext state - Scale - why matmul grows scale fastest