Implement CKKS- coefficient encoding multiplication

I want to implement an inner product using CKKS coefficient encoding (i.e., encode vectors as polynomial coefficients, multiply polynomials, and extract the relevant coefficient).

Furthermore, I want to implement them in noise-flooding methods. I need to compute a polynomial like f(1/X) (reverse coefficients) and then multiply ciphertexts to obtain the inner product coefficient.

As a first step, I wrote the following test code to multiply two ciphertexts using coefficient encoding:

#include <iostream>
#include <vector>
#include "openfhe.h"
#include <cmath>
#include <algorithm>

using namespace lbcrypto;
using namespace std;

CryptoContext<DCRTPoly> generate_parameter(CCParams<CryptoContextCKKSRNS> &params){
    params.SetSecretKeyDist(UNIFORM_TERNARY);
    params.SetSecurityLevel(HEStd_128_classic);
    params.SetScalingModSize(50);
    params.SetFirstModSize(60);
    params.SetBatchSize(8);
    params.SetKeySwitchTechnique(HYBRID);
    params.SetExecutionMode(EXEC_EVALUATION);
    params.SetNoiseEstimate(19.0);
    params.SetRingDim(8192);
    params.SetDecryptionNoiseMode(NOISE_FLOODING_DECRYPT);
    params.SetMultiplicativeDepth(1);
    params.SetDesiredPrecision(20);
    params.SetScalingTechnique(FIXEDAUTO);
    
    CryptoContext<DCRTPoly> cc = GenCryptoContext(params);
    cc->Enable(PKE);
    cc->Enable(KEYSWITCH);
    cc->Enable(LEVELEDSHE);
    cc->Enable(ADVANCEDSHE);

    return cc;
}

void keySetting(KeyPair<DCRTPoly> &keys, CryptoContext<DCRTPoly> &cc){
    cc->EvalMultKeyGen(keys.secretKey);
}

int main() {
    CCParams<CryptoContextCKKSRNS> parameters;
    CryptoContext<DCRTPoly> cc = generate_parameter(parameters);
    auto keyPair = cc->KeyGen();
    keySetting(keyPair, cc);

    std::vector<int64_t> coef = {1,2,3,4,5,6,7,8};
    Plaintext pt = cc->MakeCoefPackedPlaintext(coef);

    auto ct = cc->Encrypt(keyPair.publicKey, pt);

    ct *= ct;

    Plaintext decrypted;
    cc->Decrypt(keyPair.secretKey, ct, &decrypted); 

    std::cout << "Decrypted (as plaintext): " << decrypted << std::endl;

    return 0;
}

However, when I build and run it, I get the following error:

terminate called after throwing an instance of 'lbcrypto::OpenFHEException'
  what():  /home/sandia/plaintext/openfhe-development/src/pke/lib/scheme/ckksrns/ckksrns-pke.cpp:l.67:Decrypt(): sizeQl 2!= 1. If sizeQl = 0, consider increasing the depth. If sizeQl > 1, check parameters (this is unsupported for NativePoly).
Aborted (core dumped)

My question is

  1. How can I fix this sizeQl 2 != 1 error in this context?
  2. Does the openfhe-development library include a function to compute a polynomial like f(1/X) (i.e., reversing the coefficients of a plaintext or ciphertext)?

MakeCoefPackedPlaintext is specifically designed for the finite-field case, i.e., where the coefficients are \bmod t, where t is the plaintext modulus. This encodng is used for BGV and BFV. When we deal with the coefficient encoding of CKKS, we use the notion of RLWE schemelet (which was only added in v1.4 to support functional CKKS bootstrapping from General Functional Bootstrapping using CKKS). In summary, this feature is not supported out of the box by OpenFHE.

Following up on this question, if we would like to leverage the RLWE schemelet (designed for FBT) in CKKS, to implement coefficient CKKS multiplication. Could you guide a starting point or a function we can leverage?

Logically speaking, I would first consider using BFV or BGV in OpenFHE if you just need the multiplication without functional bootstrapping. You can encode the plaintext using MakeCoeffPackedPlaintext. You can scale up real numbers using integers by multiplying them by 2^x or 10^y, where x and y would correspond to the desired precision. Then you would make sure the plaintext modulus is large enough for the result not to wrap around the plaintext modulus. If you need larger moduli, you can use plaintext CRT (see Big plaintext moduli - #2 by Caesar for more details).

If you need more than multiplication, i.e., you want to do functional bootstrapping followed by a CKKS coefficient multiplication, you can perform it right after the trigonometric Hermite evaluation, i.e., between steps 6 and 7 of Algorithm 1 in 2024/1623. You would have to create custom subroutines for this since this is not supported out of the box now.