Skip to main content

HomOps Reference

HomSumSlots

Sum across the slots inside a ciphertext.

SsSlots
RunsServer
Changes shapeNo
Changes scaleNo
Levels spent0
RotatesYes - stage plan via stage_sizes
KeysRotation key

What it does

HomSumSlots is the slot-axis reduction: it sums packed slots over a span k using rotation and addition, and does not consume levels. Use it instead of HomAxisSum when the reduction is the ciphertext slot axis (n_axis). HomAxisSum only sums tensor axes and cannot reduce n_axis.
k is the slot span to reduce. When omitted, it defaults to the full slot count. Optional stage_sizes must multiply to k. You will mostly meet this operator indirectly: HomMatMul runs one internally when it multiplies over the n axis, and bootstrapping uses one for its partial sums.
Because it rotates, it takes a stage plan (stage_sizes), like HomExpand and HomRunningSum. Unlike those two, it is rotations and additions only: no masks, no mod-switch, no levels spent, so there is no stages_per_level knob. If stages, offsets, and rounds are new to you, read Slots, rotations & stages first.

Signature

HomSumSlots(
    k=None,
    stage_sizes=None,
)
No set_data.

Parameters

ParameterTypeDefaultDescription
kint | NoneNoneSlot span to reduce. None means the full slot count (n_slots). A smaller k sums within groups of that width rather than across the whole ciphertext.
stage_sizessequenceNoneThe stage plan: one entry per stage, each entry the stage's group size. Unset means (2,) × log₂(k), the smallest-key plan.

Rules that depend on the value

SettingRuleIf you break it
stage_sizesThe entries must multiply out to k: prod(stage_sizes) == k. When k is omitted, that product must equal the full slot count.Compilation fails: Product of stage_sizes must be equal to k.
stage_sizes unsetThe default plan (2,) × log₂(k) only exists when k is a power of two.For any other k, write the plan yourself.

Requirements

  • Rotation key entries. Every rotation offset in the stage plan must be present in the evaluation key. The compiler derives the exact set; you don't list them yourself.
These surface as compilation errors; see Compilation errors.

Shape effect - no

The output shape equals the input shape. The values are redistributed: with default k (full slot count), every slot of a block holds the block's total; with a smaller k, every slot of a group of width k holds that group's total.
pt_shape: (32,)slots: v0, v1, ..., v31
HomSumSlots()
pt_shape: (32,)every slot: v0 + v1 + ... + v31

Scale effect - no

Rotations and additions only; the scale carries through unchanged.

Stages & rotations

The general rules live on Slots, rotations & stages; here is how they apply to HomSumSlots:
QuestionAnswer for HomSumSlots
Where does k come fromConstructor argument. Omit it to reduce the full slot count; pass an explicit span to sum within groups of width k.
Default planstage_sizes unset gives (2,) × log₂(k). Requires k to be a power of two.
Plan constraintprod(stage_sizes) == k
Distinct rotation offsetsΣ (entry − 1) over the plan. Sets the rotation key size.
Roundslen(stage_sizes). Fewer rounds means a faster query.
Levels spent0, always. No mod-switch runs inside, so there is no stages_per_level here.
Since levels are never at stake, the plan is a pure speed-versus-key-size trade:
plans.pyPYTHON
# Default: k = n_slots, log-depth rotation tree
HomSumSlots()                                  # full slot-axis reduction

# Explicit span: k = 8, two stages
HomSumSlots(k=8, stage_sizes=[2, 4])     # prod(stage_sizes) == k

Level budget

Zero. The tree is rotations and additions; no prime is ever dropped, whatever the plan.

Keys

Rotation key. Every offset in the stage plan needs an entry in the rotation key, derived at compile time from the plan and generated for you during key generation. Wider stages mean more offsets and a bigger key. No square key.

Example

Standalone slot sum

Partial sums over column groups: 32 slots as 4 columns of k = 8:
pipeline.pyPYTHON
from lattica_build.operators import HomSumSlots
from lattica_build.base_classes.hom_pipeline import HomomorphicPipeline
from lattica_build.operators.composite.sequential import SequentialHomOp

pipeline = HomomorphicPipeline(
    hom=SequentialHomOp(
        HomSumSlots(k=8, stage_sizes=[2, 4]),   # 2 * 4 = 8 = k
    ),
    input_shape=(32,),
)

Inside HomMatMul

You don't write this one; when HomMatMul multiplies over the n axis, it runs a HomSumSlots with defaults to do its summation. The rotation key entries it needs are derived along with everything else at compile time.

See also