microsoft / SEAL

Microsoft SEAL is an easy-to-use and powerful homomorphic encryption library.
https://www.microsoft.com/en-us/research/group/cryptography-research/
MIT License
3.62k stars 709 forks source link

CKKS accuracy difference 16k vs 32k poly modulus #685

Open meirgold opened 6 months ago

meirgold commented 6 months ago

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);

}