Does Homomorphic Constant Multiplication Require Additional Circuit Depth?

Hello,

I have a question about the minimum required level of ciphertexts to evaluate a homomorphic circuit. Specifically, I am wondering if constant multiplication in homomorphic encryption consumes a level.

When performing homomorphic multiplication between ciphertexts, it is understood that this requires a level. However, does constant multiplication also always require rescaling after the operation?

When setting the level of ciphertext to evaluate a homomorphic circuit, should I take constant multiplication into account, or can I skip rescaling in some cases?

(Or are the cases where I just multiply a constant to a ciphertext and the case where I multiply a packed plaintext to a ciphertext different?)

Thank you.

  • In below code, why the output is as the screenshot instead of decryption failure or the vector with all entries 1?
    parameters.SetSecurityLevel(HEStd_NotSet);
    parameters.SetRingDim(1 << 12);

    
    usint scalingModSize = 50;
    uint32_t multDepth = 0;

    parameters.SetScalingModSize(scalingModSize);
    parameters.SetMultiplicativeDepth(multDepth);
    CryptoContext<DCRTPoly> cc = GenCryptoContext(parameters);
    cc->Enable(PKE);
    cc->Enable(KEYSWITCH);
    cc->Enable(LEVELEDSHE);
    cc->Enable(ADVANCEDSHE);
    cc->Enable(FHE);

    auto keyPair = cc->KeyGen();
    cc->EvalMultKeyGen(keyPair.secretKey);

    std::vector<double> input;     
    for(int i=0; i <16; i++){
        input.push_back(0.5);
    }   
    Plaintext plaintext = cc->MakeCKKSPackedPlaintext(input);
    auto ciphertext  = cc->Encrypt(keyPair.publicKey, plaintext);

    std::cout << "before eval ciph level: " << ciphertext->GetLevel() << std::endl;

    double c = 2.0;
    auto cmult = cc->EvalMult(ciphertext, c);
    std::cout << "after constant multiplication level: " << cmult->GetLevel() << std::endl;
    Plaintext plaintextDec;
    cc->Decrypt(keyPair.secretKey, cmult, &plaintextDec);
    plaintextDec->SetLength(16);
    std::vector<double> finalResult = plaintextDec->GetRealPackedValue();
    std::cout << "output: " << finalResult << std::endl << std::endl;

Result was:

before eval ciph level: 0
after constant multiplication level: 1
output: [ -8.67362e-19 -8.67362e-19 -8.67362e-19 -8.67362e-19 -8.67362e-19 -8.67362e-19 -8.67362e-19 -8.67362e-19 -8.67362e-19 -8.67362e-19 -8.67362e-19 -8.67362e-19 -8.67362e-19 -8.67362e-19 -8.67362e-19 -8.67362e-19 ]

Yes. A constant value, under the hood, is still encoded as a plaintext, thus multiplied by a scale \Delta.

I guess this happens because you set a multiplicative depth equal to 0? Try to use (at least) 1.

@narger is generally correct, but there is also an undocumented capability (MultByInteger or MultByIntegerInPlace) to multiply by an integer (assumes a small integer) that does not require a level (it still scales the noise though by its value). This capability is used internally in CKKS bootstrapping.

To call it in you code, you would need to write

auto cmult = cc->GetScheme()->MultByInteger(ciphertext, c);

Note that c in this case should be uint64_t rather than double. In other words, it only works when multiplying by an integer.

3 Likes

Did not know that, thank you! So this could be used to perform maskings by 0? Do you have any reference for that capability?

Thank you very much

1 Like

Unfortunately, this would not work for masking as in that case you have a vector of numbers in {0,1}, which after inverse FFT become real numbers (so they need to be scaled and rounded, just like a normal CKKS plaintext). This trick only works for multiplying by an integer scalar, e.g., 2.

2 Likes

Thanks! This makes sense. This explains only non-scalar multiplication in homomorphic polynomial evaluation (modular reduction) consumes levels.

1 Like

Hello,

Iā€™m hoping to clarify something regarding multiplication in OpenFHE. From what I understand, performing a multiplication by a double or plaintext consumes one multiplication level, and each multiplication requires a rescaling afterward.

I have a couple of questions related to this:

  1. Is the need to rescale after scalar and plaintext multiplications a theoretical requirement, or is it an implementation constraint in OpenFHE?
  2. If I work in FIXEDMANUAL mode and choose not to rescale after scalar or plaintext multiplications, will the results still be accurate? Or could this lead to incorrect results or potential overflow?

Thank you in advance for your help!

Best regards,
Arseniy

cc @sloede

Please create a new topic for your question as the prior question was answered and marked as a solution.

1 Like