← HomOps Reference
HomRingSwitch
Lift a query encrypted in a small ring into the large ring the rest of the pipeline uses.
RwFHE
RunsServer
Changes shapeYes - packing axis becomes the full ring slots (k copies)
Changes scaleNo - inner bootstrap targets the input scale
Levels spentRefresh - restarts from a freshly bootstrapped chain
RotatesYes - inner bootstrap rotations
KeysSquare + rotation + ring-switch key
What it does
Ring switching lets a client encrypt its query in a small ring and have the server lift that ciphertext into the large ring the rest of the computation needs.
The cost of an FHE ciphertext scales with the ring dimension. A query encrypted directly in the computation's ring is large to produce and upload; encrypted in a sub-ring it is dramatically smaller. Ring switching moves the cost of getting into the large ring from the client to the server.
The operator takes a ciphertext in a sub-ring of dimension
n' = n / k (holding n'/2 slots) and returns a ciphertext in the full ring of dimension n. The underlying plaintext becomes k consecutive copies of the input plaintext, filling the full ring's slots. Downstream operations see those copies; the plaintext is replicated, not padded.Concrete example. In the encrypted-search demo, the client encrypts its query in a ring of dimension 2¹¹ and the server searches in a ring of dimension 2¹⁴. The uploaded query ciphertext is 32 KB. Ciphertext size is
2 × n × (number of moduli) × 8 bytes, so the same query encrypted directly in the search ring would be well over an order of magnitude larger.How it works
Three stages, applied server-side:
- Embed. The sub-ring ciphertext's coefficients are expanded into the full ring, replicating the plaintext
ktimes across the slots. - Key switch. The result is re-keyed from the client's sub-ring secret key onto the full-ring secret key, using a dedicated ring switch key supplied with the client's evaluation keys.
- Bootstrap. The ciphertext arrives at the smallest modulus level, so it is bootstrapped to restore a usable modulus chain.
Because stage 3 is a full bootstrap, treat the operator as costing roughly one bootstrap. Its output has the level budget of a freshly bootstrapped ciphertext, so the rest of the pipeline starts from a full chain. The inner bootstrap targets the input scale, so
pt_scale is unchanged.HomParams for a bootstrapping pipeline
HomRingSwitch runs an inner bootstrap, so the compiler enables bootstrapping automatically. You do not set a bootstrapping flag; it is not a constructor argument of this operator.params.pyPYTHON · BOOT
from lattica_build.params import HomParams LOG_N = 14 # search / computation ring LOG_N_SUBRING = 11 # client encryption ring hom_params = HomParams( n=2 ** LOG_N, full_q_list_precision=((60,), (60,), (60,), (60,)), pt_scale=2 ** 30, num_special_primes=6, )
Variant, init-row, and special-prime settings are the same as for a standalone Bootstrap. The deployer derives the sub-ring encryption parameters automatically from this operator; there is no second
HomParams set to configure.Signature
HomRingSwitch( log_n_subring, # required: log2 of the client encryption ring log_n_boot_subring=None, # optional; defaults to log_n_subring )
Both arguments may be passed; only
log_n_subring is required. log_n_boot_subring defaults to log_n_subring when omitted. No set_data.Parameters
ParameterTypeDefaultDescription
log_n_subringintrequiredLog₂ of the ring the client encrypts in. The input ciphertext holds 2^(log_n_subring - 1) slots. This is not Bootstrap's log_n_subring (sparse packing inside a refresh).log_n_boot_subringint | NoneNoneLog₂ of the ring the cleartext is packed in — the period of the data within the plaintext. The input cleartext has 2^(log_n_boot_subring - 1) slots. When omitted, defaults to log_n_subring. Usually ≤ log_n_subring, since a small cleartext can be sparsely packed into a larger plaintext. Passed through to the inner bootstrap as its sparse-packing log_n_subring.Typical call:
HomRingSwitch(log_n_subring=11), or HomRingSwitch(log_n_subring=11, log_n_boot_subring=7) when the packed cleartext uses a smaller ring.Requirements
- First in the pipeline. Must be the first operation. Any operation before it would have to run in the sub-ring, and a pipeline carries a single context.
- HomParams. The inner bootstrap causes the compiler to enable bootstrapping automatically; do not set a
bootstrappingflag. It is not a constructor argument ofHomRingSwitch. See HomParams for a bootstrapping pipeline. - Extra key material. The client generates a second (sub-ring) secret key and a ring switch key alongside its normal evaluation keys. The operator also requires the multiplication (square) key and the rotation keys the inner bootstrap needs.
- Replication, not padding. Downstream operations see
kcopies of the input across the full ring's slots.
Shape effect - yes
The packing axis becomes the full ring's slot count (
internal_n of the pipeline context). The plaintext is repeated k = n / 2^log_n_subring times along that axis.StageSlots on the packing axis
Input (sub-ring)2log_n_subring - 1
After HomRingSwitchn / 2 (full ring), k copies of the input
Scale effect - no
Rule: the inner bootstrap targets the input scale, so
pt_scale carries over unchanged.Level budget
The switch bootstraps the sub-ring ciphertext, so level consumption restarts from the refreshed chain. Treat the operator as costing roughly one bootstrap. Usable depth after it is the depth you declared in
full_q_list_precision, the same as after a standalone Bootstrap.Keys
Sub-ring secret key. A second secret key, generated on the client, for the ring the query is encrypted in.
Ring switch key. Re-keys the embedded ciphertext from the sub-ring secret onto the full-ring secret. Generated with the evaluation keys.
Square key. Needed for multiply stages inside the inner bootstrap.
Rotation key. Needed for the inner bootstrap's slot↔coefficient transforms (and sparse-packing rotations driven by
log_n_boot_subring).Example
Encrypt the query in a sub-ring, lift on the server, then continue the search pipeline.
HomRingSwitch is first:pipeline.pyPYTHON
from lattica_build.params import HomParams from lattica_build.base_classes.hom_pipeline import HomomorphicPipeline from lattica_build.operators.composite.sequential import SequentialHomOp from lattica_build.operators import HomRingSwitch, HomExpand LOG_N = 14 LOG_N_SUBRING = 11 hom_params = HomParams( n=2 ** LOG_N, full_q_list_precision=((60,), (60,), (60,), (60,)), pt_scale=2 ** 30, num_special_primes=6, ) pipeline = HomomorphicPipeline( hom=SequentialHomOp( HomRingSwitch(log_n_subring=11, log_n_boot_subring=7), # must be first HomExpand(k=64, k_axis=1), ), input_shape=(2 ** (LOG_N_SUBRING - 1),), )
See also
- Bootstrap - the inner refresh; its
log_n_subringis sparse packing, not the client encryption ring - Choosing parameters - HomParams; bootstrapping is inferred, no second parameter set for ring switching
- Evaluation key - square, rotation, and extra ring-switch key material
- Repeat · HomExpand - the exact-search opening that follows the lift