"The decryption failed because the approximation error is too high. Check the parameters" with bootstrapping

I’m using the CKKS cipher and encountered the following error:
The decryption failed because the approximation error is too high. Check the parameters.

I checked out Error “The decryption failed because the approximation error is too high. Check the parameters. ” after second EvalPolyPS() and Adjusting Parameters for “Approximation Error Too High” Error, but my result didn’t change.

I understand this error means that the error polynomial is large.So, e that satisfies <ct, sk> = m + e is too large.

And here’s the code.

#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 = FLEXIBLEAUTOEXT;
    usint dcrtBits               = 50;
    usint firstMod               = 57;

    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 = 7;
    usint depth = levelsAvailableAfterBootstrap + FHECKKSRNS::GetBootstrapDepth(approxBootstrapDepth, levelBudget, secretKeyDist);
    parameters.SetMultiplicativeDepth(depth);

    CryptoContext<DCRTPoly> cryptoContext = GenCryptoContext(parameters);

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

    usint ringDim = cryptoContext->GetRingDimension();
    std::cout << "CKKS scheme is using ring dimension " << ringDim << std::endl << std::endl;

    uint32_t numSlots = ringDim/2;

    cryptoContext->EvalBootstrapSetup(levelBudget, bsgsDim, numSlots);

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

    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, depth - 5, nullptr, numSlots);

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

    //product storage
    Ciphertext<DCRTPoly> product_storage = cryptoContext->Encrypt(keyPair.publicKey, ptxt);

    for (int i=0; i<4; i++){
        //multiply by 1
        std::vector<double> y(numSlots, 1);
        Plaintext ptxt_1 = cryptoContext->MakeCKKSPackedPlaintext(y, 1, 0, nullptr, numSlots);

        product_storage=cryptoContext->EvalAdd(product_storage, cryptoContext->EvalMult(ciph, ptxt_1));
    }

    //I've used up my budget, so I'm going to refresh myself.
    auto ciphertextAfter = cryptoContext->EvalBootstrap(product_storage);

    Plaintext result;
    cryptoContext->Decrypt(keyPair.secretKey, ciphertextAfter, &result);
    std::cout << "Output after bootstrapping \n\t" << result->GetRealPackedValue()[0] << std::endl;

    //correct result
    cryptoContext->Decrypt(keyPair.secretKey, product_storage, &result);
    std::cout << "Output after bootstrapping \n\t" << result->GetRealPackedValue()[0] << std::endl;
}

I also tried setting uint32_t numSlots = ringDim/32 and it worked.

I’m sure that the this is caused by bootstrapping because the ciphertext without bootstrapping was decrypted correctly.

If you know of any causes or improvements, please let me know.

I do not get any error when running this. What OpenFHE version are you using?

Make sure the code is doing what you think it is doing. Currently, product_storage has a extra single level after the loop compared to before the loop. You are multiplying ciph which is not modified in the loop, so what you get is product_storage = product_storage + ciph*1 + ciph* 1 + ....

You had mentioned in a different thread that you are a beginner, so here are some general ideas for debugging: writing a reference cleartext code to check the decrypted results line by line, measuring the number of levels and scale degree after each operation and before decryption, increasing the depth, trying a lower ring dimension initially, increasing the scalingModSize etc.

Thank you for your reply.

I was using the version commit 02a8e9c76c3e2eff53392530199c63e4da53eb65 (HEAD, tag: v1.3.0).

I ran the same code using commit aa8a86e1143f1e47d4354bdd757080e903ba5875 (HEAD, tag: v1.4.0) and got the correct results.

Also, thanks for teaching me basic debugging techniques.