I want to calculate the ReLU
function through ciphertext conversion. First, I use the EvalCKKStoFHEW
function to convert it to FHEW ciphertext. Then, I calculate the ReLU function by looking up(LUT
) table on the FHEW ciphertext. Finally, I use EvalHEWtoCKKS
to convert it to CKKS ciphertext. However, the code I wrote reported an error and I don’t know how to modify it. My code is written based on scheme-switching
’s FuncViaSchemeSwitching
function. The code is as follows:
// Step 1: Setup CryptoContext for CKKS
// 1 for CKKS to FHEW, 14 for FHEW to CKKS
uint32_t multDepth = 9 + 3 + 2;
uint32_t scaleModSize = 50;
uint32_t ringDim = 2048;
SecurityLevel sl = HEStd_NotSet;
BINFHE_PARAMSET slBin = TOY;
uint32_t logQ_ccLWE = 25;
uint32_t slots = 16; // sparsely-packed
uint32_t batchSize = slots;
CCParams<CryptoContextCKKSRNS> parameters;
parameters.SetMultiplicativeDepth(multDepth);
parameters.SetScalingModSize(scaleModSize);
parameters.SetScalingTechnique(FIXEDAUTO);
parameters.SetSecurityLevel(sl);
parameters.SetRingDim(ringDim);
parameters.SetBatchSize(batchSize);
CryptoContext<DCRTPoly> cc = GenCryptoContext(parameters);
// Enable the features that you wish to use
cc->Enable(PKE);
cc->Enable(KEYSWITCH);
cc->Enable(LEVELEDSHE);
cc->Enable(ADVANCEDSHE);
cc->Enable(SCHEMESWITCH);
std::cout << "CKKS scheme is using ring dimension " << cc->GetRingDimension();
std::cout << ", and number of slots " << slots << std::endl << std::endl;
// Generate encryption keys.
auto keys = cc->KeyGen();
// Step 2: Prepare the FHEW cryptocontext and keys for FHEW and scheme switching
SchSwchParams params;
params.SetSecurityLevelCKKS(sl);
params.SetSecurityLevelFHEW(slBin);
params.SetArbitraryFunctionEvaluation(true);
params.SetCtxtModSizeFHEWLargePrec(logQ_ccLWE);
params.SetNumSlotsCKKS(slots);
params.SetNumValues(slots);
auto privateKeyFHEW = cc->EvalSchemeSwitchingSetup(params);
auto ccLWE = cc->GetBinCCForSchemeSwitch();
cc->EvalSchemeSwitchingKeyGen(keys, privateKeyFHEW);
// Generate the bootstrapping keys for EvalFunc in FHEW
ccLWE->BTKeyGen(privateKeyFHEW);
std::cout << "FHEW scheme is using lattice parameter " << ccLWE->GetParams()->GetLWEParams()->Getn();
std::cout << ", logQ " << logQ_ccLWE;
std::cout << ", and modulus q " << ccLWE->GetParams()->GetLWEParams()->Getq() << std::endl << std::endl;
// Set the scaling factor to be able to decrypt; under the hood, the LWE mod switch will be performed on the ciphertext at the last level
auto modulus_LWE = 1 << logQ_ccLWE;
auto beta = ccLWE->GetBeta().ConvertToInt();
auto pLWE = modulus_LWE / (2 * beta); // Large precision
double scaleCF = 1.0;
cc->EvalCKKStoFHEWPrecompute(scaleCF);
// Step 3: Initialize the function
// Initialize Function relu
auto fp = [](NativeInteger x, NativeInteger p) -> NativeInteger {
if (x <= p) return p;
else return x;
};
// Generate LUT from function f(x)
auto lut = ccLWE->GenerateLUTviaFunction(fp, pLWE);
// Step 4: Encoding and encryption of inputs
// Inputs
std::vector<double> x1={1,2,3,4,5,6,7,8,9,-1,-2,-3,-4,-5,-6,-78};
// Encoding as plaintexts
Plaintext ptxt1 = cc->MakeCKKSPackedPlaintext(x1, 1, 0, nullptr);
// Encrypt the encoded vectors
auto c1 = cc->Encrypt(keys.publicKey, ptxt1);
// Step 5: Scheme switching from CKKS to FHEW
auto cTemp = cc->EvalCKKStoFHEW(c1, slots);
std::cout << "Input x1: " << ptxt1->GetRealPackedValue() << std::endl;
std::cout << "FHEW decryption: ";
LWEPlaintext result;
for (uint32_t i = 0; i < cTemp.size(); ++i) {
ccLWE->Decrypt(privateKeyFHEW, cTemp[i], &result, pLWE);
std::cout << result << " ";
}
// Step 6: Evaluate the function
std::vector<LWECiphertext> cFunc(cTemp.size());
for (uint32_t i = 0; i < cTemp.size(); i++) {
cFunc[i] = ccLWE->EvalFunc(cTemp[i], lut);
}
std::cout << "\nExpected result relu ";
for (uint32_t i = 0; i < slots; ++i) {
std::cout << fp(static_cast<int>(x1[i]), 0) << " ";
}
LWEPlaintext pFunc;
std::cout << "\nFHEW decryption mod " << NativeInteger(pLWE) << ": ";
for (uint32_t i = 0; i < cFunc.size(); ++i) {
ccLWE->Decrypt(privateKeyFHEW, cFunc[i], &pFunc, pLWE);
std::cout << pFunc << " ";
}
std::cout << "\n" << std::endl;
// Step 7: Scheme switching from FHEW to CKKS
auto cTemp2 = cc->EvalFHEWtoCKKS(cFunc, slots, slots, pLWE, 0, pLWE);
Plaintext plaintextDec2;
cc->Decrypt(keys.secretKey, cTemp2, &plaintextDec2);
plaintextDec2->SetLength(slots);
std::cout << "\nSwitched decryption modulus_LWE mod " << NativeInteger(pLWE)
<< " works only for messages << p: " << plaintextDec2 << std::endl;
// Transform through arcsine
cTemp2 = cc->EvalFHEWtoCKKS(cFunc, slots, slots, 4, 0, 2);
cc->Decrypt(keys.secretKey, cTemp2, &plaintextDec2);
plaintextDec2->SetLength(slots);
std::cout << "Arcsin(switched result) * p/2pi gives the correct result if messages are < p/4: ";
for (uint32_t i = 0; i < slots; i++) {
double x = std::max(std::min(plaintextDec2->GetRealPackedValue()[i], 1.0), -1.0);
std::cout << std::asin(x) * pLWE / (2 * Pi) << " ";
}
std::cout << "\n";
But there is an error here:
Output precision is only wrt the operations in CKKS after switching back.
CKKS scheme is using ring dimension 2048, and number of slots 16
FHEW scheme is using lattice parameter 32, logQ 25, and modulus q 2048
terminate called after throwing an instance of 'lbcrypto::OpenFHEException'
what(): /openfhe-development/src/core/include/math/hal/intnat/ubintnat.h:l.481:DividedBy(): NativeIntegerT DividedBy: zero
May I ask how I can solve this problem?