← HomOps Reference
HomLinear
Fully connected layer: packed matmul of ciphertext by plaintext weights, with optional bias.
LnML
RunsServer
Changes shapeYes - MatMul axis
Changes scaleYes - via MatMul
Levels spent0 or 1 via MatMul with_modswitch
RotatesYes - MatMul path
KeysRotation key
What it does
HomLinear is a fully connected / linear layer: packed matrix multiply of a ciphertext by plaintext weights, optionally followed by a plaintext bias add.It is a composite (
SequentialHomOp), not a registered leaf. Children:HomMatMul(...) → optional HomConstAdd(...) when bias=True.Reach for it when you need an encrypted dense / FC layer, or weight + bias in one composite with a single
set_data(weight, bias). For the multiply alone, use HomMatMul.Signature
HomLinear( dims, # required: (out_features, in_features) bias=True, mul_axis=-1, mul_scale=None, # forwarded to HomMatMul as pt_scale with_modswitch=True, )
Weights (and bias, when enabled) come in through
set_data. dims must be 2D: (out_features, in_features).Parameters
ParameterTypeDefaultDescription
dimsTensorShaperequiredWeight shape (out_features, in_features); must be 2D.mul_axisint-1Axis of the ciphertext / weight broadcast along which MatMul multiplies and reduces. Passed through to HomMatMul.mul_scaleint | NoneNonePlaintext encoding scale forwarded to HomMatMul as pt_scale. When None, MatMul uses the context / scheme default. HomLinear itself has no pt_scale argument.with_modswitchboolTrueForwarded to MatMul: drop a modulus level after the packed multiplies when enabled.Bias shape. When
bias=True, the bias ConstAdd dims drop the mul axis from dims. Example: dims=(64, 784), mul_axis=-1 → bias shape (64,).set_dataParameterTypeDefaultDescription
weightTensorrequiredWeight matrix; shape must equal dims. Always required.biasTensorrequired if bias=TrueBias vector. Required when bias=True; omit when bias=False (set_data(weight) only).Raises
ValueError if bias=True but only one tensor is provided. Weight goes to the MatMul child; bias to the ConstAdd child.Rules that depend on the value
SettingRuleIf you break it
dimsMust be length 2 (assert len(dims) == 2).Assertion fails at construction.set_dataWhen bias=True, pass both weight and bias tensors.ValueError.Requirements
- Weight 2D.
dimsmust be(out_features, in_features). set_datacalled. Attach weight (and bias when enabled) before compile / prep.- Rotation key. Usually yes: MatMul paths that rotate need rotation key entries in the evaluation key. The compiler derives them; you don't list them yourself.
These surface as compilation errors; see Compilation errors.
Shape effect - yes
Rule: shape follows HomMatMul: the multiplied axis changes from
in_features to out_features; every other axis is preserved. Optional bias ConstAdd broadcasts and does not change rank beyond that.Inputdimsmul_axisOutput
(784,)(64, 784)-1(64,) - in_features axis replaced
(..., 784, ...)(64, 784)at in_features(..., 64, ...) - per MatMul rules
Exact packing follows MatMul's Simple vs Diags vs Masks selection. Shape and the n axis are covered on Concepts.
Scale effect - yes
Rule: scale is dominated by HomMatMul (HomLinear's
mul_scale is forwarded as MatMul pt_scale). Bias HomConstAdd does not change scale.StageEffect on scale
After MatMulPer HomMatMul: grows with the forwarded
pt_scale (mul_scale on HomLinear)After bias ConstAddUnchanged
How scale moves through a pipeline is on Ciphertext state - Scale.
Level budget
ConfigurationLevels spent
with_modswitch=True (default)1 - MatMul drops one prime after the mul path
with_modswitch=False0 from MatMul
Bias ConstAdd0
How primes are organized in the chain is explained on Level budget - The chain.
Keys
Rotation key. Often required: MatMul rotation paths need their offsets in the evaluation key. No square key from HomLinear itself.
Example
A 784 → 64 dense layer with bias:
pipeline.pyPYTHON
from lattica_build.operators import HomLinear from lattica_build.base_classes.hom_pipeline import HomomorphicPipeline from lattica_build.operators.composite.sequential import SequentialHomOp import torch linear = HomLinear( dims=(64, 784), bias=True, mul_axis=-1, mul_scale=None, with_modswitch=True, ) linear.set_data(torch.randn(64, 784), torch.randn(64)) pipeline = HomomorphicPipeline( hom=SequentialHomOp(linear), input_shape=(784,), )
See also
- HomMatMul - the weight multiply inside
- HomConstAdd - the optional bias step
- HomSquare - quadratic activation between linear layers
- Ciphertext state - Scale - why matmul grows scale fastest