nucypher / nufhe

NuCypher fully homomorphic encryption (NuFHE) library implemented in Python
https://nufhe.readthedocs.io/en/latest/
GNU General Public License v3.0
441 stars 53 forks source link

Precision in binary gates, how to use binary gates in sequence #36

Closed tguerand closed 2 years ago

tguerand commented 2 years ago

Dear all,

Thank you for all of you work and for this project .

I am trying to use binary gates in a sequence (approx ~70) with the best performance. But I see that there are some differences when decrypted compared to the operations done on the clear ciphertext. I have a median similarity of 70% (i.e. 70% of the decrypted cipher text is the same as the clear file).

So firstly, I would like to know if my use of the binary gates was wrong ?

Also, I would like to know if there has been some verifications/statistics about noise propagation when boostrapping ? From what I understood from the code, after each gate there is bootstrapping.

I copy pasted my code down there if you want to have a look, this one gives ~52% accuracy

Thank you !

def sat_clause(x_0, x_1, x_2, x_3, x_4, x_5, x_6, x_7, x_8, thr, perf_params, cloud_key):
    params = cloud_key.params

    # I have 23 terms with OR gates, so each of this term is initialized with an empty ciphertext

    _0 = empty_ciphertext(thr, params, x_0.shape)
    _1 = empty_ciphertext(thr, params, x_0.shape)
    _2 = empty_ciphertext(thr, params, x_0.shape)
    _3 = empty_ciphertext(thr, params, x_0.shape)
    _4 = empty_ciphertext(thr, params, x_0.shape)
    _5 = empty_ciphertext(thr, params, x_0.shape)
    _6 = empty_ciphertext(thr, params, x_0.shape)
    _7 = empty_ciphertext(thr, params, x_0.shape)
    _8 = empty_ciphertext(thr, params, x_0.shape)
    _9 = empty_ciphertext(thr, params, x_0.shape)
    _10 = empty_ciphertext(thr, params, x_0.shape)
    _11 = empty_ciphertext(thr, params, x_0.shape)
    _12 = empty_ciphertext(thr, params, x_0.shape)
    _13 = empty_ciphertext(thr, params, x_0.shape)
    _14 = empty_ciphertext(thr, params, x_0.shape)
    _15 = empty_ciphertext(thr, params, x_0.shape)
    _16 = empty_ciphertext(thr, params, x_0.shape)
    _17 = empty_ciphertext(thr, params, x_0.shape)
    _18 = empty_ciphertext(thr, params, x_0.shape)
    _19 = empty_ciphertext(thr, params, x_0.shape)
    _20 = empty_ciphertext(thr, params, x_0.shape)
    _21 = empty_ciphertext(thr, params, x_0.shape)
    _22 = empty_ciphertext(thr, params, x_0.shape)

        # each term will be save in their respective ciphertext (term 0 is saved in ciphertext 0_ , ... )

    # x_5|x_8|~x_7
    gate_or(thr, cloud_key, _0, x_5, x_8, perf_params)
    gate_oryn(thr, cloud_key, _0, _0.copy(), x_7, perf_params)

    # x_5|~x_1|~x_7
    gate_oryn(thr, cloud_key, _1, x_5, x_1, perf_params)
    gate_oryn(thr, cloud_key, _1, _1.copy(), x_7, perf_params) # doing (x_5 | ~x_1) | ~x_7

    # x_5|~x_3|~x_7
    gate_oryn(thr, cloud_key, _2, x_5, x_3, perf_params)
    gate_oryn(thr, cloud_key, _2, _2.copy(), x_7, perf_params)

    # x_5|~x_4|~x_7
    gate_oryn(thr, cloud_key, _3, x_5, x_4, perf_params)
    gate_oryn(thr, cloud_key, _3, _3.copy(), x_7, perf_params)

    # x_5|~x_6|~x_7
    gate_oryn(thr, cloud_key, _4, x_5, x_6, perf_params)
    gate_oryn(thr, cloud_key, _4, _4.copy(), x_7, perf_params)

    # x_0|x_2|x_6|~x_4
    gate_or(thr, cloud_key, _5, x_0, x_2, perf_params)
    gate_or(thr, cloud_key, _5, _5.copy(), x_6, perf_params)
    gate_oryn(thr, cloud_key, _5, _5.copy(), x_4, perf_params)

    # x_1|x_2|x_7|~x_4
    gate_or(thr, cloud_key, _6, x_1, x_2, perf_params)
    gate_or(thr, cloud_key, _6, _6.copy(), x_7, perf_params)
    gate_oryn(thr, cloud_key, _6, _6.copy(), x_4, perf_params)

    # x_1|x_4|x_8|~x_6
    gate_or(thr, cloud_key, _7, x_1, x_4, perf_params)
    gate_or(thr, cloud_key, _7, _7.copy(), x_8, perf_params)
    gate_oryn(thr, cloud_key, _7, _7.copy(), x_6, perf_params)

    # x_2|x_3|x_7|~x_4
    gate_or(thr, cloud_key, _8, x_2, x_3, perf_params)
    gate_or(thr, cloud_key, _8, _8.copy(), x_7, perf_params)
    gate_oryn(thr, cloud_key, _8, _8.copy(), x_4, perf_params)

    # x_3|x_7|x_8|~x_5
    gate_or(thr, cloud_key, _9, x_3, x_7, perf_params)
    gate_or(thr, cloud_key, _9, _9.copy(), x_8, perf_params)
    gate_oryn(thr, cloud_key, _9, _9.copy(), x_5, perf_params)

    # x_1|x_2|~x_0|~x_4
    gate_or(thr, cloud_key, _10, x_1, x_2, perf_params)
    gate_oryn(thr, cloud_key, _10, _10.copy(), x_0, perf_params)
    gate_oryn(thr, cloud_key, _10, _10.copy(), x_4, perf_params)

    # x_2|x_7|~x_4|~x_8
    gate_or(thr, cloud_key, _11, x_2, x_7, perf_params)
    gate_oryn(thr, cloud_key, _11, _11.copy(), x_4, perf_params)
    gate_oryn(thr, cloud_key, _11, _11.copy(), x_8, perf_params)

    # x_4|x_7|~x_5|~x_6
    gate_or(thr, cloud_key, _12, x_4, x_7, perf_params)
    gate_oryn(thr, cloud_key, _12, _12.copy(), x_5, perf_params)
    gate_oryn(thr, cloud_key, _12, _12.copy(), x_6, perf_params)

    # x_6|x_7|~x_4|~x_5
    gate_or(thr, cloud_key, _13, x_6, x_7, perf_params)
    gate_oryn(thr, cloud_key, _13, _13.copy(), x_4, perf_params)
    gate_oryn(thr, cloud_key, _13, _13.copy(), x_5, perf_params)

    # x_1|x_3|x_5|x_8|~x_4
    gate_or(thr, cloud_key, _14, x_1, x_3, perf_params)
    gate_or(thr, cloud_key, _14, _14.copy(), x_5, perf_params)
    gate_or(thr, cloud_key, _14, _14.copy(), x_8, perf_params)
    gate_oryn(thr, cloud_key, _14, _14.copy(), x_4, perf_params)

    # x_6|~x_0|~x_4|~x_7
    gate_oryn(thr, cloud_key, _15, x_6, x_0, perf_params)
    gate_oryn(thr, cloud_key, _15, _15.copy(), x_4, perf_params)
    gate_oryn(thr, cloud_key, _15, _15.copy(), x_7, perf_params)

    # x_6|~x_1|~x_4|~x_8
    gate_oryn(thr, cloud_key, _16, x_6, x_1, perf_params)
    gate_oryn(thr, cloud_key, _16, _16.copy(), x_4, perf_params)
    gate_oryn(thr, cloud_key, _16, _16.copy(), x_8, perf_params)

    # x_0|x_4|x_5|~x_1|~x_6
    gate_or(thr, cloud_key, _17, x_0, x_4, perf_params)
    gate_or(thr, cloud_key, _17, _17.copy(), x_5, perf_params)
    gate_oryn(thr, cloud_key, _17, _17.copy(), x_1, perf_params)
    gate_oryn(thr, cloud_key, _17, _17.copy(), x_6, perf_params)

    # ~x_0|~x_1|~x_6|~x_8
    n_x_0 = empty_ciphertext(thr, params, x_0.shape)
    gate_not(thr, cloud_key, n_x_0, x_0, perf_params)
    gate_oryn(thr, cloud_key, _18, x_0, x_1, perf_params)
    gate_oryn(thr, cloud_key, _18, _18.copy(), x_6, perf_params)
    gate_oryn(thr, cloud_key, _18, _18.copy(), x_8, perf_params)

    # x_1|x_7|~x_0|~x_5|~x_6
    gate_or(thr, cloud_key, _19, x_1, x_7, perf_params)
    gate_oryn(thr, cloud_key, _19, _19.copy(), x_0, perf_params)
    gate_oryn(thr, cloud_key, _19, _19.copy(), x_5, perf_params)
    gate_oryn(thr, cloud_key, _19, _19.copy(), x_6, perf_params)

    # x_1|x_3|x_6|x_8|~x_2|~x_7
    gate_or(thr, cloud_key, _20, x_1, x_3, perf_params)
    gate_or(thr, cloud_key, _20, _20.copy(), x_6, perf_params)
    gate_or(thr, cloud_key, _20, _20.copy(), x_8, perf_params)
    gate_oryn(thr, cloud_key, _20, _20.copy(), x_2, perf_params)
    gate_oryn(thr, cloud_key, _20, _20.copy(), x_7, perf_params)

    # x_4|~x_0|~x_1|~x_6|~x_7
    gate_oryn(thr, cloud_key, _21, x_4, x_0, perf_params)
    gate_oryn(thr, cloud_key, _21, _21.copy(), x_1, perf_params)
    gate_oryn(thr, cloud_key, _21, _21.copy(), x_6, perf_params)
    gate_oryn(thr, cloud_key, _21, _21.copy(), x_7, perf_params)

    # x_7|~x_1|~x_5|~x_6|~x_8
    gate_oryn(thr, cloud_key, _22, x_7, x_1, perf_params)
    gate_oryn(thr, cloud_key, _22, _22.copy(), x_5, perf_params)
    gate_oryn(thr, cloud_key, _22, _22.copy(), x_6, perf_params)
    gate_oryn(thr, cloud_key, _22, _22.copy(), x_8, perf_params)

    answer = empty_ciphertext(thr, params, x_0.shape)

        # apply AND gate on each term

    gate_and(thr, cloud_key, answer, _0, _1, perf_params)
    gate_and(thr, cloud_key, answer, answer.copy(), _2, perf_params)
    gate_and(thr, cloud_key, answer, answer.copy(), _3, perf_params)
    gate_and(thr, cloud_key, answer, answer.copy(), _4, perf_params)
    gate_and(thr, cloud_key, answer, answer.copy(), _5, perf_params)
    gate_and(thr, cloud_key, answer, answer.copy(), _6, perf_params)
    gate_and(thr, cloud_key, answer, answer.copy(), _7, perf_params)
    gate_and(thr, cloud_key, answer, answer.copy(), _8, perf_params)
    gate_and(thr, cloud_key, answer, answer.copy(), _9, perf_params)
    gate_and(thr, cloud_key, answer, answer.copy(), _10, perf_params)
    gate_and(thr, cloud_key, answer, answer.copy(), _11, perf_params)
    gate_and(thr, cloud_key, answer, answer.copy(), _12, perf_params)
    gate_and(thr, cloud_key, answer, answer.copy(), _13, perf_params)
    gate_and(thr, cloud_key, answer, answer.copy(), _14, perf_params)
    gate_and(thr, cloud_key, answer, answer.copy(), _15, perf_params)
    gate_and(thr, cloud_key, answer, answer.copy(), _16, perf_params)
    gate_and(thr, cloud_key, answer, answer.copy(), _17, perf_params)
    gate_and(thr, cloud_key, answer, answer.copy(), _18, perf_params)
    gate_and(thr, cloud_key, answer, answer.copy(), _19, perf_params)
    gate_and(thr, cloud_key, answer, answer.copy(), _20, perf_params)
    gate_and(thr, cloud_key, answer, answer.copy(), _21, perf_params)
    gate_and(thr, cloud_key, answer, answer.copy(), _22, perf_params)

    return answer

