← HomOps Reference
HomAdd
Add two encrypted values together.
AdArithmetic
RunsServer
Changes shapeYes - broadcast
Changes scaleNo - scales must match, then stay
Levels spent0 - alignment may cost the higher input one
RotatesNo
KeysNone
What it does
HomAdd adds two ciphertexts element by element. Both operands are encrypted values; there are no plaintext weights and no key involved. It is the cheapest way to combine two encrypted streams: merging parallel branches, residual-style connections, updating an accumulator.Pass
is_sub=True to subtract instead (a − b). HomSub is the same operation: HomSub() is equivalent to HomAdd(is_sub=True).Two related operators cover the plaintext case: to add a fixed constant or bias to a single encrypted stream, HomConstAdd adds a plaintext and takes the weights via
set_data.Implicit HomAdd: just write x + y
You don't have to spell out
HomAdd yourself. When you add two encrypted values with the + operator, the compiler infers a HomAdd behind the scenes:pipeline.pyPYTHON
y = x + pt # HomConstAdd inferred (ciphertext + plaintext) result = x + y # HomAdd inferred (ciphertext + ciphertext)
Signature
HomAdd(is_sub=False)
HomAdd takes no weights, so there is no set_data call - both operands are ciphertexts from earlier in the computation. The only constructor flag is is_sub.Parameters
ParameterTypeDefaultDescription
is_subboolFalseIf True, perform element-wise subtraction (modsub) instead of addition. Equivalent to writing HomSub.Requirements
- Same n axis. Both inputs must share the same n axis to broadcast.
- Compatible levels. The two inputs must sit on compatible modulus chains: one's active levels a subset of the other's. When they differ, the higher input is automatically brought down to match before the add; you don't call this. Chains that can't be reconciled fail at compile time.
- Compatible scales. After level alignment, the two scales must match. If they differ, the input with more levels remaining is rescaled to match the other, spending one of its levels. If the scales differ and neither input has a level to spend on the fix, compilation fails.
These surface as compilation errors; see Compilation errors.
Shape effect - yes
Rule: the two inputs broadcast against each other the way array shapes do, on every axis except the n axis, which must already match. The output shape is the broadcast of the two input shapes.
The n axis is marked in bold in each shape below:
Input AInput BOutput
(128,)(128,)(128,) - element-wise
(4, 8)(4, 1)(4, 8) - B broadcast across the second axis
(C, H, W)(1, H, W)(C, H, W) - B broadcast across channels
The two streams merge at the add:
A
(4, 8)@scale SB
(4, 1)@scale Sout
(4, 8)@scale Sbroadcast shapes, matched scales
Shape and the n axis are covered on Concepts.
Scale effect - no
StageEffect on scale
Inputs at the same scaleUnchanged:
scale_out = scale_inInputs at different scalesThe input with more levels left is adjusted down to the other's scale before the add, spending one of its levels. The output carries the matched scale.
How scale moves through a pipeline is on Ciphertext state - Scale.
Level budget
ConfigurationLevels spent
Inputs at matching level and scale0
Inputs at different levels, same scale0 from the chain's point of view: the higher input drops primes to match the lower one
Inputs at different scales1 from the higher input, spent on the automatic scale fix
The add itself is free. Any cost comes from alignment, and only when the two branches arrive uneven; the way to avoid it is to plan both branches to land at the same level and scale. How primes are organized in the chain is explained on Level budget - The chain.
Keys
None. Addition needs no relinearization and no rotations, so
HomAdd adds nothing to the evaluation key.Example
Combine a transformed value with a shifted copy of itself:
result = x + (x * x). With operator inference you never write HomAdd at all:pipeline.pyPYTHON
# x - encrypted input y = x * x # HomSquare inferred, with_modswitch=True result = x + y # HomAdd inferred
For the add to compile, the two branches must land at compatible levels and scales. Here
y sits one level below x after its mod-switch, so x (the higher input) is automatically brought down one level to match; the add itself spends nothing.A residual-style merge of two parallel branches:
Input
main branch: multiply, rescale
shortcut branch
HomAdd
Plan both branches so they exit at the same scale: a branch that multiplied and one that didn't will arrive uneven, and the alignment will bill the richer branch.
See also
- HomSub - subtract two ciphertexts (same alignment rules)
- HomConstAdd - add a fixed plaintext, weights via
set_data - HomMul - multiply two ciphertexts, the operation that does grow scale
- Level budget - The chain - what alignment drops and why
- Ciphertext state - how shape, scale, and levels travel through a pipeline