homenc / HElib

HElib is an open-source software library that implements homomorphic encryption. It supports the BGV scheme with bootstrapping and the Approximate Number CKKS scheme. HElib also includes optimizations for efficient homomorphic evaluation, focusing on effective use of ciphertext packing techniques and on the Gentry-Halevi-Smart optimizations.
https://homenc.github.io/HElib
Other
3.11k stars 760 forks source link

Multiplying rotated ciphertext vector by DummyEncrypt'ed vector throws "addPrimes can only be called on a disjoint set" error #392

Open raghav198 opened 3 years ago

raghav198 commented 3 years ago

Hi,

I'm trying to write a matrix/vector multiplication where the vector is encrypted but the matrix is not, and I've been using DummyEncrypt on the matrix to achieve that, but I've been running into some errors. Using the matrix multiplication algorithm found in "Algorithms in HELib", the matrix A is represented as a std::vector of ciphertexts of the diagonals of the matrix, and each diagonal is multiplied by the corresponding rotation of the vector b. Since I have As I need to multiply by the same b, I've been storing all the necessary rotations of b in a vector and then using them for each matrix. This works fine normally, but when the matrix is DummyEncrypted and each diagonal is processed in a different thread (using NTL_EXEC_INDEX for multithreading) I get a few WARNING: addPrimes called (2) in DoubleCRT::op and then eventually an helib::LogicError that says addPrimes can only be called on a disjoint set.

I'm not sure what's causing this, since it doesn't always happen after the same number of steps, and only occurs when the matrix is DummyEncrypt'ed and the vector of rotations of b is accessed from multiple threads.

DummyEncrypt usage:

std::vector<long> ptxt = ...;
NTL:ZZX pp;
ea->encode(pp, ptxt);
helib::Ctxt ctxt(pk);
ctxt.DummyEncrypt(pp); // now "ctxt" contains a DummyEncrypt'ed ptxt

Sample code:

int main()
{
    unsigned long p = 2;
    unsigned long m = 0;
    unsigned long bits = 400;
    unsigned long r = 1;

    unsigned long sec = 128;
    unsigned long cols = 3;
    unsigned long minslots = 0;

    m = helib::FindM(sec, bits, cols, p, minslots, 0, 0);

    helib::Context context(m, p, r);
    helib::buildModChain(context, bits, cols);
    helib::SecKey *sk = new helib::SecKey(context);
    sk->GenSecKey();
    helib::addSome1DMatrices(*sk);
    long nslots = context.ea->size();

    std::vector<helib::Ctxt> mat = ...; // each helib::Ctxt has been DummyEncrypt'ed
    helib::Ctxt vec = ...; // this is properly encrypted
    std::vector<helib::Ctxt> rotations;
    for (int i = 0; i < mat.size(); i++) {
        auto rotated = vec;
        context.ea->rotate(rotated, i);
        rotations.push_back(rotated);
    }

    // multiplying once
    int cols = mat.size();
    helib::Ctxt ans(rotations[0].getPubKey());
    NTL_EXEC_INDEX(cols, i)
    {
        auto rotated = rotations[i];
        rotated.multiplyBy(mat[i]); // NOTE: this is the line that throws the error
        ans += rotated;
    }
    NTL_EXEC_END

    return 0;

}

What does this error mean? Am I using something incorrectly, or mistaken with what DummyEncrypt does?

Thanks in advance!

jlhcrawford commented 3 years ago

Hi @raghav198, I cannot seem to reproduce the helib::LogicError using the code you have provided above.

Have you tried running it single threaded?

How are you populating the matrix mat and vector vec? Also how are you encrypting your data using the conventional encrypt and dummy encrypt?

You might want to look at using the function helib::innerProduct(ans, mat, rotations) to perform your multiplication and summation at the end of your code. The API can be found in Ctxt.h

P.S. You might want to consider finding out the m value produced by findM and hard coding it so as to not call the function every time you run the program.

raghav198 commented 3 years ago

Hi @jlhcrawford,

It runs fine single threaded; for some reason the issue only appears when its multithreaded. During testing I've just been using garbage data in mat and vec, but these are normally filled with 1s and 0s (plaintext space mod 2). I encode mat as follows:

std::vector<long> plain;
std::vector<helib::Ctxt> mat;
for (int i = 0; i < sz; i++) {
    std::vector<long> diag = {1, 0, 1, 1, 1, 0, 1, 1, 0, 1};
    diag.resize(ea->size());
    NTL::ZZX pp;
    ea->encode(pp, diag);
    helib::Ctxt enc_diag(pk);
    enc_diag.DummyEncrypt(pp);
    mat.push_back(enc);
}

The vector vec is simply

std::vector<long> plain_vec;
helib::Ctxt vec(pk);
ea->encrypt(vec, pk, plain_vec);

I've noticed that the error doesn't always show up, and also not always at the same iteration of the loop; the matrices I've been working with are ~100-150 columns wide, in case that changes things.

Also, thanks, I'll look into helib::innerProduct; do you know if it does the multplications in parallel (multithreaded)?

DylanWangWQF commented 2 years ago

@raghav198 @faberga hit this issue when using multithread