The function that does the same equation but on the clear array:

def check_answer(x_0, x_1, x_2, x_3, x_4, x_5, x_6, x_7, x_8):
    answer = (x_5 | x_8 | ~x_7) & (x_5 | ~x_1 | ~x_7) & (x_5 | ~x_3 | ~x_7) & (x_5 | ~x_4 | ~x_7) & (x_5 | ~x_6 | ~x_7) & (x_0 | x_2 | x_6 | ~x_4) & (x_1 | x_2 | x_7 | ~x_4) & (x_1 | x_4 | x_8 | ~x_6) & (x_2 | x_3 | x_7 | ~x_4) & (x_3 | x_7 | x_8 | ~x_5) & (x_1 | x_2 | ~x_0 | ~x_4) & (x_2 | x_7 | ~x_4 | ~x_8) & (x_4 | x_7 | ~x_5 | ~x_6) & (x_6 | x_7 | ~x_4 | ~x_5) & (x_1 | x_3 | x_5 | x_8 | ~x_4) & (x_6 | ~x_0 | ~x_4 | ~x_7) & (x_6 | ~x_1 | ~x_4 | ~x_8) & (x_0 | x_4 | x_5 | ~x_1 | ~x_6) & (~x_0 | ~x_1 | ~x_6 | ~x_8) & (x_1 | x_7 | ~x_0 | ~x_5 | ~x_6) & (x_1 | x_3 | x_6 | x_8 | ~x_2 | ~x_7) & (x_4 | ~x_0 | ~x_1 | ~x_6 | ~x_7) & (x_7 | ~x_1 | ~x_5 | ~x_6 | ~x_8)

    return answer
tguerand commented 2 years ago

It was an error from my side, I forgot a not gate everything works fine!