Hello, I attempted to test different operations because the results I had were inconsistent, so I created shortint Ciphertexts with
fn main(){
let (client_key, server_key) = gen_keys(PARAM_MESSAGE_1_CARRY_0_KS_PBS);
let msg1 = 1;
let msg2 = 1;
let ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
let mut start_time = Instant::now();
let ct_3 = server_key.unchecked_add(&ct_1, &ct_2);
let mut elapsed_time = start_time.elapsed();
println!("unchecked_add duration{:?}",elapsed_time);
start_time = Instant::now();
let ct_4 = server_key.unchecked_mul_lsb(&ct_1, &ct_2);
elapsed_time = start_time.elapsed();
println!("unchecked_mul duration {:?}",elapsed_time);
start_time = Instant::now();
let ct_5 = server_key.mul_lsb(&ct_1, &ct_2);
elapsed_time = start_time.elapsed();
println!("mul_lsb duration {:?}",elapsed_time);
start_time = Instant::now();
let ct_6 = server_key.unchecked_bitand(&ct_1, &ct_2);
elapsed_time = start_time.elapsed();
println!("unchecked_bitand duration{:?}",elapsed_time);
start_time = Instant::now();
let ct_7 = server_key.bitand(&ct_1, &ct_2);
elapsed_time = start_time.elapsed();
println!("bitand duration{:?}",elapsed_time);
let res_1 = client_key.decrypt(&ct_3);
let res_2 = client_key.decrypt(&ct_4);
let res_3 = client_key.decrypt(&ct_5);
let res_4 = client_key.decrypt(&ct_6);
let res_5 = client_key.decrypt(&ct_7);
println!("res unchecked_add{}",res_1);
println!("res unchecked_mul{}",res_2);
println!("res mul{}",res_3);
println!("res unchecked_bitand{}",res_4);
println!("res bitand{}",res_5);
}
And the results are surprising
unchecked_add duration110.613µs
unchecked_mul duration 5.15653459s
mul_lsb duration 18.800073878s
unchecked_bitand duration5.145344191s
bitand duration5.137075047s
res unchecked_add0
res unchecked_mul0
res mul1
res unchecked_bitand0
res bitand0
the unchecked mul gives 0 instead of 1, as well as the unchecked and and the and.
hello @Norrin_Radix
You are using a 1_0 parameter which is not compatible with “bivariate operations”
-
add is modulo your modulo so 1 bit = 2, 1 + 1 % 2 == 0
-
unchecked mul as said not compatible with bivariate operations but as you call the unchecked operations there is no “safety net” and it lets you do whatever you want
-
mul result cannot potentially be relied on as the parameters are not compatible with bivariate ops, but some ops have edge cases for “weird parameters” and you might have been lucky
-
same for bitand and bivariate stuff
Try the 1_1 parameter and the results should all be correct modulo 2
If you need boolean arithmetic you may want to check the boolean module but the set of operation is likely not all you may want/need and is always bootstrapped
Cheers
Also make sure to use the --release
flag when running with cargo
cargo run --release
…
Thanks I thought no carry would just give 1*1 = 1. The same for the and. Not sure why the carry is needed then. I tried it after posting and yes it works with message_1_carry_1, mult or and are twice longer than with 1_0 but at least it works. With a parallel implementation it should work.
Edit: Indeed --release is much faster.
Thanks
Essentially for the carry space when we compute a bivariate pbs we do:
lhs + msg_mod * rhs
this gives a ciphertext encrypting a representation of both lhs and rhs, with a well crafterd lookup table we can evaluate a bivariate function with a single input containing the above.
Only issue is you need space for both as multiplying by msg_mod means you don’t fit in just msg mod, to fit a msg that fits in msg_mod you need carry_mod >= msg_mod
essentially bit wise you have
for carry mod == msg mod = 2
if lhs = rhs = 1
lhs * msg_mod
carry | msg
1 | 0
+ rhs
carry | msg
1 | 1
if carry mod = 1 then the carry space does not exist and you have potentially undefined behavior
ok thanks, that’s funny, I did not know that.
I think, depending on the parameters, you can speed up your calculations.
from what I understand, the LUT is produced with
unchecked_evaluate_bivariate_function_assign(ct_left, ct_right, |x, y| {
(x * y) % res_modulus
});
So, if x=1 and y = 0, in the 1 bit scenario, you will produce one cell in the LUT, different from x=0 and y = 1, or x = 0 and y =0 while the result is the same.
So in our case, there will be 4 cells while only 2 are needed. If you add lhs and rhs together and consider only the msb, you get the result of the multiplication.
So in a message_1_carry_1 scenario |x, y| {(x + y) / res_modulus});
would give the same result (given that you keep both the lhs and the rhs at the same level).
(lhs + rhs)/res_modulus
That’s possible yes, but some parts of the library are not speciliazed for booleans