Hello everyone!
I would like to use EvalLinearTransfrom
function to perform arbitrary permutations over a ckks-ciphertext. The problem is that every call of EvalLinearTransfrom
results in growth of a noise, so I have to call RescaleInPlace
every time GetNoiseScaleDeg
is equal to 2. But then if I try to perform EvalLinearTransfrom
over the rescaled ciphertext with the same permutation matrix(aka std::vector<ConstPlaintext>
), I get Modulus missmatch
error. As long as I understand the problem is that permutation matrix (aka std::vector<ConstPlaintext>
), that was precomputed in EvalLinearTransformPrecompute
, has a different Modulus
. How can I change Modulus
of the matrix (aka std::vector<ConstPlaintext>
) accordingly to the rescaled ciphertext?
I’m experimenting and my understanding of what happens under the hood of all these functions is really superficial, so I would appreciate any ideas!
My code:
#define _USE_MATH_DEFINES
#include <vector>
#include <cassert>
#include <iostream>
#include <cmath>
#include <fstream>
#include <tuple>
#include <string>
#include "openfhe.h"
std::tuple<lbcrypto::CryptoContext<lbcrypto::DCRTPoly>, lbcrypto::KeyPair<lbcrypto::DCRTPoly>, size_t, size_t> default_fhe_setup_local(\
const size_t ring_dim, const size_t levelsAvailableAfterBootstrap, const size_t data_length, \
const std::vector<int32_t> & rotation_index_list){
// openfhe setup from https://github.com/openfheorg/openfhe-development/blob/main/src/pke/examples/advanced-ckks-bootstrapping.cpp
lbcrypto::CCParams<lbcrypto::CryptoContextCKKSRNS> parameters;
lbcrypto::SecretKeyDist secretKeyDist = lbcrypto::UNIFORM_TERNARY;
parameters.SetSecretKeyDist(secretKeyDist);
//parameters.SetSecurityLevel(lbcrypto::HEStd_128_classic)
parameters.SetSecurityLevel(lbcrypto::HEStd_NotSet);
parameters.SetRingDim(ring_dim);
parameters.SetNumLargeDigits(3);
parameters.SetKeySwitchTechnique(lbcrypto::HYBRID);
#if NATIVEINT == 128 && !defined(__EMSCRIPTEN__)
// Currently, only FIXEDMANUAL and FIXEDAUTO modes are supported for 128-bit CKKS bootstrapping.
lbcrypto::ScalingTechnique rescaleTech = lbcrypto::FIXEDAUTO;
usint dcrtBits = 78;
usint firstMod = 89;
#else
// All modes are supported for 64-bit CKKS bootstrapping.
lbcrypto::ScalingTechnique rescaleTech = lbcrypto::FIXEDMANUAL;
usint dcrtBits = 59;
usint firstMod = 60;
#endif
parameters.SetScalingModSize(dcrtBits);
parameters.SetScalingTechnique(rescaleTech);
parameters.SetFirstModSize(firstMod);
std::vector<uint32_t> levelBudget = {1, 1};
std::vector<uint32_t> bsgsDim = {0, 0};
size_t depth = levelsAvailableAfterBootstrap + lbcrypto::FHECKKSRNS::GetBootstrapDepth(levelBudget, secretKeyDist);
parameters.SetMultiplicativeDepth(depth);
lbcrypto::CryptoContext<lbcrypto::DCRTPoly> cryptoContext = GenCryptoContext(parameters);
cryptoContext->Enable(lbcrypto::PKE);
cryptoContext->Enable(lbcrypto::KEYSWITCH);
cryptoContext->Enable(lbcrypto::LEVELEDSHE);
cryptoContext->Enable(lbcrypto::ADVANCEDSHE);
cryptoContext->Enable(lbcrypto::FHE);
//plaintext has to have length power of two. Round data_length to the nearest upper power of two
size_t length = std::pow(2, std::ceil(std::log2(data_length)));
cryptoContext->EvalBootstrapSetup(levelBudget, bsgsDim, length);
lbcrypto::KeyPair<lbcrypto::DCRTPoly> keyPair = cryptoContext->KeyGen();
cryptoContext->EvalMultKeyGen(keyPair.secretKey);
// Generate bootstrapping keys
cryptoContext->EvalBootstrapKeyGen(keyPair.secretKey, length);
//specify rotation we need
cryptoContext->EvalRotateKeyGen(keyPair.secretKey, rotation_index_list);
return std::tuple<lbcrypto::CryptoContext<lbcrypto::DCRTPoly>, lbcrypto::KeyPair<lbcrypto::DCRTPoly>, size_t, size_t>(cryptoContext, keyPair, depth, length);
}
int main(){
size_t N = 8;
auto cryptoContext_key_depth_length = default_fhe_setup_local(2<<11, 30, N, {});
auto cc = std::get<0>(cryptoContext_key_depth_length);
//get keys
auto keyPair = std::get<1>(cryptoContext_key_depth_length);
//this determines the size of the permutation matrix.
auto size_of_permutation = 8;
std::vector<double> a({0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8});
lbcrypto::Plaintext plaintext = cc->MakeCKKSPackedPlaintext(a, 1, 0, nullptr, size_of_permutation);
auto ciphertext = cc->Encrypt(keyPair.publicKey, plaintext);
//permutation matrix
std::vector<std::vector<std::complex<double>>> matrix({{0, 1, 0, 0,0,0,0,0}, {1, 0, 0, 0,0,0,0,0}, {0, 0, 0, 1,0,0,0,0},\
{0, 0, 1, 0,0,0,0,0}, {0, 0, 0, 0,1,0,0,0}, {0, 0, 0, 0,0,1,0,0}, {0, 0, 0, 0,0,0,1,0}, {0, 0, 0, 0,0,0,0,1}});
lbcrypto::FHECKKSRNS ckksrns;
uint32_t l = 0;
ckksrns.EvalBootstrapSetup(*cc, {1, 1}, {0, 0}, size_of_permutation, 0, true);
auto matrix_pre = ckksrns.EvalLinearTransformPrecompute(*cc, matrix, 1.0, l);
lbcrypto::Plaintext result;
cc->Decrypt(keyPair.secretKey, ciphertext, &result);
result->SetLength(N);
std::cout << "Before permutation = " << result;
for(int i=0; i<10; i++){
if(ciphertext->GetNoiseScaleDeg() ==2){
std::cout<<"Rescale"<<std::endl;
cc->RescaleInPlace(ciphertext);
}
ciphertext = ckksrns.EvalLinearTransform(matrix_pre, ciphertext);
std::cout<<"Linear transformed "<<i+1<<" times"<<std::endl;
}
cc->Decrypt(keyPair.secretKey, ciphertext, &result);
result->SetLength(N);
std::cout << "After permutation = " << result;
return 0;
}
Output:
$ OMP_NUM_THREADS=1 ./test/test_EvalLinearTransform.exe
Before permutation = (0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, ... ); Estimated precision: 49 bits
Linear transformed 1 times
Rescale
terminate called after throwing an instance of 'lbcrypto::OpenFHEException'
what(): D:/work/homomorphic_encryption/openfhe-development/src/core/include/lattice/hal/default/poly.h:l.274:operator*=(): Modulus missmatch