I’m trying to perform vectorized operations using CpuFheUintXXArray data type in TFHE-rs. I’m using RadixCiphertext instead of FheUintXX because it seems that CpuFheUintXXArray does not support holding FheUintXX types.
This is my code:
use std::time::Instant;
use tfhe::integer::{gen_keys_radix, IntegerRadixCiphertext, RadixCiphertext};
use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS;
use tfhe::{CpuFheUint32Array, ClearArray, ConfigBuilder, generate_keys, set_server_key, FheUint32};
use tfhe::array::Slicing;
use tfhe::prelude::*;
fn main() {
let num_block = 6;
let (client_key, server_key) = gen_keys_radix(PARAM_MESSAGE_2_CARRY_2_KS_PBS, num_block);
let config = ConfigBuilder::default().build();
let (cks, sks) = generate_keys(config);
set_server_key(sks);
let msg1 = 12u32;
let msg2 = 11u32;
let msg3 = 9u32;
// We use the client key to encrypt two messages:
let ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
let ct_3 = client_key.encrypt(msg3);
let ct_4 = client_key.encrypt(1u32);
let ct_5 = client_key.encrypt(1u32);
let ct_6 = client_key.encrypt(1u32);
let t1 = vec![ct_1.clone(), ct_2.clone(), ct_3.clone()];
let tt1 = CpuFheUint32Array::new(t1, vec![1, 3]);
let t2 = vec![ct_4.clone(), ct_5.clone(), ct_6.clone()];
let tt2 = CpuFheUint32Array::new(t2, vec![1, 3]);
let clear = ClearArray::new(vec![1u32, 2u32, 3u32], vec![1, 3]);
let res1 = &tt1 * &tt2;
let res2 = &tt2 + &tt1;
let res3 = &tt1 + &clear;
let j0 = clear.as_slice().container().clone().into_inner()[0];
let j1 = clear.as_slice().container().clone().into_inner()[1];
let j2 = clear.as_slice().container().clone().into_inner()[2];
let triv0: RadixCiphertext = server_key.create_trivial_radix(j0, num_block);
let triv1: RadixCiphertext = server_key.create_trivial_radix(j1, num_block);
let triv2: RadixCiphertext = server_key.create_trivial_radix(j2, num_block);
let trivss = CpuFheUint32Array::new(vec![triv0, triv1, triv2], vec![1, 3]);
let res4 = &tt2 * &trivss;
let end_time = start_time.elapsed();
let tmp_t1 = tt1.container();
let tmp_t2 = tt2.container();
let tmp_res1 = res1.container();
let tmp_res2 = res2.container();
let tmp_res3 = res3.container();
let tmp_res4 = res4.container();
let dec_t1: Vec<u32> = tmp_t1.iter().map(|x| {client_key.decrypt(x)}).collect();
let dec_t2: Vec<u32> = tmp_t2.iter().map(|x| {client_key.decrypt(x)}).collect();
let dec_res1: Vec<u32> = tmp_res1.iter().map(|x| {client_key.decrypt(x)}).collect();
let dec_res2: Vec<u32> = tmp_res2.iter().map(|x| {client_key.decrypt(x)}).collect();
let dec_res3: Vec<u32> = tmp_res3.iter().map(|x| {client_key.decrypt(x)}).collect();
let dec_res4: Vec<u32> = tmp_res4.iter().map(|x| {client_key.decrypt(x)}).collect();
println!("{:?}", dec_t1);
println!("{:?}", dec_t2);
println!("{:?}", dec_res1);
println!("{:?}", dec_res2);
println!("{:?}", dec_res3);
println!("{:?}", dec_res4);
}
But it outputs non-sense results (except the first two lines):
[12, 11, 9]
[1, 1, 1]
[2938, 1768, 3510]
[1505, 2086, 462]
[7, 2530, 1703]
[4012, 1201, 2203]
I think this because using CpuFheUint32Array requires the keys generated using the default config builder (e.g., let config = ConfigBuilder::default().build(); let (cks,sks)=generate_keys(config); set_server_key(sks);). Thus, the operations are performed using the sks key.
Does this have a solution?
Thanks in advance!