← HomOps Reference
HomSub
Subtract one encrypted value from another.
SbArithmetic
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
HomSub subtracts two ciphertexts element by element: a − b. Both operands are encrypted values; there are no plaintext weights and no key involved. It is the subtraction counterpart of HomAdd, and shares the same alignment rules for level and scale.Reach for it when you need a difference of two encrypted streams: residuals, pairwise deltas, or building blocks inside compare and min/max composites. It is a two-input operator: it consumes two ciphertexts from earlier in the computation.
Signature
HomSub()No parameters.
HomSub takes no weights, so there is no set_data call - both operands are ciphertexts.Parameters
HomSub has no configurable parameters. Writing HomAdd(is_sub=True) is equivalent.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 subtract; 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
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 subtract, 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 subtract itself is free. Any cost comes from alignment, and only when the two branches arrive uneven. How primes are organized in the chain is explained on Level budget - The chain.
Keys
None. Subtraction needs no relinearization and no rotations, so
HomSub adds nothing to the evaluation key.Example
Subtract a squared copy from the original stream in a two-input graph:
pipeline.pyPYTHON
from lattica_build.operators import HomSub, HomSquare # Two-input graph: subtract one encrypted stream from another y = HomSquare()(x) diff = HomSub()(x, y) # x - y
For the subtract to compile, the two branches must land at compatible levels and scales - the same alignment rules as HomAdd.
See also
- HomAdd - the addition counterpart, same alignment rules
- HomConstAdd - add a fixed plaintext, weights via
set_data - Level budget - The chain - what alignment drops and why
- Ciphertext state - how shape, scale, and levels travel through a pipeline