Hi all. I am trying to understand how the estimated precision is affected when the number of ciphertext levels is exhausted due to repeated multiplications. In the following setting:
#include "openfhe.h"
using namespace std;
using namespace lbcrypto;
int main() {
int batchsize = 8;
int scaling_mod_size = 59;
int first_scaling_mod_size = 59;
int logN = 16;
int depth = 20;
int numLargeDigits = 3;
SecretKeyDist secretKeyDist = UNIFORM_TERNARY;
CCParams<CryptoContextCKKSRNS> parameters;
parameters.SetSecurityLevel(HEStd_128_classic);
parameters.SetBatchSize(batchsize);
parameters.SetRingDim(1 << logN);
parameters.SetScalingModSize(scaling_mod_size);
parameters.SetFirstModSize(first_scaling_mod_size);
parameters.SetMultiplicativeDepth(depth);
parameters.SetNumLargeDigits(numLargeDigits);
parameters.SetSecretKeyDist(secretKeyDist);
CryptoContext<DCRTPoly> cc = GenCryptoContext(parameters);
cc->Enable(PKE);
cc->Enable(ADVANCEDSHE);
cc->Enable(LEVELEDSHE);
cc->Enable(KEYSWITCH);
auto keys= cc->KeyGen();
cc->EvalMultKeyGen(keys.secretKey);
// Experiment: What is the estimated precision after repeated multiplications
vector<double> x1 = {1, 2, 3, 4, 1, 2, 3, 4};
vector<double> x2 = {1, 2, 1, 2, 1, 2, 1, 2};
Plaintext pt1 = cc->MakeCKKSPackedPlaintext(x1);
Plaintext pt2 = cc->MakeCKKSPackedPlaintext(x2);
auto ct1 = cc->Encrypt(keys.publicKey, pt1);
auto ct2 = cc->Encrypt(keys.publicKey, pt2);
auto cMul = cc->EvalMult(ct1, ct2);
for (int i=0; i<depth-1; i++){
cMul = cc->EvalMult(cMul, ct2);
}
Plaintext pMul;
cc->Decrypt(keys.secretKey, cMul, &pMul);
pMul->SetLength(batchsize);
cout << "Repeated HEMult result: " << endl << pMul << endl;
// The same experiment, using a smaller range for the plaintext values
vector<double> x1_small = {0.5, 0.3, -0.2, 0.7, -0.7, 0.6, -0.1, 0.1};
vector<double> x2_small = {0.9, 0.1, 0.2, 0.05, -0.2, -0.1, -0.7, 0.3};
Plaintext pt1_small = cc->MakeCKKSPackedPlaintext(x1_small);
Plaintext pt2_small = cc->MakeCKKSPackedPlaintext(x2_small);
auto ct1_small = cc->Encrypt(keys.publicKey, pt1_small);
auto ct2_small = cc->Encrypt(keys.publicKey, pt2_small);
auto cMul_small = cc->EvalMult(ct1_small, ct2_small);
for (int i=0; i<depth-1; i++){
cMul_small = cc->EvalMult(cMul_small, ct2_small);
}
Plaintext pMul_small;
cc->Decrypt(keys.secretKey, cMul_small, &pMul_small);
pMul_small->SetLength(batchsize);
cout << "Repeated HEMult result, smaller range inputs: " << endl << pMul_small << endl;
return 0;
}
with the output:
Repeated HEMult result:
(-0.482971, -0.0339079, 0.219426, 0.297126, -0.482971, -0.0339078, 0.219426, 0.297126, ... ); Estimated precision: 27 bits
Repeated HEMult result, smaller range inputs:
(0.0607883, -3.02942e-16, -1.63923e-15, -3.43574e-17, -7.52176e-15, 1.32031e-16, -7.97923e-05, 3.48671e-12, ... ); Estimated precision: 51 bits
The estimated precision dropped severely when the input range is larger. In the error analyses in the “Approximate Homomorphic Encryption with Reduced Approximation Error” paper, I do not see any error terms taking into account the input range. Could you please help me with this?