CKKS accuracy difference 16k vs 32k poly modulus
Hi,
I'm working with the CKKS scheme to perform a calculation involving plaintexts and ciphertexts. It appears that there is a larger accuracy difference to the same cleartext calculations when using 32k vs 16k cyclotomic polynomial. What could be the reason for it?
The encryption parameters are: scale=2^60, coefficient modulus: {60, 60, 60, 60, 60}. I tested the calculation using 16k and 32k cyclotomic polynomial separately. My SEAL version is 4.1.1.
The cleartext vs encrypted comparison results are:
testing poly modulus: 16384 enc result: 274895208699.0369873046875 cleartext result: 274895208699 diff: 0.0369873046875
testing poly modulus: 32768 enc result: 274895208700.9400634765625 cleartext result: 274895208699 diff: 1.9400634765625
The code:
void run_test_16_32_poly_1(int poly_modulus_degree)
{
cout.precision(32);
cout << "testing poly modulus: " << poly_modulus_degree << endl;
EncryptionParameters parms(scheme_type::ckks);
parms.set_poly_modulus_degree(poly_modulus_degree);
parms.set_coeff_modulus(CoeffModulus::Create(poly_modulus_degree, { 60, 60, 60, 60, 60 }));
double scale = pow(2.0, 60);
// Create context
SEALContext context(parms);
// Create keys
KeyGenerator keygen(context);
seal::PublicKey public_key;
keygen.create_public_key(public_key);
SecretKey secret_key = keygen.secret_key();
RelinKeys relin_keys;
keygen.create_relin_keys(relin_keys);
// Create encryptor, evaluator, decryptor
Encryptor encryptor(context, public_key);
Evaluator evaluator(context);
Decryptor decryptor(context, secret_key);
CKKSEncoder encoder(context);
vector<double> result;
double p = 524309;
double p_square = p*p;
double a = p-1;
double u = 1;
// calculation
Plaintext plain_u, plain_p_square, plain_a, plain_p_times_10, plain_result;
Ciphertext encrypted_u, encrypted_a;
encoder.encode(u, scale, plain_u);
encryptor.encrypt(plain_u, encrypted_u);
encoder.encode(p_square, scale, plain_p_square);
evaluator.multiply_plain_inplace(encrypted_u, plain_p_square);
evaluator.rescale_to_next_inplace(encrypted_u);
encrypted_u.scale() = scale;
encoder.encode(a, encrypted_u.parms_id(), encrypted_u.scale(), plain_a);
encryptor.encrypt(plain_a, encrypted_a);
evaluator.add_inplace(encrypted_u, encrypted_a);
encoder.encode(p*10, encrypted_u.parms_id(), encrypted_u.scale(), plain_p_times_10);
evaluator.sub_plain_inplace(encrypted_u, plain_p_times_10);
decryptor.decrypt(encrypted_u, plain_result);
encoder.decode(plain_result, result);
double cleartext_calc = (u*p_square)+a-(p*10);
cout << "result vec[0]: " << result[0] << endl;
cout << "cleartext: " << cleartext_calc << endl;
cout << "diff: " << result[0] - cleartext_calc << endl;
}
void test_16_32_poly(void)
{
run_test_16_32_poly_1(16384);
cout << endl;
run_test_16_32_poly_1(32768);
}