KeySwitch keys reuse & optimization for hybrid key switching

Hi guys,
I’m wondering if the key-switching Keys in OpenFHE are reusable, that is the ciphertext in different levels (BGV and CKKS) can use the same KeySwitch Key.

That feature is viable in SEAL, as far as I know, cause they use “one special prime policy”, that is the special case of dnum = l for the hybrid key-switch. So, when applying key-switching on level-i ciphertext, they just delete part of the key-switch keys (according to the modulus deleted during Modulus Switching).

But for hybrid key-switching, is this technique still applicable? Or we just have to store a key-switch key for multiplication in every level? Is there any reading material for discussion on doing key-switching in different multiplication levels?

Thank you in advance!

Hi @Sijia,

In all OpenFHE examples based on hybrid key switching (default for CKKS and BGV), only one switching key (e.g., for multiplication or a specific rotation) is used for all levels; typically it corresponds to level 0. For a level-i ciphertext, the other RNS limbs (for levels < i) in the evaluation key are ignored during key switching. This is done automatically by OpenFHE.

Thank you so much for replying! Actually, what I really want to know is the mathematics behind Hybrid-Key-switching, and I’ve just found the answer in “Better Bootstrapping for Approximate Homomorphic Encryption”. It seems OpenFHE goes totally with the Key-Switch operation described in that paper, with little modification in the step “2-1. Zero-padding and Split”.

But one thing that still confuses me is the difference between SEAL and OpenFHE. Because we can treat Ke-Switching in SEAL as a special case of Hybrid Key-Switching in OpenFHE with dnum = L.

Key-Switching in SEAL is basically
Sum{DigitDecompose(c2), KeySwitchKey[(Q_hat)(Q_hat_inverse)Ps2]}

While Key-Switching in OpenFHE goes like
Sum{(Q_hat_inverse)DigitDecompose(c2), KeySwitchKey[(Q_hat)Ps2]}

That would introduce a multiplication of (Q_hat_inverse) on c2 every time a KeySwitch is executed. I wonder if it is possible for OpenFHE to move that (Q_hat_inverse) into Key-Switch Keys to save some computation? Or that sort of technique is simply impossible (not right)?

1 Like

Not true. We use the same variant as in SEAL. This variant was actually proposed in my paper :slight_smile: https://eprint.iacr.org/2018/117.pdf (see Section 3.3). See openfhe-development/keyswitch-hybrid.cpp at v1.0.3 · openfheorg/openfhe-development · GitHub

Note that (Q_hat)(Q_hat_inverse) does not need to be computed (also see Section 3.3)

Hybrid key switching and the optimization are also described in Remark B.3 of https://eprint.iacr.org/2021/204.pdf

Thank you for the reply.

That is exactly what I’m talking about. But your code in OpenFHE seems to be the opposite, that is computing

Sum{(Q_hat_inverse)DigitDecompose(c2), KeySwitchKey[(Q_hat)Ps2]}

See openfhe-development/keyswitch-hybrid.cpp at 122f470e0dbf94688051ab852131ccc5d26be934 · openfheorg/openfhe-development · GitHub for Key-Switch Keys generation with multiplication by PartQHatModq

And openfhe-development/keyswitch-hybrid.cpp at 122f470e0dbf94688051ab852131ccc5d26be934 · openfheorg/openfhe-development · GitHub for digit extraction in OpenFHE. It seems that the multiplication by QHatInvModq is executed when every digit is extracted from c2.

Thanks @Sijia Good catch! It looks we missed this optimization in hybrid key switching (back when we implemented it in PALISADE). The BV key switching does include the optimization (I just looked at the code).

I’ve created an issue for this: Implement optimization for hybrid key switching · Issue #373 · openfheorg/openfhe-development · GitHub

Thank you for your response :grinning: :grinning: :grinning:

I looked deeper into this issue and decided to write it down in more detail. In the case of hybrid key switching, our Q_i is decomposed into multiple digits \tilde{Q_j}, where j = 0 \dots d_{num}-1. Each of this digits may have multiple machine-word RNS moduli q_k, where k = 0 \dots i (a subset of this for each digit).

If \tilde{Q_j} has only one machine-word modulus, then we can apply the HPS optimization directly. We can move \left[ \left( \frac{Q_i}{\tilde{Q_j}} \right)^{-1} \right]_{\tilde{Q_j}} \frac{Q_i}{\tilde{Q_j}} to s from the ciphertext digits, i.e., do this product as part of key generation (saving scalar multiplications). Moreover, as we need s_{q_k}, this scalar factor for a given q_k becomes congruent to 1 \bmod q_k. So we do not even need to do the scalar multiplication as part of key generation.

When \tilde{Q_j} contains multiple machine-word RNS moduli, which is what desired in practice (the SEAL mode requires larger keys for larger i, starting with i = 4 or so, and, hence, is rarely used in practice), we can still move the product \left[ \left(\frac{Q_i}{\tilde{Q_j}}\right) ^{-1} \right]_{\tilde{Q_j}} \frac{Q_i}{\tilde{Q_j}} to s, i.e., do it as part of key generation. This scalar product is still congruent to 1 \bmod q_k as the RNS decomposition of 1 \bmod \tilde{Q_j} implies congruency to 1 for all q_k within the digit \tilde{Q_j}.

Just wanted to write it down to simplify the implementation of the optimization in OpenFHE.

1 Like

Please note that the optimization has been merged to the dev branch (see Hybrid Key Switching Optimization by kimandrik · Pull Request #377 · openfheorg/openfhe-development · GitHub for more details). It will get included in v1.0.4 (we will merge it to the main branch then).

v1.0.4 (with the optimization) is now released.