Thank you for your detailed explanation! Based on your guidance, I can now perform leveled computations after the FBT. However, I encountered an issue when attempting to execute multiple FBTs sequentially with different functions.
In the following code, I first perform an identity function through FBT, followed by a leveled multiplication. Then, I adjust the modulus and perform a mod function through another FBT. However, I encountered an error when calling EvalHomDecoding after these operations. Specifically, the error occurs during the homomorphic decoding step, likely due to inconsistencies in the EvalFBTSetup phase (different functions) or modulus adjustments between the FBT operations. Could you please provide guidance on how to execute multiple FBTs sequentially with different functions?
Here is my code:
int main() {
FBTtest(QBFVINIT, BigInteger(256), BigInteger(16), (BigInteger(1) << 47), (BigInteger(1) << 47), 32, 1, 8, 4096);
return 0;
}
void FBTtest(BigInteger QBFVInit, BigInteger PInput, BigInteger POutput, BigInteger Q, BigInteger Bigq,
uint32_t scaleTHI, size_t order, uint32_t numSlots, uint32_t ringDim) {
auto numSlotsCKKS = numSlots;
std::vector<int64_t> x = {1, 2, 3, 4, 5, 6, 7, 8};
std::cerr << "First 8 elements of the input x :" << std::endl;
std::cerr << x << std::endl;
std::vector<int64_t> y = {1, 3, 1, 3, 1, 3, 1, 3};
std::cerr << "First 8 elements of the input y :" << std::endl;
std::cerr << y << std::endl;
std::vector<std::complex<double>> coeffcompidentity, coeffmod;
auto funcidentity = [](int64_t x) -> int64_t {
return x;
};
auto funcmod = [](int64_t x) -> int64_t {
return x % 16;
};
coeffcompidentity = GetHermiteTrigCoefficients(funcidentity, PInput.ConvertToInt(), order,
scaleTHI); // divided by 2
coeffmod = GetHermiteTrigCoefficients(funcmod, PInput.ConvertToInt(), order, scaleTHI);
uint32_t dcrtBits = Bigq.GetMSB() - 1;
uint32_t firstMod = Bigq.GetMSB() - 1;
uint32_t levelsAvailableAfterBootstrap = 0;
uint32_t levelsAvailableBeforeBootstrap = 0;
uint32_t dnum = 3;
SecretKeyDist secretKeyDist = SPARSE_ENCAPSULATED;
std::vector<uint32_t> lvlb = {3, 3};
auto levelsComputation = 1;
CCParams<CryptoContextCKKSRNS> parameters;
parameters.SetSecretKeyDist(secretKeyDist);
parameters.SetSecurityLevel(HEStd_NotSet);
parameters.SetScalingModSize(dcrtBits);
parameters.SetScalingTechnique(FIXEDMANUAL);
parameters.SetFirstModSize(firstMod);
parameters.SetNumLargeDigits(dnum);
parameters.SetRingDim(ringDim);
parameters.SetBatchSize(numSlotsCKKS);
uint32_t depth = levelsAvailableAfterBootstrap + lvlb[0] + lvlb[1] + 2 + levelsComputation;
bool flagBR = true;
depth += FHECKKSRNS::AdjustDepthFBT(coeffcompidentity, PInput, order, secretKeyDist);
parameters.SetMultiplicativeDepth(depth);
auto cc = GenCryptoContext(parameters);
cc->Enable(PKE);
cc->Enable(KEYSWITCH);
cc->Enable(LEVELEDSHE);
cc->Enable(ADVANCEDSHE);
cc->Enable(FHE);
std::cout << "CKKS scheme is using ring dimension " << cc->GetRingDimension() << " and a multiplicative depth of "
<< depth << std::endl
<< std::endl;
auto keyPair = cc->KeyGen();
cc->EvalFBTSetup(coeffcompidentity, numSlotsCKKS, PInput, POutput, Bigq, keyPair.publicKey, {0, 0}, lvlb,
levelsAvailableAfterBootstrap, levelsComputation, order);
cc->EvalBootstrapKeyGen(keyPair.secretKey, numSlotsCKKS);
cc->EvalMultKeyGen(keyPair.secretKey);
auto ep = SchemeletRLWEMP::GetElementParams(keyPair.secretKey, depth - (levelsAvailableBeforeBootstrap > 0));
auto cx_bfv = SchemeletRLWEMP::EncryptCoeff(x, QBFVInit, PInput, keyPair.secretKey, ep, flagBR);
auto cy_bfv = SchemeletRLWEMP::EncryptCoeff(y, QBFVInit, PInput, keyPair.secretKey, ep, flagBR);
Ciphertext<DCRTPoly> ctxtAfterFBT_id, ctxtAfterFBT_mod;
SchemeletRLWEMP::ModSwitch(cx_bfv, Q, QBFVInit);
SchemeletRLWEMP::ModSwitch(cy_bfv, Q, QBFVInit);
auto cx = SchemeletRLWEMP::convert(*cc, cx_bfv, keyPair.publicKey, Bigq, numSlotsCKKS,
depth - (levelsAvailableBeforeBootstrap > 0));
auto cy = SchemeletRLWEMP::convert(*cc, cy_bfv, keyPair.publicKey, Bigq, numSlotsCKKS,
depth - (levelsAvailableBeforeBootstrap > 0));
auto c_sub = cc->EvalSub(cx, cy);
ctxtAfterFBT_id = cc->EvalFBTNoDecoding(c_sub, coeffcompidentity, PInput.GetMSB() - 1, ep->GetModulus(), order);
auto csquare = cc->EvalSquare(ctxtAfterFBT_id);
cc->ModReduceInPlace(csquare);
auto csquare_decode = cc->EvalHomDecoding(csquare, scaleTHI * scaleTHI, 0);
auto polys = SchemeletRLWEMP::convert(csquare_decode, Q);
auto computed =
SchemeletRLWEMP::DecryptCoeff(polys, Q, POutput, keyPair.secretKey, ep, numSlotsCKKS, numSlotsCKKS, flagBR);
std::cerr << "FBT square decode result: [";
std::copy_n(computed.begin(), 8, std::ostream_iterator<int64_t>(std::cerr, " "));
std::cerr << "]" << std::endl;
SchemeletRLWEMP::ModSwitch(polys, Q, QBFVInit);
auto c_txt_mod = SchemeletRLWEMP::convert(*cc, polys, keyPair.publicKey, Bigq, numSlotsCKKS,
depth - (levelsAvailableBeforeBootstrap > 0));
ctxtAfterFBT_mod = cc->EvalFBTNoDecoding(c_txt_mod, coeffmod, PInput.GetMSB() - 1, ep->GetModulus(), order);
auto c_comp = cc->EvalHomDecoding(ctxtAfterFBT_mod, scaleTHI * scaleTHI, 0);
polys = SchemeletRLWEMP::convert(c_comp, Q);
computed =
SchemeletRLWEMP::DecryptCoeff(polys, Q, POutput, keyPair.secretKey, ep, numSlotsCKKS, numSlotsCKKS, flagBR);
for (int i = 0; i < 8; i++) {
std::cout << "(x-y)^2 %16 :" << static_cast<int>((x[i] - y[i]) * (x[i] - y[i])) % 16 << ", dec: " << computed[i]
<< std::endl;
}
}