The task is to modify the code sample to adjust the size of the random input between 100.0 and 500.0
//==================================================================================
// BSD 2-Clause License
//
// Copyright (c) 2014-2022, NJIT, Duality Technologies Inc. and other contributors
//
// All rights reserved.
//
// Author TPOC: contact@openfhe.org
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//==================================================================================
/*
Example for CKKS bootstrapping with sparse packing
*/
#define PROFILE
#include "openfhe.h"
using namespace lbcrypto;
void BootstrapExample(uint32_t numSlots);
int main(int argc, char* argv[]) {
// We run the example with 8 slots and ring dimension 4096 to illustrate how to run bootstrapping with a sparse plaintext.
// Using a sparse plaintext and specifying the smaller number of slots gives a performance improvement (typically up to 3x).
BootstrapExample(8);
}
void BootstrapExample(uint32_t numSlots) {
// Step 1: Set CryptoContext
CCParams<CryptoContextCKKSRNS> parameters;
// A. Specify main parameters
/* A1) Secret key distribution
* The secret key distribution for CKKS should either be SPARSE_TERNARY or UNIFORM_TERNARY.
* The SPARSE_TERNARY distribution was used in the original CKKS paper,
* but in this example, we use UNIFORM_TERNARY because this is included in the homomorphic
* encryption standard.
*/
SecretKeyDist secretKeyDist = UNIFORM_TERNARY;
parameters.SetSecretKeyDist(secretKeyDist);
/* A2) Desired security level based on FHE standards.
* In this example, we use the "NotSet" option, so the example can run more quickly with
* a smaller ring dimension. Note that this should be used only in
* non-production environments, or by experts who understand the security
* implications of their choices. In production-like environments, we recommend using
* HEStd_128_classic, HEStd_192_classic, or HEStd_256_classic for 128-bit, 192-bit,
* or 256-bit security, respectively. If you choose one of these as your security level,
* you do not need to set the ring dimension.
*/
parameters.SetSecurityLevel(HEStd_NotSet);
parameters.SetRingDim(1 << 12);
/* A3) Key switching parameters.
* By default, we use HYBRID key switching with a digit size of 3.
* Choosing a larger digit size can reduce complexity, but the size of keys will increase.
* Note that you can leave these lines of code out completely, since these are the default values.
*/
parameters.SetNumLargeDigits(3);
parameters.SetKeySwitchTechnique(HYBRID);
/* A4) Scaling parameters.
* By default, we set the modulus sizes and rescaling technique to the following values
* to obtain a good precision and performance tradeoff. We recommend keeping the parameters
* below unless you are an FHE expert.
*/
#if NATIVEINT == 128 && !defined(__EMSCRIPTEN__)
// Currently, only FIXEDMANUAL and FIXEDAUTO modes are supported for 128-bit CKKS bootstrapping.
ScalingTechnique rescaleTech = FIXEDAUTO;
usint dcrtBits = 78;
usint firstMod = 89;
#else
// All modes are supported for 64-bit CKKS bootstrapping.
ScalingTechnique rescaleTech = FLEXIBLEAUTO;
usint dcrtBits = 59;
usint firstMod = 60;
#endif
parameters.SetScalingModSize(dcrtBits);
parameters.SetScalingTechnique(rescaleTech);
parameters.SetFirstModSize(firstMod);
/* A4) Bootstrapping parameters.
* We set a budget for the number of levels we can consume in bootstrapping for encoding and decoding, respectively.
* Using larger numbers of levels reduces the complexity and number of rotation keys,
* but increases the depth required for bootstrapping.
* We must choose values smaller than ceil(log2(slots)). A level budget of {4, 4} is good for higher ring
* dimensions (65536 and higher).
*/
std::vector<uint32_t> levelBudget = {3, 3};
// We approximate the number of levels bootstrapping will consume to help set our initial multiplicative depth.
uint32_t approxBootstrapDepth = 8;
/* We give the user the option of configuring values for an optimization algorithm in bootstrapping.
* Here, we specify the giant step for the baby-step-giant-step algorithm in linear transforms
* for encoding and decoding, respectively. Either choose this to be a power of 2
* or an exact divisor of the number of slots. Setting it to have the default value of {0, 0} allows OpenFHE to choose
* the values automatically.
*/
std::vector<uint32_t> bsgsDim = {0, 0};
/* A5) Multiplicative depth.
* The goal of bootstrapping is to increase the number of available levels we have, or in other words,
* to dynamically increase the multiplicative depth. However, the bootstrapping procedure itself
* needs to consume a few levels to run. We compute the number of bootstrapping levels required
* using GetBootstrapDepth, and add it to levelsUsedBeforeBootstrap to set our initial multiplicative
* depth.
*/
uint32_t levelsUsedBeforeBootstrap = 10;
usint depth =
levelsUsedBeforeBootstrap + FHECKKSRNS::GetBootstrapDepth(approxBootstrapDepth, levelBudget, secretKeyDist);
parameters.SetMultiplicativeDepth(depth);
// Generate crypto context.
CryptoContext<DCRTPoly> cryptoContext = GenCryptoContext(parameters);
// Enable features that you wish to use. Note, we must enable FHE to use bootstrapping.
cryptoContext->Enable(PKE);
cryptoContext->Enable(KEYSWITCH);
cryptoContext->Enable(LEVELEDSHE);
cryptoContext->Enable(ADVANCEDSHE);
cryptoContext->Enable(FHE);
usint ringDim = cryptoContext->GetRingDimension();
std::cout << "CKKS scheme is using ring dimension " << ringDim << std::endl << std::endl;
// Step 2: Precomputations for bootstrapping
cryptoContext->EvalBootstrapSetup(levelBudget, bsgsDim, numSlots);
// Step 3: Key Generation
auto keyPair = cryptoContext->KeyGen();
cryptoContext->EvalMultKeyGen(keyPair.secretKey);
// Generate bootstrapping keys.
cryptoContext->EvalBootstrapKeyGen(keyPair.secretKey, numSlots);
// Step 4: Encoding and encryption of inputs
// Generate random input
std::vector<double> x;
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<> dis(100.0, 500.0);
for (size_t i = 0; i < numSlots; i++) {
x.push_back(dis(gen));
}
// Encoding as plaintexts
// We specify the number of slots as numSlots to achieve a performance improvement.
// We use the other default values of depth 1, levels 0, and no params.
// Alternatively, you can also set batch size as a parameter in the CryptoContext as follows:
// parameters.SetBatchSize(numSlots);
// Here, we assume all ciphertexts in the cryptoContext will have numSlots slots.
// We start with a depleted ciphertext that has used up all of its levels.
Plaintext ptxt = cryptoContext->MakeCKKSPackedPlaintext(x, 1, depth - 1, nullptr, numSlots);
ptxt->SetLength(numSlots);
std::cout << "Input: " << ptxt << std::endl;
// Encrypt the encoded vectors
Ciphertext<DCRTPoly> ciph = cryptoContext->Encrypt(keyPair.publicKey, ptxt);
std::cout << "Initial number of levels remaining: " << depth - ciph->GetLevel() << std::endl;
// Step 5: Perform the bootstrapping operation. The goal is to increase the number of levels remaining
// for HE computation.
auto ciphertextAfter = cryptoContext->EvalBootstrap(ciph);
std::cout << "Number of levels remaining after bootstrapping: " << depth - ciphertextAfter->GetLevel() << std::endl
<< std::endl;
// Step 7: Decryption and output
Plaintext result;
cryptoContext->Decrypt(keyPair.secretKey, ciphertextAfter, &result);
result->SetLength(numSlots);
std::cout << "Output after bootstrapping \n\t" << result << std::endl;
}
out put:
CKKS scheme is using ring dimension 4096
Input: (424.691, 267.531, 237.782, 129.829, 111.516, 305.156, 168.588, 294.903, … ); Estimated precision: 59 bits
Initial number of levels remaining: 1
Number of levels remaining after bootstrapping: 10
Output after bootstrapping
(419.045, 261.984, 232.179, 124.373, 106.045, 299.576, 163.091, 289.27, … ); Estimated precision: 26 bits