# Precision problems with approximations under CKKS

**URL:** https://openfhe.discourse.group/t/precision-problems-with-approximations-under-ckks/1669
**Category:** Library Questions
**Tags:** openfhe-help, questions
**Created:** [October 27, 2024, 3:48pm UTC](https://openfhe.discourse.group/t/precision-problems-with-approximations-under-ckks/1669 "2024-10-27T15:48:45Z")
**Posts on this page:** 4
**Page:** 1

<div class="post-metadata">

### Author: ![nbruechmann](https://avatars.discourse-cdn.com/v4/letter/n/c37758/32.png) [@nbruechmann](https://openfhe.discourse.group/u/nbruechmann)
#### Post date: [October 27, 2024, 3:48pm UTC](https://openfhe.discourse.group/t/precision-problems-with-approximations-under-ckks/1669/1 "2024-10-27T15:48:45Z")

</div>

Hi there,

I’m implementing an algorithm in CKKS. The algorithm involves the sequential evaluation of several non-polynomial functions.  
For this reason I use EvalChebyshevFunction along with bootstrapping every time I run out of usable levels.  
However, I encountered a situation in which - even by having enough available levels to perform the Chebyshev evaluation - the result is completely messed up.  
I am attaching a minimal script to reproduce my problem. I think it is a precision problem.  
I know I can fix the code below by using more dcrtBits, a lower approximation degree, or by using iterative bootstrapping. Unfortunately, despite all these adjustments, I have a similar problem in my algorithm.

Can anyone hint me in the right direction?

```
#include "openfhe.h"
using namespace lbcrypto;

usint depth;
void log(lbcrypto::KeyPair<lbcrypto::DCRTPoly> keyPair, const std::string& s, 
const lbcrypto::Ciphertext<lbcrypto::DCRTPoly> cipher, const size_t numSlots) {
  Plaintext result;
  std::cerr<<s<<std::endl<<"remaining level:"<<(depth - cipher->GetLevel() - (cipher->GetNoiseScaleDeg()-1))<<std::endl;
  cipher->GetCryptoContext()->Decrypt(keyPair.secretKey, cipher, &result);
  result->SetLength(numSlots);
  std::cerr<< result<<std::endl<< std::endl;
}

int main() {
  std::vector<double> values = { -0.5, -0.5, -0.5, -0.5, 0.0, 0.0, 0.0, 0.0, 0.1, 0.1, 0.1, 0.1, 0.5, 0.5, 0.5, 0.5 };
  usint numSlots = values.size();

  CCParams<CryptoContextCKKSRNS> parameters;
  SecretKeyDist secretKeyDist = UNIFORM_TERNARY;
  parameters.SetSecretKeyDist(secretKeyDist);

  parameters.SetSecurityLevel(HEStd_NotSet);
  parameters.SetRingDim(8192);

  ScalingTechnique rescaleTech = FLEXIBLEAUTO;
  usint dcrtBits = 52;
  usint firstMod = 60;

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

  parameters.SetBatchSize(numSlots);

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

  uint32_t levelsAvailableAfterBootstrap = 37;
  depth = levelsAvailableAfterBootstrap + FHECKKSRNS::GetBootstrapDepth(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<< depth << std::endl;

  const std::vector<usint> bsgsDim = {0, 0};
  cryptoContext->EvalBootstrapSetup(levelBudget, bsgsDim, numSlots);

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

  Plaintext plaintext = cryptoContext->MakeCKKSPackedPlaintext(values,1,29,nullptr, numSlots);
  auto cipher = cryptoContext->Encrypt(keyPair.publicKey, plaintext);
  log(keyPair,"=======Input======", cipher, numSlots);

  cipher = cryptoContext->EvalBootstrap(cipher);
  log(keyPair,"=======After Bootstrap======", cipher, numSlots);

  auto fn = [](double x) -> double { 
          if (x > 0.00001) return .5;
          else return 0;
  };
  
  cipher = cryptoContext->EvalChebyshevFunction(
      fn,cipher,-1,1,2031
  );
  log(keyPair,"=======First EvalChebyshevFunction======", cipher, numSlots);

  cipher = cryptoContext->EvalChebyshevFunction(
      fn,cipher,-1,1,2031
  );
  log(keyPair,"=======Second EvalChebyshevFunction======", cipher, numSlots);
  return 0;
}

```

This is my output:

```
CKKS scheme is using ring dimension 8192
59
=======Input======
remaining level:30
logstd= 7.57681 > 47 (stddev) 190.919
(-0.5, -0.5, -0.5, -0.5, -2.56323e-13, 1.0586e-13, -6.29496e-14, 2.42223e-13, 0.1, 0.1, 0.1, 0.1, 0.5, 0.5, 0.5, 0.5, ... ); Estimated precision: 41 bits

=======After Bootstrap======
remaining level:37
logstd= 34.6272 > 47 (stddev) 2.65358e+10
(-0.500091, -0.500057, -0.500078, -0.50001, -1.73649e-05, 2.52282e-05, -8.12387e-05, -1.41845e-05, 0.100004, 0.099962, 0.0999808, 0.0999734, 0.500043, 0.500054, 0.500038, 0.499977, ... ); Estimated precision: 14 bits

=======First EvalChebyshevFunction======
remaining level:26
logstd= 41.8902 > 47 (stddev) 4.07581e+12
(0.000188768, 0.00399775, 0.00470639, -0.0069619, 0.240097, 0.262608, 0.243547, 0.249906, 0.512721, 0.502465, 0.503065, 0.504301, 0.49773, 0.507258, 0.503919, 0.501146, ... ); Estimated precision: 7 bits

=======Second EvalChebyshevFunction======
remaining level:15
logstd= 152.012 > 47 (stddev) 5.75654e+45
(2.186e+30, -2.1376e+30, 3.3977e+30, -7.37699e+30, 1.94446e+31, 7.03397e+30, 1.33626e+31, -8.6772e+30, 1.60981e+31, 5.35571e+30, 1.27029e+30, 8.23277e+30, 3.66217e+30, -1.24086e+31, -6.18034e+30, 3.32868e+30, ... ); Estimated precision: -103 bits

```

---

<div class="post-metadata">

### Author: ![ypolyakov](https://yyz1.discourse-cdn.com/flex031/user_avatar/openfhe.discourse.group/ypolyakov/32/47_2.png) [@ypolyakov](https://openfhe.discourse.group/u/ypolyakov)
#### Post date: [November 11, 2024, 11:07pm UTC](https://openfhe.discourse.group/t/precision-problems-with-approximations-under-ckks/1669/2 "2024-11-11T23:07:00Z")

</div>

One potential reason for the behavior you observe is that at least one value after first Chebyshev evaluation goes outside the range of [-1,1]. Here is why it can happen. As far as I can see, you have a number of values in the input to first Chebyshev evaluation that are very close to 0, which are bad inputs for a Chebyshev approximation of the sign (discontinuous) function. When values that are very close to 0 are supplied, the result can be very inaccurate (easily going outside the range of [-1,1]). In summary, one should not use a polynomial approximation of sign for values that are approximately zero, i.e., in the discontinuous region.

---

<div class="post-metadata">

### Author: ![nbruechmann](https://avatars.discourse-cdn.com/v4/letter/n/c37758/32.png) [@nbruechmann](https://openfhe.discourse.group/u/nbruechmann)
#### Post date: [November 14, 2024, 4:53pm UTC](https://openfhe.discourse.group/t/precision-problems-with-approximations-under-ckks/1669/3 "2024-11-14T16:53:02Z")

</div>

But if I remove the bootstrapping at the beginning, the code works fine… Is it possible that this problem is more related to precision?  
Thank you for your response!

---

<div class="post-metadata">

### Author: ![ypolyakov](https://yyz1.discourse-cdn.com/flex031/user_avatar/openfhe.discourse.group/ypolyakov/32/47_2.png) [@ypolyakov](https://openfhe.discourse.group/u/ypolyakov)
#### Post date: [November 15, 2024, 9:58pm UTC](https://openfhe.discourse.group/t/precision-problems-with-approximations-under-ckks/1669/4 "2024-11-15T21:58:14Z")

</div>

It could be both. For instance, if you try to run Chebyshev interpolation few more times, you may run into the same issue (even w/o bootstrapping). In general, one cannot expect good accuracy from Chebyshev approximation in the proximity of singular points.
