Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Composite Scaling CKKS Bootstrapping (#910 phase 3) #931

Merged
merged 41 commits into from
Mar 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
3ed5909
Initial commit: adding composite scaling parameters and scaling tech …
fdiasmor Dec 12, 2024
fb50d79
Initial commit: adding composite scaling parameters and scaling tech …
fdiasmor Dec 12, 2024
d3c1a58
Composite prime generation and adaptive scaling factors.
fdiasmor Dec 16, 2024
e397a73
Propagating composite degree to mod reduce calls.
fdiasmor Dec 17, 2024
3bf393a
Handling overflow for scaling factors over 64 bits on composite scali…
fdiasmor Dec 17, 2024
51db3ca
Bug fix of overflow handling for d > 2 and sc > 64.
fdiasmor Dec 18, 2024
44b206b
Update impl of Rescale and Mod/LevelReduce methods with composite deg…
fdiasmor Dec 18, 2024
3d2c220
Minor bug fix: else case missing.
fdiasmor Dec 19, 2024
a9c9978
Update EvalPoly functions to use composite degree.
fdiasmor Dec 19, 2024
c03c8f9
Update Compress function to raise error.
fdiasmor Dec 19, 2024
470cd11
Update/Add new composite scaling examples.
fdiasmor Dec 19, 2024
6369eaa
Recalculate sizeP and update EstimateLogP interface.
fdiasmor Dec 19, 2024
c29fc6b
Updated CKKS bootstrapping for composite scaling with examples.
fdiasmor Dec 20, 2024
fde39d7
Update error conditions.
fdiasmor Dec 21, 2024
58881c0
Update poly evaluation example.
fdiasmor Dec 21, 2024
d257f15
Tweak error condition regarding prime moduli size.
fdiasmor Dec 21, 2024
5d54a9f
Update composite-prime generation function.
fdiasmor Dec 31, 2024
a5a2b9c
Remove comments from prime gen function.
fdiasmor Jan 3, 2025
9a7ad26
Update composite scaling unittests and fix unittest runtime issue.
fdiasmor Dec 25, 2024
c8c76a8
Some review changes.
fdiasmor Jan 8, 2025
4b721d9
Update composite scaling unittests and fix unittest runtime issue.
fdiasmor Dec 25, 2024
cf685de
Disable composite scaling set methods for non-CKKS schemes.
fdiasmor Jan 9, 2025
a5a443a
Move COMPOSITESCALING support error handling.
fdiasmor Jan 10, 2025
b14472d
Disable composite scaling set methods for non-CKKS schemes.
fdiasmor Jan 9, 2025
6dd35c0
Removing additional constructor that accepts composite scaling parame…
fdiasmor Jan 10, 2025
e9cdffa
Merge/rebase review changes from (#928).
fdiasmor Jan 18, 2025
80fe03f
Merge branch 'dev' into 910-composite-prime-gen
fdiasmor Jan 18, 2025
c63d0fe
Review changes for PR 910 phase 2.
fdiasmor Feb 5, 2025
2ec7adf
Review updates and fixes.
fdiasmor Feb 6, 2025
abd4585
Review updates and fixes.
fdiasmor Feb 6, 2025
d3208fa
Review updates and fixes.
fdiasmor Feb 6, 2025
3fdb02e
Rebase with latest revised changes.
fdiasmor Feb 20, 2025
13424f2
Merge branch 'dev' into 910-bootstrapping-composite-scaling
fdiasmor Feb 20, 2025
044ba22
Update error message.
fdiasmor Feb 20, 2025
28e7a1a
Update error message.
fdiasmor Feb 20, 2025
e54a469
Code improvements
dsuponitskiy-duality Mar 3, 2025
9e0eaba
Avoid infinite loop in next prime sampling.
fdiasmor Mar 5, 2025
cdb9d69
Fix decryption failed bug.
fdiasmor Mar 7, 2025
13a3236
Replace std::round(std::log2()) by std::frexp to get bit size of qDou…
fdiasmor Mar 7, 2025
2a7477c
Update error message.
fdiasmor Mar 11, 2025
d7402eb
Code improvements
dsuponitskiy-duality Mar 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 202 additions & 0 deletions src/pke/examples/iterative-composite-scaling-bootstrapping.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
//==================================================================================
// 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 multiple iterations of CKKS bootstrapping to improve precision. Note that you need to run a
single iteration of bootstrapping first, to measure the precision. Then, you can input the measured
precision as a parameter to EvalBootstrap with multiple iterations. With 2 iterations, you can achieve
double the precision of a single bootstrapping.

* Source: Bae Y., Cheon J., Cho W., Kim J., and Kim T. META-BTS: Bootstrapping Precision
* Beyond the Limit. Cryptology ePrint Archive, Report
* 2022/1167. (https://eprint.iacr.org/2022/1167.pdf)

*/

#define PROFILE

#include "openfhe.h"

#include <vector>
#include <iostream>

using namespace lbcrypto;

void IterativeBootstrapExample();

int main(int argc, char* argv[]) {
// We run the example with 8 slots and ring dimension 4096.
IterativeBootstrapExample();
}

// CalculateApproximationError() calculates the precision number (or approximation error).
// The higher the precision, the less the error.
double CalculateApproximationError(const std::vector<std::complex<double>>& result,
const std::vector<std::complex<double>>& expectedResult) {
if (result.size() != expectedResult.size())
OPENFHE_THROW("Cannot compare vectors with different numbers of elements");

// using the infinity norm
double maxError = 0;
for (size_t i = 0; i < result.size(); ++i) {
double error = std::abs(result[i].real() - expectedResult[i].real());
if (maxError < error)
maxError = error;
}

return std::abs(std::log2(maxError));
}

void IterativeBootstrapExample() {
// Step 1: Set CryptoContext
CCParams<CryptoContextCKKSRNS> parameters;
SecretKeyDist secretKeyDist = UNIFORM_TERNARY;
parameters.SetSecretKeyDist(secretKeyDist);
parameters.SetSecurityLevel(HEStd_NotSet);
parameters.SetRingDim(1 << 7);

// All modes are supported for 64-bit CKKS bootstrapping.
ScalingTechnique rescaleTech = COMPOSITESCALINGAUTO;
usint dcrtBits = 61;
usint firstMod = 66;
usint registerWordSize = 27;

parameters.SetScalingModSize(dcrtBits);
parameters.SetScalingTechnique(rescaleTech);
parameters.SetFirstModSize(firstMod);
parameters.SetRegisterWordSize(registerWordSize);

// Here, we specify the number of iterations to run bootstrapping. Note that we currently only support 1 or 2 iterations.
// Two iterations should give us approximately double the precision of one iteration.
uint32_t numIterations = 2;

std::vector<uint32_t> levelBudget = {3, 3};
// Each extra iteration on top of 1 requires an extra level to be consumed.
uint32_t approxBootstrapDepth = 8 + (numIterations - 1);
std::vector<uint32_t> bsgsDim = {0, 0};

uint32_t levelsAvailableAfterBootstrap = 10;
// usint depth =
// levelsAvailableAfterBootstrap + FHECKKSRNS::GetBootstrapDepth(levelBudget, secretKeyDist) + (numIterations - 1);
usint depth =
levelsAvailableAfterBootstrap + 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;

const auto cryptoParamsCKKSRNS =
std::dynamic_pointer_cast<CryptoParametersCKKSRNS>(cryptoContext->GetCryptoParameters());
usint compositeDegree = cryptoParamsCKKSRNS->GetCompositeDegree();
std::cout << "compositeDegree=" << cryptoParamsCKKSRNS->GetCompositeDegree()
<< " modBitWidth=" << static_cast<float>(dcrtBits) / compositeDegree
<< " targetHWArchWordSize=" << registerWordSize << std::endl;

// Step 2: Precomputations for bootstrapping
// We use a sparse packing.
// uint32_t numSlots = 8;
// We use a full packing.
uint32_t numSlots = cryptoContext->GetCyclotomicOrder() / 4;
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(0.0, 1.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, compositeDegree * (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);

// Step 5: Measure the precision of a single bootstrapping operation.
auto ciphertextAfter = cryptoContext->EvalBootstrap(ciph);

Plaintext result;
cryptoContext->Decrypt(keyPair.secretKey, ciphertextAfter, &result);
result->SetLength(numSlots);
uint32_t precision =
std::floor(CalculateApproximationError(result->GetCKKSPackedValue(), ptxt->GetCKKSPackedValue()));
std::cout << "Bootstrapping precision after 1 iteration: " << precision << std::endl;

// Set precision equal to empirically measured value after many test runs.
precision = 7;
std::cout << "Precision input to algorithm: " << precision << std::endl;

// Step 6: Run bootstrapping with multiple iterations.
auto ciphertextTwoIterations = cryptoContext->EvalBootstrap(ciph, numIterations, precision);

Plaintext resultTwoIterations;
cryptoContext->Decrypt(keyPair.secretKey, ciphertextTwoIterations, &resultTwoIterations);
result->SetLength(numSlots);
auto actualResult = resultTwoIterations->GetCKKSPackedValue();

std::cout << "Output after two iterations of bootstrapping: " << actualResult << std::endl;
double precisionMultipleIterations = CalculateApproximationError(actualResult, ptxt->GetCKKSPackedValue());

// Output the precision of bootstrapping after two iterations. It should be approximately double the original precision.
std::cout << "Bootstrapping precision after 2 iterations: " << precisionMultipleIterations << std::endl;
std::cout << "Number of levels remaining after 2 bootstrappings: "
<< compositeDegree * depth - ciphertextTwoIterations->GetLevel() << std::endl;
// << compositeDegree * depth - ciphertextTwoIterations->GetLevel() - (ciphertextTwoIterations->GetNoiseScaleDeg() - 1)
// << std::endl;
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ int main(int argc, char* argv[]) {
std::cout << "\n======EXAMPLE FOR EVALPOLY========\n" << std::endl;

uint32_t multDepth = 6;
int argcCount = 0;
int argcCount = 1;
if (argc > 1) {
while (argcCount < argc) {
uint32_t paramValue = atoi(argv[argcCount]);
Expand All @@ -136,9 +136,9 @@ int main(int argc, char* argv[]) {
break;
}
argcCount += 1;
std::cout << "argcCount: " << argcCount << std::endl;
}
std::cout << "Complete !" << std::endl;

std::cout << "Completed reading input parameters!" << std::endl;
}
else {
std::cout << "Using default parameters" << std::endl;
Expand Down Expand Up @@ -166,9 +166,12 @@ int main(int argc, char* argv[]) {

const auto cryptoParamsCKKSRNS = std::dynamic_pointer_cast<CryptoParametersCKKSRNS>(cc->GetCryptoParameters());
uint32_t compositeDegree = cryptoParamsCKKSRNS->GetCompositeDegree();
std::cout << "Composite Degree: " << compositeDegree << "\nPrime Moduli Bit Length: "

std::cout << "-----------------------------------------------------------------" << std::endl;
std::cout << "Composite Degree: " << compositeDegree << "\nPrime Moduli Size: "
<< static_cast<float>(scalingModSize) / cryptoParamsCKKSRNS->GetCompositeDegree()
<< "\nTarget HW Arch Word Size: " << registerWordSize << std::endl;
<< "\nRegister Word Size: " << registerWordSize << std::endl;
std::cout << "-----------------------------------------------------------------" << std::endl;

std::vector<std::complex<double>> input({0.5, 0.7, 0.9, 0.95, 0.93});

Expand All @@ -178,9 +181,7 @@ int main(int argc, char* argv[]) {
std::vector<double> coefficients2({1, 2, 3, 4, 5, -1, -2, -3, -4, -5,
0.1, 0.2, 0.3, 0.4, 0.5, -0.1, -0.2, -0.3, -0.4, -0.5,
0.1, 0.2, 0.3, 0.4, 0.5, -0.1, -0.2, -0.3, -0.4, -0.5});
// std::vector<double> coefficients2({0, 0, 0, 0, 0, -0, -0, -0, -0, -0,
// 0., 0., 0., 0., 0., -0., -0., -0., -0., -0.,
// 0., 0., 0., 0., 0., -0., -0., -0., -0., -0.});

Plaintext plaintext1 = cc->MakeCKKSPackedPlaintext(input);

auto keyPair = cc->KeyGen();
Expand Down
Loading