def divide_and_round_q_last_inplace(self, input): base_q_size = self.base_q.size last_ptr = input[base_q_size - 1] # Add (qi-1)/2 to change from flooring to rounding last_modulus = self.base_q.base[-1] half = last_modulus >> 1 last_ptr = [(x + half) % last_modulus for x in last_ptr] temp_ptr = [] for i in range(base_q_size - 1): temp_ptr = [x % self.base_q.base[i] for x in last_ptr] half_mod = half % self.base_q.base[i] temp_ptr = [(x - half_mod) % self.base_q.base[i] for x in temp_ptr] input[i] = poly_sub_mod(input[i], temp_ptr, self.base_q.base[i], self.coeff_count) # qk^(-1) * ((ct mod qi) - (ct mod qk)) mod qi input[i] = [(x * self.inv_q_last_mod_q[i]) % self.base_q.base[i] for x in input[i]] return input
def _sub_cipher_cipher(self, ct1, ct2): """Subtract two ciphertexts. Args: ct1 (Ciphertext): First polynomial argument (Minuend). ct2 (Ciphertext): Second polynomial argument (Subtrahend). Returns: A Ciphertext object with value equivalent to result of subtraction of two provided arguments. """ ct1, ct2 = copy.deepcopy(ct1.data), copy.deepcopy(ct2.data) result = ct2 if len(ct2) > len(ct1) else ct1 min_size, max_size = min(len(ct1), len(ct2)), max(len(ct1), len(ct2)) for i in range(min_size): for j in range(len(self.coeff_modulus)): result[i][j] = poly_sub_mod(ct1[i][j], ct2[i][j], self.coeff_modulus[j], self.poly_modulus) for i in range(min_size + 1, max_size): for j in range(len(self.coeff_modulus)): result[i][j] = poly_negate_mod(result[i][j], self.coeff_modulus[j]) return CipherText(result)
def _switch_key_inplace(self, ct, key): if not isinstance(key, RelinKey): raise RuntimeError("Relinearization key is invalid") param_id = ct.param_id ct = ct.data key_vector = key.data context_data = self.context.context_data_map[param_id] key_context = self.context.context_data_map[self.context.key_param_id] coeff_modulus = context_data.param.coeff_modulus decomp_mod_count = len(coeff_modulus) key_mod = key_context.param.coeff_modulus key_mod_count = len(key_mod) rns_mod_count = decomp_mod_count + 1 target = ct[-1] # Last component of ciphertext modswitch_factors = key_context.rns_tool.inv_q_last_mod_q for i in range(decomp_mod_count): local_small_poly_0 = copy.deepcopy(target[i]) temp_poly = [[[0] for x in range(rns_mod_count)], [[0] for x in range(rns_mod_count)]] for j in range(rns_mod_count): index = key_mod_count - 1 if j == decomp_mod_count else j if key_mod[i] <= key_mod[index]: local_small_poly_1 = copy.deepcopy(local_small_poly_0) else: local_small_poly_1 = [ x % key_mod[index] for x in local_small_poly_0 ] for k in range(2): local_small_poly_2 = poly_mul_mod( local_small_poly_1, key_vector[i][k][index], key_mod[index], self.poly_modulus, ) temp_poly[k][j] = poly_add_mod(local_small_poly_2, temp_poly[k][j], key_mod[index], self.poly_modulus) # Results are now stored in temp_poly[k] # Modulus switching should be performed for k in range(2): temp_poly_ptr = temp_poly[k][decomp_mod_count] temp_last_poly_ptr = temp_poly[k][decomp_mod_count] temp_poly_ptr = [x % key_mod[-1] for x in temp_poly_ptr] # Add (p-1)/2 to change from flooring to rounding. half = key_mod[-1] >> 1 temp_last_poly_ptr = [(x + half) % key_mod[-1] for x in temp_last_poly_ptr] encrypted_ptr = ct[k] for j in range(decomp_mod_count): temp_poly_ptr = temp_poly[k][j] temp_poly_ptr = [x % key_mod[j] for x in temp_poly_ptr] local_small_poly = [x % key_mod[j] for x in temp_last_poly_ptr] half_mod = half % key_mod[j] local_small_poly = [(x - half_mod) % key_mod[j] for x in local_small_poly] # ((ct mod qi) - (ct mod qk)) mod qi temp_poly_ptr = poly_sub_mod(temp_poly_ptr, local_small_poly, key_mod[j], self.poly_modulus) # qk^(-1) * ((ct mod qi) - (ct mod qk)) mod qi temp_poly_ptr = [(x * modswitch_factors[j]) % key_mod[j] for x in temp_poly_ptr] encrypted_ptr[j] = poly_add_mod(temp_poly_ptr, encrypted_ptr[j], key_mod[j], self.poly_modulus) return CipherText(ct[0:2], param_id)