Mod reduction error

Hello, fellow developers.

I’m using CKKS with ML, and I have a question about mod reduction.

I’m using the ModReduce function to speed things up, but I encountered an error when using this function.

Below is a simplified reproduction of the error.

#define PROFILE

#include "openfhe.h"

using namespace lbcrypto;

int main(int argc, char* argv[]) {
    CCParams<CryptoContextCKKSRNS> parameters;

    SecretKeyDist secretKeyDist = SPARSE_TERNARY;
    parameters.SetSecretKeyDist(secretKeyDist);

    parameters.SetSecurityLevel(HEStd_128_classic);
    parameters.SetRingDim(1 << 16);

    parameters.SetNumLargeDigits(3);
    parameters.SetKeySwitchTechnique(HYBRID);

    ScalingTechnique rescaleTech = FIXEDMANUAL;
    usint dcrtBits               = 50;
    usint firstMod               = 59;

    parameters.SetScalingModSize(dcrtBits);
    parameters.SetScalingTechnique(rescaleTech);
    parameters.SetFirstModSize(firstMod);

    std::vector<uint32_t> levelBudget = {3, 3};

    std::vector<uint32_t> bsgsDim = {0, 0};

    uint32_t approxBootstrapDepth = 9;

    uint32_t levelsAvailableAfterBootstrap = 8;
    usint depth = levelsAvailableAfterBootstrap + FHECKKSRNS::GetBootstrapDepth(approxBootstrapDepth, levelBudget, secretKeyDist);
    parameters.SetMultiplicativeDepth(depth);

    std::cout << "depth=" << depth << std::endl;

    CryptoContext<DCRTPoly> cryptoContext = GenCryptoContext(parameters);

    cryptoContext->Enable(PKE);
    cryptoContext->Enable(KEYSWITCH);
    cryptoContext->Enable(LEVELEDSHE);
    cryptoContext->Enable(ADVANCEDSHE);
    cryptoContext->Enable(FHE);

    usint ringDim = cryptoContext->GetRingDimension();

    uint32_t numSlots = ringDim/2;

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

    std::vector<double> x;
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_real_distribution<> dis(0.0, 1.0);
    for (size_t i = 0; i < numSlots; i++) {
        x.push_back(dis(gen));
    }

    Plaintext ptxt = cryptoContext->MakeCKKSPackedPlaintext(x, 1, 0, nullptr, numSlots);

    Ciphertext<DCRTPoly> ciph = cryptoContext->Encrypt(keyPair.publicKey, ptxt);

    std::cout << "Level of ciph:" << ciph->GetLevel() << std::endl;
    for (int i=0; i<(int)depth; i++){
        ciph=cryptoContext->ModReduce(ciph);
        std::cout << "Level of Mod reduced ciph:" << ciph->GetLevel() << std::endl;
    }
}

result&error:

depth=23
Level of ciph:0
Level of Mod reduced ciph:1
terminate called after throwing an instance of 'lbcrypto::OpenFHEException'
  what():  /openfhe-development/src/pke/lib/ciphertext-impl.cpp:l.50:SetLevel(): The current multiplicative depth [23] is insufficient; increase it.

My guess is that since I’ve defined a level 0 ciphertext, I should be able to reduce the mod depth times.

If you have any advice, please let me know.

In CKKS, ModReduce is the same as Rescale. it is purpose is to scale down the message along with the noise (typically after multiplication). Initially, the ciphertext has a scale of \Delta. During rescaling, it gets scaled down by \Delta. If you try to scale down again, you will get an equivalent scale of 1/\Delta, which means the message will be fully erased. ModReduce/Rescale in CKKS should only be called after multiplication, for example, to scale down from \Delta^2 to \Delta. Please see the intro of 2020/1118 to get familiar with how CKKS rescaling is done in OpenFHE.

Thank you for your reply.

So ModReduce is a function that reduces the scale value.

I thought it was a function that reduces the number of modulo q values ​​in ciphertext in RNS systems.

Is there a function in OpenFHE that reduces the number of modulo q values? (Similar to the mod_switch_to_inplace() function in the SEAL library.)

If there is, I would appreciate it if you could tell me how to use it.

Rescaling in CKKS is the same as modulus switching in BGV and BFV. Both remove the number of modulo q values. The main difference is that in the case of BGV and BFV, the scale of the encrypted message is not reduced (only the noise is reduced) because of the special encoding used for the message. In the case of CKKS, both message and noise are reduced because the message and noise are encoded together (they are often inseparable in CKKS).

Thank you for your reply.

So, is there a function that will lower the level without changing the scale value?

I checked the API and it looks like the LevelReduce function is the one that will lower the level only. Is that correct?

Also, what should I pass to eval, the argument to the LevelReduce function?