# Combination of multiple \`EvalLinearTransform\` and \`RescaleInPlace\` results in "Modulus missmatch"

**URL:** https://openfhe.discourse.group/t/combination-of-multiple-evallineartransform-and-rescaleinplace-results-in-modulus-missmatch/1245
**Category:** Library Questions
**Created:** [May 1, 2024, 11:01am UTC](https://openfhe.discourse.group/t/combination-of-multiple-evallineartransform-and-rescaleinplace-results-in-modulus-missmatch/1245 "2024-05-01T11:01:57Z")
**Posts on this page:** 2
**Page:** 1

<div class="post-metadata">

### Author: ![Arseniy](https://avatars.discourse-cdn.com/v4/letter/a/f9ae1b/32.png) [@Arseniy](https://openfhe.discourse.group/u/Arseniy)
#### Post date: [May 1, 2024, 11:01am UTC](https://openfhe.discourse.group/t/combination-of-multiple-evallineartransform-and-rescaleinplace-results-in-modulus-missmatch/1245/1 "2024-05-01T11:01:57Z")

</div>

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:

```auto
#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:

```auto
$ 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

```

---

<div class="post-metadata">

### Author: ![ypolyakov](https://yyz1.discourse-cdn.com/flex031/user_avatar/openfhe.discourse.group/ypolyakov/32/47_2.png) [@ypolyakov](https://openfhe.discourse.group/u/ypolyakov)
#### Post date: [May 3, 2024, 10:00pm UTC](https://openfhe.discourse.group/t/combination-of-multiple-evallineartransform-and-rescaleinplace-results-in-modulus-missmatch/1245/2 "2024-05-03T22:00:29Z")

</div>

OpenFHE provides four different modes for CKKS: `FIXEDMANUAL`, 'FIXEDAUTO, `FLEXIBLEAUTO`, `FLEXIBLEAUTOEXT`. All AUTO modes run rescaling automatically. So you should not need to worry about manually doing it. Also, for CKKS bootstrapping, I recommend using `FLEXIBLEAUTO` which provides the best accuracy (and faster than `FLEXIBLEAUTOEXT`).
