← HomOps Reference
HomConstMul
Multiply an encrypted value by a fixed plaintext (weights, a mask, a per-channel scale).
CmArithmetic
RunsServer
Changes shapeYes - broadcast
Changes scaleYes - × the plaintext's encoding scale
Levels spent1 by default, 0 with with_modswitch=False
RotatesNo
KeysNone
What it does
HomConstMul multiplies a ciphertext element by element with a plaintext tensor you attach via set_data: model weights, a batch-norm scale, an averaging mask, a selector. One operand is encrypted, the other is fixed data, so no key is needed.It is the plaintext counterpart of HomMul (ciphertext × ciphertext). Like every multiply, it grows the scale; unlike
HomMul, it needs no square key. Its with_modswitch default matches HomMul: True.You will often meet it inside composites: HomBatchNorm uses it for the scale step, HomExpand and HomRunningSum use it internally for their stage masks.
Implicit HomConstMul: just write x * pt
When you multiply an encrypted value by a plaintext with the
* operator, the compiler infers a HomConstMul behind the scenes:pipeline.pyPYTHON
y = x * pt # HomConstMul inferred (ciphertext × plaintext) result = x * y # HomMul inferred (ciphertext × ciphertext)
Write
HomConstMul(...) explicitly when you need to set a parameter, or when the plaintext arrives later via set_data.Signature
HomConstMul( dims, # required: plaintext multiplier shape with_modswitch=True, rows_budget=None, pt_scale=None, # defaults to the context's pt_scale )
The plaintext comes in through
set_data:pipeline.pyPYTHON
pipeline.set_data(op_index, scale_vector)Parameters
ParameterTypeDefaultDescription
dimsshaperequiredExpected plaintext multiplier shape, used for the broadcast check at compile time. The tensor attached with set_data must be compatible with this shape.with_modswitchboolTrueRun a mod-switch after the multiply: rescale the result and spend one level. The default is True, matching HomMul.rows_budgetsequenceNoneRestricts which levels the mod-switch may spend, by absolute row index. Only relevant when with_modswitch=True. See rows_budget.pt_scaleintcontext pt_scaleThe encoding scale for the plaintext. The output scale is the input scale times this value. Lower it to keep scale growth small (at the cost of constant precision); leave it unset for the standard plan.Rules that depend on the value
SettingRuleIf you break it
dims / data.shapeMust broadcast with the ciphertext shape. When the plaintext is broadcast on the n axis, it must be broadcast exactly there (e.g. (C, 1, 1) against (C, H, W)).Compilation fails.with_modswitchAt least one level must still be droppable when the mod-switch runs.Compilation fails.Requirements
set_datacalled. The plaintext must be attached before the pipeline compiles.- Broadcastable shapes. As above; the efficient packed path runs when the plaintext spans the n axis at full length, the broadcast fallback otherwise.
These surface as compilation errors; see Compilation errors.
Shape effect - yes
Rule: the output shape is the broadcast of the ciphertext shape and the plaintext shape.
The n axis is marked in bold:
CiphertextPlaintextOutput
(128,)(128,)(128,) - packed along the n axis
(C, H, W)(C, 1, 1)(C, H, W) - per-channel scale
(1, K², P)(1, K²)(1, K², P) - pooling mask
Shape and the n axis are covered on Concepts.
Scale effect - yes
Rule: the output scale is the input scale times the plaintext's encoding scale, the
pt_scale parameter:StageEffect on scale
After the multiplyscale_out = scale_in × pt_scale (parameter; context default when unset)
with_modswitch=TrueThen divided by the dropped prime - spending one level.
So a
HomConstMul at the default pt_scale grows the scale exactly like one ciphertext multiply, and pt_scale=1 (encoding integers) leaves it unchanged. How scale moves through a pipeline is on Ciphertext state - Scale.Level budget
ConfigurationLevels spent
with_modswitch=True (default)1 - drops one prime from the chain
with_modswitch=False0 - scale keeps climbing until you rescale
rows_budget limits which level the optional drop may touch. How primes are organized in the chain is explained on Level budget - The chain.Keys
None. A ciphertext-plaintext multiply produces no oversized intermediate, so no square key; nothing rotates.
Example
Explicit HomConstMul with rescale
Scale a 128-slot vector and pay the growth down in the same op:
pipeline.pyPYTHON
from lattica_build.operators import HomConstMul from lattica_build.base_classes.hom_pipeline import HomomorphicPipeline from lattica_build.operators.composite.sequential import SequentialHomOp pipeline = HomomorphicPipeline( hom=SequentialHomOp( HomConstMul(dims=(128,), with_modswitch=True), ), input_shape=(128,), ) pipeline.set_data(0, scale_vector) # shape (128,)
Inside a search pipeline
The database multiply in exact search is a
HomConstMul against the expanded query:pipeline.pyPYTHON
HomExpand(k=64, k_axis=1, stage_sizes=[4, 4, 4], stages_per_level=3), HomConstMul(dims=db.shape, with_modswitch=True), HomAxisSum(dim=2, keep_dim=True),
See also
- HomMul - multiply two ciphertexts, needs the square key
- HomConstAdd - add a plaintext, the op that leaves scale alone
- HomBatchNorm - composite built from ConstMul + ConstAdd
- Level budget - The chain - what the optional mod-switch drops