Skip to main content

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.
biasboolTrueIf True, append HomConstAdd after MatMul.
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_data
ParameterTypeDefaultDescription
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. dims must be (out_features, in_features).
  • set_data called. 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