I have a problem using the Chebyshev evaluation.
I’ve attached a minimal example.
Input2 contains my data and I need to perform a square root operation on it.
With this data I always gettig a decryption error (approximation error too high).
I tried several things like increasing the polyDegree and multiplicative depth according to FUNCTION_EVALUATION.MD.
The range of values is pretty high, therefore I divided the input by 1E8.
With this scaling the decryption works somehow but the results are not correct.
/*
Example of evaluating arbitrary smooth functions with the Chebyshev approximation using CKKS.
*/
#include “openfhe.h”
using namespace lbcrypto;
void EvalFunctionExample();
int main(int argc, char* argv ) {
EvalFunctionExample();
return 0;
}
void EvalFunctionExample() {
std::cout << “--------------------------------- EVAL SQUARE ROOT FUNCTION ---------------------------------”
<< std::endl;
CCParams parameters;
// We set a smaller ring dimension to improve performance for this example.
// In production environments, the security level should be set to
// HEStd_128_classic, HEStd_192_classic, or HEStd_256_classic for 128-bit, 192-bit,
// or 256-bit security, respectively.
parameters.SetSecurityLevel(HEStd_NotSet);
parameters.SetRingDim(1 << 10);
#if NATIVEINT == 128
usint scalingModSize = 78;
usint firstModSize = 89;
#else
usint scalingModSize = 50;
usint firstModSize = 60;
#endif
parameters.SetScalingModSize(scalingModSize);
parameters.SetFirstModSize(firstModSize);
// Choosing a higher degree yields better precision, but a longer runtime.
uint32_t polyDegree = 100;
// The multiplicative depth depends on the polynomial degree.
// See the FUNCTION_EVALUATION.md file for a table mapping polynomial degrees to multiplicative depths.
uint32_t multDepth = 10;
parameters.SetMultiplicativeDepth(multDepth);
CryptoContext<DCRTPoly> cc = GenCryptoContext(parameters);
cc->Enable(PKE);
cc->Enable(KEYSWITCH);
cc->Enable(LEVELEDSHE);
// We need to enable Advanced SHE to use the Chebyshev approximation.
cc->Enable(ADVANCEDSHE);
auto keyPair = cc->KeyGen();
// We need to generate mult keys to run Chebyshev approximations.
cc->EvalMultKeyGen(keyPair.secretKey);
std::vector<std::complex<double>> input{1, 2, 3, 4, 5, 6, 7, 8, 9};
std::vector<std::complex<double>> input2{4438.1700, 166.5460, 78721900.0000, 43896800000.0000, 171859000.0000, 31678300.0000, 3647310.0000, 945615.0000};
size_t encodedLength = input.size();
Plaintext plaintext = cc->MakeCKKSPackedPlaintext(input);
auto ciphertext = cc->Encrypt(keyPair.publicKey, plaintext);
Plaintext plaintext2 = cc->MakeCKKSPackedPlaintext(input2);
auto ciphertext2 = cc->Encrypt(keyPair.publicKey, plaintext2);
double lowerBound = 0;
double upperBound = 10;
double lowerBound2 = 0;
double upperBound2 = 6;
// We can input any lambda function, which inputs a double and returns a double.
auto result = cc->EvalChebyshevFunction([](double x) -> double { return std::sqrt(x); }, ciphertext, lowerBound,
upperBound, polyDegree);
auto result2 = cc->EvalChebyshevFunction([](double x) -> double { return std::sqrt(x); }, ciphertext2, lowerBound2,
upperBound2, polyDegree);
Plaintext plaintextDec, plaintextDec2;
cc->Decrypt(keyPair.secretKey, result, &plaintextDec);
plaintextDec->SetLength(encodedLength);
cc->Decrypt(keyPair.secretKey, result2, &plaintextDec2);
plaintextDec2->SetLength(encodedLength);
std::vector<std::complex<double>> expectedOutput(
{1, 1.414213, 1.732050, 2, 2.236067, 2.449489, 2.645751, 2.828427, 3});
std::cout << "Expected output\n\t" << expectedOutput << std::endl;
std::vector<std::complex<double>> finalResult = plaintextDec->GetCKKSPackedValue();
std::cout << "Actual output\n\t" << finalResult << std::endl << std::endl;
std::vector<std::complex<double>> expectedOutput2(
{66.6196, 12.9053, 8872.5400, 209516.0000, 13109.5000, 5628.3500, 1909.7900, 972.4270});
std::cout << "Expected output\n\t" << expectedOutput2 << std::endl;
std::vector<std::complex<double>> finalResult2 = plaintextDec2->GetCKKSPackedValue();
std::cout << "Actual output\n\t" << finalResult2 << std::endl << std::endl;
}