From 57fcc63b071b930a5bf4dc176ce5d74bbdb96eb9 Mon Sep 17 00:00:00 2001 From: Dmitriy Suponitskiy Date: Tue, 6 Jan 2026 10:05:04 -0500 Subject: [PATCH 1/7] Updated bindings --- ...interactive-mp-bootstrapping-Chebyschev.py | 2 +- .../pke/tckks-interactive-mp-bootstrapping.py | 2 +- src/include/docstrings/cryptocontext_docs.h | 8 ---- src/lib/bindings.cpp | 45 ++++++++++++++----- 4 files changed, 35 insertions(+), 22 deletions(-) diff --git a/examples/pke/tckks-interactive-mp-bootstrapping-Chebyschev.py b/examples/pke/tckks-interactive-mp-bootstrapping-Chebyschev.py index 9a9f848..00e74a5 100644 --- a/examples/pke/tckks-interactive-mp-bootstrapping-Chebyschev.py +++ b/examples/pke/tckks-interactive-mp-bootstrapping-Chebyschev.py @@ -49,7 +49,7 @@ def TCKKSCollectiveBoot(scaleTech): batchSize = 16 # Adjust batch size if needed parameters.SetBatchSize(batchSize) - compressionLevel = COMPRESSION_LEVEL.COMPACT # or COMPRESSION_LEVEL.SLACK + compressionLevel = CompressionLevel.COMPACT # or CompressionLevel.SLACK parameters.SetInteractiveBootCompressionLevel(compressionLevel) cryptoContext = GenCryptoContext(parameters) diff --git a/examples/pke/tckks-interactive-mp-bootstrapping.py b/examples/pke/tckks-interactive-mp-bootstrapping.py index 762263a..8a97770 100644 --- a/examples/pke/tckks-interactive-mp-bootstrapping.py +++ b/examples/pke/tckks-interactive-mp-bootstrapping.py @@ -57,7 +57,7 @@ def TCKKSCollectiveBoot(scaleTech): batchSize = 4 parameters.SetBatchSize(batchSize) - compressionLevel = COMPRESSION_LEVEL.SLACK + compressionLevel = CompressionLevel.SLACK parameters.SetInteractiveBootCompressionLevel(compressionLevel) cryptoContext = GenCryptoContext(parameters) diff --git a/src/include/docstrings/cryptocontext_docs.h b/src/include/docstrings/cryptocontext_docs.h index a7571a0..5508cad 100644 --- a/src/include/docstrings/cryptocontext_docs.h +++ b/src/include/docstrings/cryptocontext_docs.h @@ -123,8 +123,6 @@ const char* cc_EvalRotateKeyGen_docs = R"pbdoc( :type privateKey: PrivateKey :param indexList: list of integers representing the indices :type indexList: list - :param publicKey: public key (used in NTRU schemes) - :type publicKey: PublicKey )pbdoc"; const char* cc_MakeStringPlaintext_docs = R"pbdoc( @@ -305,8 +303,6 @@ const char* cc_EvalAtIndexKeyGen_docs = R"pbdoc( :type privateKey: PrivateKey :param indexList: list of indices :type indexList: list - :param publicKey: the public key (used in NTRU schemes). Not used anymore. - :type publicKey: PublicKey :return: None )pbdoc"; @@ -785,8 +781,6 @@ const char* cc_EvalSumKeyGen_docs = R"pbdoc( :param privateKey: private key :type privateKey: PrivateKey - :param publicKey: public key (used in NTRU schemes) - :type publicKey: PublicKey :return: None )pbdoc"; @@ -795,8 +789,6 @@ const char* cc_EvalSumRowsKeyGen_docs = R"pbdoc( :param privateKey: Private key used for key generation. :type privateKey: PrivateKey - :param publicKey: Public key (used in NTRU schemes; unused now). - :type publicKey: PublicKey :param rowSize: Number of slots per row in the packed matrix. :type rowSize: int :param subringDim: Subring dimension (use cyclotomic order if 0). diff --git a/src/lib/bindings.cpp b/src/lib/bindings.cpp index 094f09a..0f24335 100644 --- a/src/lib/bindings.cpp +++ b/src/lib/bindings.cpp @@ -344,7 +344,6 @@ void bind_crypto_context(py::module &m) { .def("EvalRotateKeyGen", &CryptoContextImpl::EvalRotateKeyGen, py::arg("privateKey"), py::arg("indexList"), - py::arg("publicKey") = py::none(), py::doc(cc_EvalRotateKeyGen_docs)) .def("MakeStringPlaintext", &CryptoContextImpl::MakeStringPlaintext, py::arg("str"), @@ -424,7 +423,6 @@ void bind_crypto_context(py::module &m) { .def("EvalAtIndexKeyGen", &CryptoContextImpl::EvalAtIndexKeyGen, py::arg("privateKey"), py::arg("indexList"), - py::arg("publicKey") = py::none(), py::doc(cc_EvalAtIndexKeyGen_docs)) .def("EvalAtIndex", &CryptoContextImpl::EvalAtIndex, py::arg("ciphertext"), @@ -692,18 +690,41 @@ void bind_crypto_context(py::module &m) { py::doc(cc_EvalDivide_docs)) .def("EvalSumKeyGen", &CryptoContextImpl::EvalSumKeyGen, py::arg("privateKey"), - py::arg("publicKey") = py::none(), py::doc(cc_EvalSumKeyGen_docs)) - //TODO (Oliveira, R.): Solve pointer handling bug when dealing with EvalKeyMap object for the next functions - .def("EvalSumRowsKeyGen", &CryptoContextImpl::EvalSumRowsKeyGen, + .def("EvalSumRowsKeyGen", + [](CryptoContextImpl& self, + const PrivateKey privateKey, + uint32_t rowSize, + uint32_t subringDim) { + auto m = self.EvalSumRowsKeyGen(privateKey, rowSize, subringDim); + py::dict d; + for (const auto& [k, v] : *m) + d[py::int_(k)] = v; + return d; + }, py::arg("privateKey"), - py::arg("publicKey") = py::none(), py::arg("rowSize") = 0, py::arg("subringDim") = 0, py::doc(cc_EvalSumRowsKeyGen_docs)) + .def("EvalSumRowsKeyGen", + [](CryptoContextImpl& self, + const PrivateKey privateKey, + const PublicKey publicKey, + uint32_t rowSize, + uint32_t subringDim) { + auto m = self.EvalSumRowsKeyGen(privateKey, publicKey, rowSize, subringDim); + py::dict d; + for (const auto& [k, v] : *m) + d[py::int_(k)] = v; + return d; + }, + py::arg("privateKey"), + py::arg("publicKey"), + py::arg("rowSize") = 0, + py::arg("subringDim") = 0) + // TODO (Oliveira, R.): Solve pointer handling bug when dealing with EvalKeyMap object for the next functions .def("EvalSumColsKeyGen", &CryptoContextImpl::EvalSumColsKeyGen, py::arg("privateKey"), - py::arg("publicKey") = py::none(), py::doc(cc_EvalSumColsKeyGen_docs)) .def("EvalSum", &CryptoContextImpl::EvalSum, py::arg("ciphertext"), @@ -1294,11 +1315,11 @@ void bind_enums_and_constants(py::module &m) { m.attr("HPSPOVERQLEVELED") = py::cast(MultiplicationTechnique::HPSPOVERQLEVELED); // Compression Leval - py::enum_(m,"COMPRESSION_LEVEL") - .value("COMPACT", COMPRESSION_LEVEL::COMPACT) - .value("SLACK", COMPRESSION_LEVEL::SLACK); - m.attr("COMPACT") = py::cast(COMPRESSION_LEVEL::COMPACT); - m.attr("SLACK") = py::cast(COMPRESSION_LEVEL::SLACK); + py::enum_(m,"CompressionLevel") + .value("COMPACT", CompressionLevel::COMPACT) + .value("SLACK", CompressionLevel::SLACK); + m.attr("COMPACT") = py::cast(CompressionLevel::COMPACT); + m.attr("SLACK") = py::cast(CompressionLevel::SLACK); py::enum_(m,"CKKSDataType") .value("REAL", CKKSDataType::REAL) From 589da801c2ce694bcfc651cd34fd5e373ad78239 Mon Sep 17 00:00:00 2001 From: Dmitriy Suponitskiy Date: Mon, 2 Feb 2026 07:56:26 -0500 Subject: [PATCH 2/7] Bindings for 2 overloads of IntMPBootRandomElementGen() --- src/include/docstrings/cryptocontext_docs.h | 11 ++++++++- src/lib/bindings.cpp | 27 ++++++++++++++++----- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/include/docstrings/cryptocontext_docs.h b/src/include/docstrings/cryptocontext_docs.h index 5508cad..4738c2f 100644 --- a/src/include/docstrings/cryptocontext_docs.h +++ b/src/include/docstrings/cryptocontext_docs.h @@ -1115,7 +1115,7 @@ const char* cc_IntMPBootAdjustScale_docs = R"pbdoc( :rtype: Ciphertext )pbdoc"; -const char* cc_IntMPBootRandomElementGen_docs = R"pbdoc( +const char* cc_IntMPBootRandomElementGenPublicKey_docs = R"pbdoc( Threshold FHE: Generate a common random polynomial for Multi-Party Interactive Bootstrapping :param publicKey: the scheme public key (you can also provide the lead party's public-key) @@ -1124,6 +1124,15 @@ const char* cc_IntMPBootRandomElementGen_docs = R"pbdoc( :rtype: Ciphertext )pbdoc"; +const char* cc_IntMPBootRandomElementGenCiphertext_docs = R"pbdoc( + Threshold FHE: Generate a common random polynomial for Multi-Party Interactive Bootstrapping + + :param ciphertext: reference ciphertext used to derive cryptographic parameters + :type ciphertext: Ciphertext + :return: Resulting ring element + :rtype: Ciphertext +)pbdoc"; + const char* cc_IntMPBootDecrypt_docs = R"pbdoc( Threshold FHE: Does masked decryption as part of Multi-Party Interactive Bootstrapping. Each party calls this function as part of the protocol diff --git a/src/lib/bindings.cpp b/src/lib/bindings.cpp index 0f24335..4424c7a 100644 --- a/src/lib/bindings.cpp +++ b/src/lib/bindings.cpp @@ -232,6 +232,7 @@ void bind_crypto_context(py::module &m) { py::class_(m, "ParmType"); auto cc_class = py::class_, std::shared_ptr>>(m, "CryptoContext"); + using CC = CryptoContextImpl; cc_class.def(py::init<>()) .def("GetKeyGenLevel", &CryptoContextImpl::GetKeyGenLevel, cc_GetKeyGenLevel_docs) @@ -840,9 +841,22 @@ void bind_crypto_context(py::module &m) { .def("IntMPBootAdjustScale",&CryptoContextImpl::IntMPBootAdjustScale, py::arg("ciphertext"), py::doc(cc_IntMPBootAdjustScale_docs)) - .def("IntMPBootRandomElementGen", &CryptoContextImpl::IntMPBootRandomElementGen, + // .def("IntMPBootRandomElementGen", + // py::overload_cast>(&CryptoContextImpl::IntMPBootRandomElementGen, py::const_), + // py::arg("publicKey"), + // py::doc(cc_IntMPBootRandomElementGen_from_publickey_docs)) + // .def("IntMPBootRandomElementGen", + // py::overload_cast&>(&CryptoContextImpl::IntMPBootRandomElementGen, py::const_), + // py::arg("ciphertext"), + // py::doc(cc_IntMPBootRandomElementGen_from_ciphertext_docs)) + .def("IntMPBootRandomElementGen", + static_cast (CC::*)(PublicKey) const>(&CC::IntMPBootRandomElementGen), py::arg("publicKey"), - py::doc(cc_IntMPBootRandomElementGen_docs)) + py::doc(cc_IntMPBootRandomElementGenPublicKey_docs)) + .def("IntMPBootRandomElementGen", + static_cast (CC::*)(ConstCiphertext&) const>(&CC::IntMPBootRandomElementGen), + py::arg("ciphertext"), + py::doc(cc_IntMPBootRandomElementGenCiphertext_docs)) .def("IntMPBootDecrypt", &CryptoContextImpl::IntMPBootDecrypt, py::arg("privateKey"), py::arg("ciphertext"), @@ -898,6 +912,7 @@ void bind_crypto_context(py::module &m) { py::arg("slots") = 0, py::arg("correctionFactor") = 0, py::arg("precompute")= true, + py::arg("BTSlotsEncoding")= false, py::doc(cc_EvalBootstrapSetup_docs)) .def("EvalBootstrapKeyGen", &CryptoContextImpl::EvalBootstrapKeyGen, py::arg("privateKey"), @@ -1610,19 +1625,19 @@ void bind_sch_swch_params(py::module &m) { } void bind_utils(py::module& m) { - m.def("EnablePrecomputeCRTTablesAfterDeserializaton", &lbcrypto::EnablePrecomputeCRTTablesAfterDeserializaton, + m.def("EnablePrecomputeCRTTablesAfterDeserializaton", &EnablePrecomputeCRTTablesAfterDeserializaton, py::doc("Enable CRT precomputation after deserialization")); - m.def("DisablePrecomputeCRTTablesAfterDeserializaton", &lbcrypto::DisablePrecomputeCRTTablesAfterDeserializaton, + m.def("DisablePrecomputeCRTTablesAfterDeserializaton", &DisablePrecomputeCRTTablesAfterDeserializaton, py::doc("Disable CRT precomputation after deserialization")); } void bind_free_functions(py::module& m) { - m.def("EvalChebyshevCoefficients", &lbcrypto::EvalChebyshevCoefficients, + m.def("EvalChebyshevCoefficients", &EvalChebyshevCoefficients, py::arg("func"), py::arg("a"), py::arg("b"), py::arg("degree")); - m.def("EvalChebyshevFunctionPtxt", &lbcrypto::EvalChebyshevFunctionPtxt, + m.def("EvalChebyshevFunctionPtxt", &EvalChebyshevFunctionPtxt, py::arg("func"), py::arg("ptxt"), py::arg("a"), From 2b8ac0d2b4d10a2ba1ef675fb92fb1b40dae8e5d Mon Sep 17 00:00:00 2001 From: Dmitriy Suponitskiy Date: Mon, 2 Feb 2026 13:22:39 -0500 Subject: [PATCH 3/7] Bindings for EvalMultInPlace() --- src/lib/bindings.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/lib/bindings.cpp b/src/lib/bindings.cpp index 4424c7a..30af383 100644 --- a/src/lib/bindings.cpp +++ b/src/lib/bindings.cpp @@ -610,6 +610,26 @@ void bind_crypto_context(py::module &m) { py::arg("scalar"), py::arg("ciphertext"), py::doc("")) // TODO (dsuponit): replace this with an actual docstring + .def("EvalMultInPlace", + static_cast&, double) const>(&CC::EvalMultInPlace), + py::arg("ciphertext"), + py::arg("scalar"), + py::doc("")) // TODO (dsuponit): replace this with an actual docstring + .def("EvalMultInPlace", + static_cast&) const>(&CC::EvalMultInPlace), + py::arg("scalar"), + py::arg("ciphertext"), + py::doc("")) // TODO (dsuponit): replace this with an actual docstring + .def("EvalMultInPlace", + static_cast&, std::complex) const>(&CC::EvalMultInPlace), + py::arg("ciphertext"), + py::arg("scalar"), + py::doc("")) // TODO (dsuponit): replace this with an actual docstring + .def("EvalMultInPlace", + static_cast, Ciphertext&) const>(&CC::EvalMultInPlace), + py::arg("scalar"), + py::arg("ciphertext"), + py::doc("")) // TODO (dsuponit): replace this with an actual docstring .def("EvalMultMutable", py::overload_cast&, Ciphertext&>(&CryptoContextImpl::EvalMultMutable, py::const_), py::arg("ciphertext1"), @@ -841,6 +861,11 @@ void bind_crypto_context(py::module &m) { .def("IntMPBootAdjustScale",&CryptoContextImpl::IntMPBootAdjustScale, py::arg("ciphertext"), py::doc(cc_IntMPBootAdjustScale_docs)) + ///////////////////////////////////////////////////////// + // static_cast seems to be better than py::overload_cast for overloaded functions (even it doesn't look pretty): + // 1. compiler tells your exactly what doesn’t match + // 2. never silently selects the wrong overload if only one is visible + ///////////////////////////////////////////////////////// // .def("IntMPBootRandomElementGen", // py::overload_cast>(&CryptoContextImpl::IntMPBootRandomElementGen, py::const_), // py::arg("publicKey"), From ccf87212ff3e2a1f7e87422475a9852e8eb6db91 Mon Sep 17 00:00:00 2001 From: Dmitriy Suponitskiy Date: Tue, 17 Feb 2026 23:14:25 -0500 Subject: [PATCH 4/7] Additional bindings and added ..._bootstrap_stc_example() to iterative-ckks-bootstrapping.py and simple-ckks-bootstrapping.py --- examples/pke/iterative-ckks-bootstrapping.py | 138 ++++++++++++++++++- examples/pke/simple-ckks-bootstrapping.py | 68 +++++++++ src/lib/bindings.cpp | 24 ++-- 3 files changed, 221 insertions(+), 9 deletions(-) diff --git a/examples/pke/iterative-ckks-bootstrapping.py b/examples/pke/iterative-ckks-bootstrapping.py index 360b350..6f5b156 100644 --- a/examples/pke/iterative-ckks-bootstrapping.py +++ b/examples/pke/iterative-ckks-bootstrapping.py @@ -4,8 +4,9 @@ def main(): iterative_bootstrap_example() + iterative_bootstrap_stc_example() -def calculate_approximation_error(result,expected_result): +def calculate_approximation_error(result, expected_result): if len(result) != len(expected_result): raise Exception("Cannot compare vectors with different numbers of elements") # using the infinity norm @@ -13,6 +14,7 @@ def calculate_approximation_error(result,expected_result): max_error = max([abs(el1.real - el2.real) for (el1, el2) in zip(result, expected_result)]) # return absolute value of log base2 of the error return abs(math.log(max_error,2)) + def iterative_bootstrap_example(): # Step 1: Set CryptoContext parameters = CCParamsCKKSRNS() @@ -114,5 +116,139 @@ def iterative_bootstrap_example(): print(f"Bootstrapping precision after 2 iterations: {precision_multiple_iterations} bits\n") print(f"Number of levels remaining after 2 bootstrappings: {depth - ciphertext_two_iterations.GetLevel()}\n") +def iterative_bootstrap_stc_example(): + # Step 1: Set CryptoContext + parameters = CCParamsCKKSRNS() + secret_key_dist = SecretKeyDist.UNIFORM_TERNARY + parameters.SetSecretKeyDist(secret_key_dist) + parameters.SetSecurityLevel(SecurityLevel.HEStd_NotSet) + parameters.SetRingDim(1 << 12) + + if get_native_int()==128: + rescale_tech = ScalingTechnique.FIXEDAUTO + dcrt_bits = 78 + first_mod = 89 + else: + rescale_tech = ScalingTechnique.FLEXIBLEAUTO + dcrt_bits = 59 + first_mod = 60 + + parameters.SetScalingModSize(dcrt_bits) + parameters.SetScalingTechnique(rescale_tech) + parameters.SetFirstModSize(first_mod) + + # 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. + num_iterations = 2 + + level_budget = [3, 3] + bsgs_dim = [0,0] + + levels_available_after_bootstrap = 10 + level_budget[1] + depth = levels_available_after_bootstrap + FHECKKSRNS.GetBootstrapDepth(9, level_budget, secret_key_dist) + parameters.SetMultiplicativeDepth(depth) + + # Generate crypto context + cryptocontext = GenCryptoContext(parameters) + + # Enable features that you wish to use. Note, we must enable FHE to use bootstrapping. + + cryptocontext.Enable(PKESchemeFeature.PKE) + cryptocontext.Enable(PKESchemeFeature.KEYSWITCH) + cryptocontext.Enable(PKESchemeFeature.LEVELEDSHE) + cryptocontext.Enable(PKESchemeFeature.ADVANCEDSHE) + cryptocontext.Enable(PKESchemeFeature.FHE) + + ring_dim = cryptocontext.GetRingDimension() + print(f"CKKS is using ring dimension {ring_dim}\n\n") + + # Step 2: Precomputations for bootstrapping + # We use a sparse packing + num_slots = 8 + cryptocontext.EvalBootstrapSetup(level_budget, bsgs_dim, num_slots, 0, True, True) + + # Step 3: Key generation + key_pair = cryptocontext.KeyGen() + cryptocontext.EvalMultKeyGen(key_pair.secretKey) + # Generate bootstrapping keys. + cryptocontext.EvalBootstrapKeyGen(key_pair.secretKey, num_slots) + + # Step 4: Encoding and encryption of inputs + # Generate random input + x = [random.uniform(0, 2) for i in range(num_slots)] + + """ Encoding as plaintexts + We specify the number of slots as num_slots 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(num_slots); + Here, we assume all ciphertexts in the cryptoContext will have num_slots slots. + We start with a depleted ciphertext that has used up all of its levels.""" + ptxt = cryptocontext.MakeCKKSPackedPlaintext(x, 1, depth-1-level_budget[1], None, num_slots) + ptxt.SetLength(num_slots) + print(f"Input: {ptxt}") + + # Encrypt the encoded vectors + ciph = cryptocontext.Encrypt(key_pair.publicKey, ptxt) + + # Step 5: Measure the precision of a single bootstrapping operation. + ciphertext_after = cryptocontext.EvalBootstrap(ciph) + + result = cryptocontext.Decrypt(ciphertext_after, key_pair.secretKey) + result.SetLength(num_slots) + precision = calculate_approximation_error(result.GetCKKSPackedValue(), ptxt.GetCKKSPackedValue()) + print(f"Bootstrapping precision after 1 iteration: {precision} bits\n") + + # Set the precision equal to empirically measured value after many test runs. + precision -= 5 + print(f"Precision input to algorithm: {precision}\n") + + # Step 6: Run bootstrapping with multiple iterations + ciphertext_two_iterations = cryptocontext.EvalBootstrap(ciph, num_iterations, precision) + + result_two_iterations = cryptocontext.Decrypt(ciphertext_two_iterations, key_pair.secretKey) + result_two_iterations.SetLength(num_slots) + actual_result = result_two_iterations.GetCKKSPackedValue() + + print(f"Output after two interations of bootstrapping: {actual_result}\n") + precision_multiple_iterations = calculate_approximation_error(actual_result, ptxt.GetCKKSPackedValue()) + + print(f"Bootstrapping precision after 2 iterations: {precision_multiple_iterations} bits\n") + print(f"Number of levels remaining after 2 bootstrappings: " + f"{depth - ciphertext_two_iterations.GetLevel() - ciphertext_two_iterations.GetNoiseScaleDeg() - 1}\n\n") + + #--------------------------------------------------------------------------------------------------------------------- + # When using EvalBootstrap for 2 iterations with STC first, it may be beneficial to scale down the default correction + # factor to achieve a higher final precision. This behavior is specifically pronounced for sparse packing. As the + # number of slots increases, the difference between the default correction factor and the best empirical correction + # factor decreases. For full packing at full security for CKKS bootstrapping, this variant of CKKS bootstrapping + # has better precision than the ModRaise-first variant without any change to the default correction factor. + + cryptocontext.SetCKKSBootCorrectionFactor(cryptocontext.GetCKKSBootCorrectionFactor() - 5) + print(f"Correction factor used: {cryptocontext.GetCKKSBootCorrectionFactor()}") + + ciphertext_after = cryptocontext.EvalBootstrap(ciph) + result = cryptocontext.Decrypt(key_pair.secretKey, ciphertext_after) + result.SetLength(num_slots) + precision = math.floor(calculate_approximation_error(result.GetCKKSPackedValue(), ptxt.GetCKKSPackedValue())) + print(f"Bootstrapping precision after 1 iteration: {precision}\n\n") + + # Set precision equal to empirically measured value after many test runs. One could add a buffer to reduce this value as below. + precision -= 5 + print(f"Precision input to 2nd iteration: {precision}\n") + + ciphertext_two_iterations = cryptocontext.EvalBootstrap(ciph, num_iterations, precision) + result_two_iterations = cryptocontext.Decrypt(key_pair.secretKey, ciphertext_two_iterations) + actual_result = result_two_iterations.GetCKKSPackedValue() + + print(f"Output after two iterations of bootstrapping: {actual_result}\n") + precision_multiple_iterations = calculate_approximation_error(actual_result, ptxt.GetCKKSPackedValue()) + + # Output the precision of bootstrapping after two iterations. It should be approximately double the original precision. + print(f"Bootstrapping precision after 2 iterations: {precision_multiple_iterations}\n") + print(f"Number of levels remaining after 2 bootstrappings: " + f"{depth - ciphertext_two_iterations.GetLevel() - (ciphertext_two_iterations.GetNoiseScaleDeg() - 1)}\n\n") + if __name__ == "__main__": main() \ No newline at end of file diff --git a/examples/pke/simple-ckks-bootstrapping.py b/examples/pke/simple-ckks-bootstrapping.py index 4bb8c26..612eb58 100644 --- a/examples/pke/simple-ckks-bootstrapping.py +++ b/examples/pke/simple-ckks-bootstrapping.py @@ -72,5 +72,73 @@ def simple_bootstrap_example(): result.SetLength(encoded_length) print(f"Output after bootstrapping: {result}") +def simple_bootstrap_stc_example(): + parameters = CCParamsCKKSRNS() + + secret_key_dist = SecretKeyDist.UNIFORM_TERNARY + parameters.SetSecretKeyDist(secret_key_dist) + + parameters.SetSecurityLevel(SecurityLevel.HEStd_NotSet) + parameters.SetRingDim(1<<12) + + if get_native_int()==128: + rescale_tech = ScalingTechnique.FIXEDAUTO + dcrt_bits = 78 + first_mod = 89 + else: + rescale_tech = ScalingTechnique.FLEXIBLEAUTO + dcrt_bits = 59 + first_mod = 60 + + parameters.SetScalingModSize(dcrt_bits) + parameters.SetScalingTechnique(rescale_tech) + parameters.SetFirstModSize(first_mod) + + level_budget = [4, 4] + + levels_available_after_bootstrap = 10 + level_budget[1] + + depth = levels_available_after_bootstrap + FHECKKSRNS.GetBootstrapDepth({level_budget[0], 0}, secret_key_dist) + + parameters.SetMultiplicativeDepth(depth) + + cryptocontext = GenCryptoContext(parameters) + cryptocontext.Enable(PKESchemeFeature.PKE) + cryptocontext.Enable(PKESchemeFeature.KEYSWITCH) + cryptocontext.Enable(PKESchemeFeature.LEVELEDSHE) + cryptocontext.Enable(PKESchemeFeature.ADVANCEDSHE) + cryptocontext.Enable(PKESchemeFeature.FHE) + + ring_dim = cryptocontext.GetRingDimension() + # This is the mazimum number of slots that can be used full packing. + num_slots = int(ring_dim / 2) + print(f"CKKS is using ring dimension {ring_dim}") + + cryptocontext.EvalBootstrapSetup(level_budget, [0, 0], num_slots, 0, True, True) + + key_pair = cryptocontext.KeyGen() + cryptocontext.EvalMultKeyGen(key_pair.secretKey) + cryptocontext.EvalBootstrapKeyGen(key_pair.secretKey, num_slots) + + x = [0.25, 0.5, 0.75, 1.0, 2.0, 3.0, 4.0, 5.0] + encoded_length = len(x) + + ptxt = cryptocontext.MakeCKKSPackedPlaintext(x, 1, depth-1-levelBudget[1], None, num_slots) + ptxt.SetLength(encoded_length) + + print(f"Input: {ptxt}") + + ciph = cryptocontext.Encrypt(key_pair.publicKey, ptxt) + + print(f"Initial number of levels remaining: {depth - ciph.GetLevel()}") + + ciphertext_after = cryptocontext.EvalBootstrap(ciph) + + print(f"Number of levels remaining after bootstrapping: {depth - ciphertext_after.GetLevel() - (ciphertext_after.GetNoiseScaleDeg() - 1)}") + + result = cryptocontext.Decrypt(ciphertext_after,key_pair.secretKey) + result.SetLength(encoded_length) + print(f"Output after bootstrapping: {result}") + if __name__ == '__main__': main() \ No newline at end of file diff --git a/src/lib/bindings.cpp b/src/lib/bindings.cpp index 30af383..49b3c08 100644 --- a/src/lib/bindings.cpp +++ b/src/lib/bindings.cpp @@ -288,6 +288,8 @@ void bind_crypto_context(py::module &m) { }) .def("GetCyclotomicOrder", &CryptoContextImpl::GetCyclotomicOrder, cc_GetCyclotomicOrder_docs) .def("GetCKKSDataType", &CryptoContextImpl::GetCKKSDataType) + .def("GetCKKSBootCorrectionFactor", &CC::GetCKKSBootCorrectionFactor) + .def("SetCKKSBootCorrectionFactor", &CC::SetCKKSBootCorrectionFactor, py::arg("cf")) .def("GetNoiseEstimate", [](CryptoContext& self) { return GetParamsRNSChecked(self, "GetNoiseEstimate")->GetNoiseEstimate(); }) @@ -396,18 +398,22 @@ void bind_crypto_context(py::module &m) { py::arg("ciphertext"), py::doc(cc_EvalFastRotationPreCompute_docs)) .def("EvalFastRotation", - [](CryptoContext& self, - ConstCiphertext ciphertext, - uint32_t index, - uint32_t m, - ConstCiphertext digits) { - return self->EvalFastRotation(ciphertext, index, m, std::make_shared>(digits->GetElements())); - }, + static_cast (CC::*)(ConstCiphertext&, uint32_t, uint32_t, + const std::shared_ptr>) const>( + &CC::EvalFastRotation), py::arg("ciphertext"), py::arg("index"), py::arg("m"), py::arg("digits"), py::doc(cc_EvalFastRotation_docs)) + .def("EvalFastRotation", + static_cast (CC::*)(ConstCiphertext&, uint32_t, + const std::shared_ptr>) const>( + &CC::EvalFastRotation), + py::arg("ciphertext"), + py::arg("index"), + py::arg("digits"), + py::doc("")) // TODO (dsuponit): replace this with an actual docstring .def("EvalFastRotationExt", [](CryptoContext& self, ConstCiphertext ciphertext, @@ -1297,10 +1303,12 @@ void bind_enums_and_constants(py::module &m) { py::enum_(m, "SecretKeyDist") .value("GAUSSIAN", SecretKeyDist::GAUSSIAN) .value("UNIFORM_TERNARY", SecretKeyDist::UNIFORM_TERNARY) - .value("SPARSE_TERNARY", SecretKeyDist::SPARSE_TERNARY); + .value("SPARSE_TERNARY", SecretKeyDist::SPARSE_TERNARY) + .value("SPARSE_ENCAPSULATED", SecretKeyDist::SPARSE_ENCAPSULATED); m.attr("GAUSSIAN") = py::cast(SecretKeyDist::GAUSSIAN); m.attr("UNIFORM_TERNARY") = py::cast(SecretKeyDist::UNIFORM_TERNARY); m.attr("SPARSE_TERNARY") = py::cast(SecretKeyDist::SPARSE_TERNARY); + m.attr("SPARSE_ENCAPSULATED") = py::cast(SecretKeyDist::SPARSE_ENCAPSULATED); // ProxyReEncryptionMode py::enum_(m, "ProxyReEncryptionMode") From 02c780bebd54b60bf499cfc4b14c906ea0154096 Mon Sep 17 00:00:00 2001 From: Dmitriy Suponitskiy Date: Wed, 18 Feb 2026 09:50:27 -0500 Subject: [PATCH 5/7] Added simple-real-numbers-composite-scaling.py and simple-complex-numbers.py --- examples/pke/simple-complex-numbers.py | 289 ++++++++++++++++++ .../simple-real-numbers-composite-scaling.py | 149 +++++++++ 2 files changed, 438 insertions(+) create mode 100644 examples/pke/simple-complex-numbers.py create mode 100644 examples/pke/simple-real-numbers-composite-scaling.py diff --git a/examples/pke/simple-complex-numbers.py b/examples/pke/simple-complex-numbers.py new file mode 100644 index 0000000..522ef55 --- /dev/null +++ b/examples/pke/simple-complex-numbers.py @@ -0,0 +1,289 @@ +from openfhe import * + + +def SimpleComplexNumbers(): + print("\n================= Simple Operations on Complex Numbers =====================") + + # Step 1: Setup CryptoContext + multDepth = 1 + scaleModSize = 50 + batchSize = 8 + ckksDataType = CKKSDataType.COMPLEX + + parameters = CCParamsCKKSRNS() + parameters.SetMultiplicativeDepth(multDepth) + parameters.SetScalingModSize(scaleModSize) + parameters.SetBatchSize(batchSize) + parameters.SetCKKSDataType(ckksDataType) + + cc = GenCryptoContext(parameters) + + cc.Enable(PKE) + cc.Enable(KEYSWITCH) + cc.Enable(LEVELEDSHE) + print(f"CKKS scheme is using ring dimension {cc.GetRingDimension()}\n") + + # Step 2: Key Generation + keys = cc.KeyGen() + cc.EvalMultKeyGen(keys.secretKey) + cc.EvalRotateKeyGen(keys.secretKey, [1, -2]) + + # Conjugation key: automorphism with index 2N-1 + indexConj = 2 * cc.GetRingDimension() - 1 + cc.EvalAutomorphismKeyGen(keys.secretKey, [indexConj]) + + # Step 3: Encoding and encryption of inputs + x1 = [0.25 + 0.25j, 0.5 + 0.5j, 0.75 + 0.75j, 1.0 + 1.0j, + 2.0 + 2.0j, 3.0 + 3.0j, 4.0 + 4.0j, 5.0 + 5.0j] + x2 = [5.0 - 5.0j, 4.0 - 4.0j, 3.0 - 3.0j, 2.0 - 2.0j, + 1.0 - 1.0j, 0.75 - 0.75j, 0.5 - 0.5j, 0.25 - 0.25j] + + constComplex = 1.0 - 2.0j + constComplex2 = 1.0 + 0.5j + + ptxt1 = cc.MakeCKKSPackedPlaintext(x1) + ptxt2 = cc.MakeCKKSPackedPlaintext(x2) + + print(f"Input x1: {ptxt1}", end="") + print(f"Input x2: {ptxt2}", end="") + + c1 = cc.Encrypt(keys.publicKey, ptxt1) + c2 = cc.Encrypt(keys.publicKey, ptxt2) + + # Step 4: Evaluation + cAdd = cc.EvalAdd(c1, c2) + cSub = cc.EvalSub(c1, c2) + cScalar = cc.EvalMult(c1, 4.0) + cMul = cc.EvalMult(c1, c2) + cRot1 = cc.EvalRotate(c1, 1) + cRot2 = cc.EvalRotate(c1, -2) + + # Conjugation (automorphism) + evalConjKeyMap = cc.GetEvalAutomorphismKeyMap(c1.GetKeyTag()) + cConj1 = cc.EvalAutomorphism(c1, indexConj, evalConjKeyMap) + + # Multiply by a complex constant + cMulC = cc.EvalMult(c1, constComplex) + + # Additions by complex constants + cAddC = cc.EvalAdd(c2, constComplex) + cc.EvalAddInPlace(cAddC, constComplex2) + + # Subtractions by complex constants + cSubC = cc.EvalSub(c2, constComplex) + cc.EvalSubInPlace(cSubC, constComplex2) + + # Step 5: Decryption and output + result = Plaintext() + + print("\nDecrypted complex inputs:\n") + result = cc.Decrypt(keys.secretKey, c1) + result.SetLength(batchSize) + print(f"x1 = {result}", end="") + + result = cc.Decrypt(keys.secretKey, c2) + result.SetLength(batchSize) + print(f"x2 = {result}", end="") + + print("\nResults of homomorphic computations:\n") + + result = cc.Decrypt(keys.secretKey, cAdd) + result.SetLength(batchSize) + print(f"x1 + x2 = {result}", end="") + + result = cc.Decrypt(keys.secretKey, cSub) + result.SetLength(batchSize) + print(f"x1 - x2 = {result}", end="") + + result = cc.Decrypt(keys.secretKey, cScalar) + result.SetLength(batchSize) + print(f"4 * x1 = {result}", end="") + + result = cc.Decrypt(keys.secretKey, cMul) + result.SetLength(batchSize) + print(f"x1 * x2 = {result}", end="") + + result = cc.Decrypt(keys.secretKey, cRot1) + result.SetLength(batchSize) + print("\nIn rotations, very small outputs (~10^-10 here) correspond to 0's:") + print(f"x1 rotated by 1 = {result}", end="") + + result = cc.Decrypt(keys.secretKey, cRot2) + result.SetLength(batchSize) + print(f"x1 rotated by -2 = {result}", end="") + + result = cc.Decrypt(keys.secretKey, cConj1) + result.SetLength(batchSize) + print(f"x1 conjugated = {result}", end="") + + result = cc.Decrypt(keys.secretKey, cMulC) + result.SetLength(batchSize) + print(f"x1 * (1 - 2i) = {result}", end="") + + result = cc.Decrypt(keys.secretKey, cAddC) + result.SetLength(batchSize) + print(f"x2 + (1 - 2i) + (1 + 0.5i) = {result}", end="") + + result = cc.Decrypt(keys.secretKey, cSubC) + result.SetLength(batchSize) + print(f"x2 - (1 - 2i) - (1 + 0.5i) = {result}", end="") + + +def _fill_to_length(vec, n, fill_value=0): + # C++ Fill(x, numSlots) behavior for demos is usually "pad with zeros" + if len(vec) >= n: + return vec[:n] + return vec + [fill_value] * (n - len(vec)) + + +def SimpleBootstrappingComplex(): + print("\n================= Bootstrapping Complex Numbers =====================") + + parameters = CCParamsCKKSRNS() + + secretKeyDist = SecretKeyDist.UNIFORM_TERNARY + parameters.SetSecretKeyDist(secretKeyDist) + + parameters.SetSecurityLevel(SecurityLevel.HEStd_NotSet) + ringDim = 1 << 6 + parameters.SetRingDim(ringDim) + + # In your C++ demo this depends on NATIVEINT. Here we pick the common 64-bit path. + rescaleTech = ScalingTechnique.FLEXIBLEAUTO + dcrtBits = 59 + firstMod = 60 + + parameters.SetScalingModSize(dcrtBits) + parameters.SetScalingTechnique(rescaleTech) + parameters.SetFirstModSize(firstMod) + + parameters.SetCKKSDataType(CKKSDataType.COMPLEX) + + numSlots = ringDim // 2 + # parameters.SetBatchSize(numSlots) # intentionally not set in the C++ demo + + levelBudget = [2, 2] + levelsAvailableAfterBootstrap = 10 + depth = levelsAvailableAfterBootstrap + FHECKKSRNS.GetBootstrapDepth(levelBudget, secretKeyDist) + parameters.SetMultiplicativeDepth(depth) + + cryptoContext = GenCryptoContext(parameters) + cryptoContext.Enable(PKE) + cryptoContext.Enable(KEYSWITCH) + cryptoContext.Enable(LEVELEDSHE) + cryptoContext.Enable(ADVANCEDSHE) + cryptoContext.Enable(FHE) + + print(f"CKKS scheme is using ring dimension {ringDim} and number of slots {numSlots}\n") + + cryptoContext.EvalBootstrapSetup(levelBudget, [0, 0], numSlots) + + keyPair = cryptoContext.KeyGen() + cryptoContext.EvalMultKeyGen(keyPair.secretKey) + cryptoContext.EvalBootstrapKeyGen(keyPair.secretKey, numSlots) + + x = [0.25 + 0.25j, 0.5 - 0.5j, 0.75 + 0.75j, 1.0 - 1.0j, + 2.0 + 2.0j, 3.0 - 3.0j, 4.0 + 4.0j, 5.0 - 5.0j] + x = _fill_to_length(x, numSlots, 0.0 + 0.0j) + encodedLength = len(x) + + # depleted ciphertext: level = depth-1 + ptxt = cryptoContext.MakeCKKSPackedPlaintext(x, 1, depth - 1, None, numSlots) + ptxt.SetLength(encodedLength) + print(f"Input: {ptxt}") + + ciph = cryptoContext.Encrypt(keyPair.publicKey, ptxt) + + print(f"Initial number of levels remaining: {depth - ciph.GetLevel()}") + + ciphertextAfter = cryptoContext.EvalBootstrap(ciph) + + levels_after = depth - ciphertextAfter.GetLevel() - (ciphertextAfter.GetNoiseScaleDeg() - 1) + print(f"Number of levels remaining after bootstrapping: {levels_after}\n") + + result = cryptoContext.Decrypt(keyPair.secretKey, ciphertextAfter) + result.SetLength(encodedLength) + print(f"Output after bootstrapping:\n\t{result}", end="") + + +def SimpleBootstrappingStCFirstComplex(): + print("\n================= Bootstrapping Complex Numbers with StC Transformation First =====================") + + parameters = CCParamsCKKSRNS() + + secretKeyDist = SecretKeyDist.UNIFORM_TERNARY + parameters.SetSecretKeyDist(secretKeyDist) + + parameters.SetSecurityLevel(SecurityLevel.HEStd_NotSet) + ringDim = 1 << 6 + parameters.SetRingDim(ringDim) + + # Common 64-bit path + rescaleTech = ScalingTechnique.FLEXIBLEAUTO + dcrtBits = 59 + firstMod = 60 + + parameters.SetScalingModSize(dcrtBits) + parameters.SetScalingTechnique(rescaleTech) + parameters.SetFirstModSize(firstMod) + + parameters.SetCKKSDataType(CKKSDataType.COMPLEX) + + numSlots = ringDim // 2 + # parameters.SetBatchSize(numSlots) # intentionally not set in the C++ demo + + levelBudget = [2, 2] + + levelsAvailableAfterBootstrap = 10 + levelBudget[1] + depth = levelsAvailableAfterBootstrap + FHECKKSRNS.GetBootstrapDepth([levelBudget[0], 0], secretKeyDist) + parameters.SetMultiplicativeDepth(depth) + + cryptoContext = GenCryptoContext(parameters) + cryptoContext.Enable(PKE) + cryptoContext.Enable(KEYSWITCH) + cryptoContext.Enable(LEVELEDSHE) + cryptoContext.Enable(ADVANCEDSHE) + cryptoContext.Enable(FHE) + + print(f"CKKS scheme is using ring dimension {ringDim} and number of slots {numSlots} with depth {depth}\n") + + cryptoContext.EvalBootstrapSetup(levelBudget, [0, 0], numSlots, 0, True, True) + + keyPair = cryptoContext.KeyGen() + cryptoContext.EvalMultKeyGen(keyPair.secretKey) + cryptoContext.EvalBootstrapKeyGen(keyPair.secretKey, numSlots) + + x = [0.25 + 0.25j, 0.5 - 0.5j, 0.75 + 0.75j, 1.0 - 1.0j, + 2.0 + 2.0j, 3.0 - 3.0j, 4.0 + 4.0j, 5.0 - 5.0j] + x = _fill_to_length(x, numSlots, 0.0 + 0.0j) + encodedLength = len(x) + + # depleted ciphertext: level = depth - 1 - levelBudget[1] + ptxt = cryptoContext.MakeCKKSPackedPlaintext(x, 1, depth - 1 - levelBudget[1], None, numSlots) + ptxt.SetLength(encodedLength) + print(f"Input: {ptxt}") + + ciph = cryptoContext.Encrypt(keyPair.publicKey, ptxt) + + print(f"Initial number of levels remaining: {depth - ciph.GetLevel()}") + + ciphertextAfter = cryptoContext.EvalBootstrap(ciph) + + levels_after = depth - ciphertextAfter.GetLevel() - (ciphertextAfter.GetNoiseScaleDeg() - 1) + print(f"Number of levels remaining after bootstrapping: {levels_after}\n") + + result = cryptoContext.Decrypt(keyPair.secretKey, ciphertextAfter) + + # C++: result->SetLength(2 * encodedLength); + result.SetLength(2 * encodedLength) + print(f"Output after bootstrapping:\n\t{result}", end="") + + +def main(): + SimpleComplexNumbers() + SimpleBootstrappingComplex() + SimpleBootstrappingStCFirstComplex() + + +if __name__ == "__main__": + main() diff --git a/examples/pke/simple-real-numbers-composite-scaling.py b/examples/pke/simple-real-numbers-composite-scaling.py new file mode 100644 index 0000000..2b14dbd --- /dev/null +++ b/examples/pke/simple-real-numbers-composite-scaling.py @@ -0,0 +1,149 @@ +from openfhe import * +import sys + + +def main(): + # Defaults (same as C++) + multDepth = 2 + firstModSize = 90 + scaleModSize = 73 + batchSize = 8 + registerWordSize = 32 + + # Parse CLI args like the C++ demo: + # script.py [firstModSize] [scalingModSize] [registerWordSize] [multDepth] + argv = sys.argv + if len(argv) > 1: + argcCount = 1 + while argcCount < len(argv): + paramValue = int(argv[argcCount]) + if argcCount == 1: + firstModSize = paramValue + print(f"Setting First Mod Size: {firstModSize}") + elif argcCount == 2: + scaleModSize = paramValue + print(f"Setting Scaling Mod Size: {scaleModSize}") + elif argcCount == 3: + registerWordSize = paramValue + print(f"Setting Register Word Size: {registerWordSize}") + elif argcCount == 4: + multDepth = paramValue + print(f"Setting Multiplicative Depth: {multDepth}") + else: + print("Invalid option") + argcCount += 1 + print(f"argcCount: {argcCount}") + print("Complete !") + else: + print("Using default parameters") + print(f"First Mod Size: {firstModSize}") + print(f"Scaling Mod Size: {scaleModSize}") + print(f"Register Word Size: {registerWordSize}") + print(f"Multiplicative Depth: {multDepth}") + print(f"Usage: {argv[0]} [firstModSize] [scalingModSize] [registerWordSize] [multDepth]") + + # Step 1: Setup CryptoContext (CKKS) + parameters = CCParamsCKKSRNS() + parameters.SetMultiplicativeDepth(multDepth) + parameters.SetFirstModSize(firstModSize) + parameters.SetScalingModSize(scaleModSize) + parameters.SetBatchSize(batchSize) + parameters.SetSecurityLevel(SecurityLevel.HEStd_NotSet) + parameters.SetRingDim(1 << 12) + + parameters.SetScalingTechnique(ScalingTechnique.COMPOSITESCALINGAUTO) + parameters.SetRegisterWordSize(registerWordSize) + + cc = GenCryptoContext(parameters) + + # If your Python bindings expose CryptoParametersCKKSRNS, you may be able to do: + # cryptoParams = cc.GetCryptoParameters() + # print("Composite Degree:", cryptoParams.GetCompositeDegree()) + # but not all builds expose this cleanly; the C++ demo prints it mainly for info. + + # Enable features + cc.Enable(PKE) + cc.Enable(KEYSWITCH) + cc.Enable(LEVELEDSHE) + + print(f"CKKS scheme is using ring dimension {cc.GetRingDimension()}\n") + + # Step 2: Key Generation + keys = cc.KeyGen() + cc.EvalMultKeyGen(keys.secretKey) + cc.EvalRotateKeyGen(keys.secretKey, [1, -2]) + + # Step 3: Encoding and encryption of inputs + x1 = [0.25, 0.5, 0.75, 1.0, 2.0, 3.0, 4.0, 5.0] + x2 = [5.0, 4.0, 3.0, 2.0, 1.0, 0.75, 0.5, 0.25] + + ptxt1 = cc.MakeCKKSPackedPlaintext(x1) + ptxt2 = cc.MakeCKKSPackedPlaintext(x2) + + print(f"Input x1: {ptxt1}") + print(f"Input x2: {ptxt2}") + + c1 = cc.Encrypt(keys.publicKey, ptxt1) + c2 = cc.Encrypt(keys.publicKey, ptxt2) + + # Step 4: Evaluation + cAdd = cc.EvalAdd(c1, c2) + cSub = cc.EvalSub(c1, c2) + cScalar = cc.EvalMult(c1, 4.0) + cMul = cc.EvalMult(c1, c2) + cRot1 = cc.EvalRotate(c1, 1) + cRot2 = cc.EvalRotate(c1, -2) + + # Step 5: Decryption and output + print("\nResults of homomorphic computations:\n") + + result = cc.Decrypt(keys.secretKey, c1) + result.SetLength(batchSize) + print(f"x1 = {result}", end="") + print(f"Estimated precision in bits: {result.GetLogPrecision()}") + + result = cc.Decrypt(keys.secretKey, cAdd) + result.SetLength(batchSize) + print(f"x1 + x2 = {result}", end="") + print(f"Estimated precision in bits: {result.GetLogPrecision()}") + + result = cc.Decrypt(keys.secretKey, cSub) + result.SetLength(batchSize) + print(f"x1 - x2 = {result}") + + result = cc.Decrypt(keys.secretKey, cScalar) + result.SetLength(batchSize) + print(f"4 * x1 = {result}") + + result = cc.Decrypt(keys.secretKey, cMul) + result.SetLength(batchSize) + print(f"x1 * x2 = {result}") + + result = cc.Decrypt(keys.secretKey, cRot1) + result.SetLength(batchSize) + print("\nIn rotations, very small outputs (~10^-10 here) correspond to 0's:") + print(f"x1 rotate by 1 = {result}") + + result = cc.Decrypt(keys.secretKey, cRot2) + result.SetLength(batchSize) + print(f"x1 rotate by -2 = {result}") + + # Testing EvalSub ciphertext - double + cSubDouble = cc.EvalSub(c1, 0.5) + print(f"c1 noise degree = {c1.GetNoiseScaleDeg()}") + print(f"c1 scaling factor = {c1.GetScalingFactor()}") + + # Testing EvalAdd ciphertext + negative double + cAddNegDouble = cc.EvalAdd(c1, -0.5) + + result = cc.Decrypt(keys.secretKey, cSubDouble) + result.SetLength(batchSize) + print(f"x1 - 0.5 = {result}") + + result = cc.Decrypt(keys.secretKey, cAddNegDouble) + result.SetLength(batchSize) + print(f"x1 + (-0.5) = {result}") + + +if __name__ == "__main__": + main() From d7761f51dd9fbf8ad36e6b264d28cf425df5ad61 Mon Sep 17 00:00:00 2001 From: Dmitriy Suponitskiy Date: Wed, 18 Feb 2026 10:18:00 -0500 Subject: [PATCH 6/7] Fixes --- examples/pke/iterative-ckks-bootstrapping.py | 4 +- examples/pke/simple-complex-numbers.py | 2 - src/lib/bindings.cpp | 72 ++++++++++++++++---- 3 files changed, 60 insertions(+), 18 deletions(-) diff --git a/examples/pke/iterative-ckks-bootstrapping.py b/examples/pke/iterative-ckks-bootstrapping.py index 6f5b156..f25aa03 100644 --- a/examples/pke/iterative-ckks-bootstrapping.py +++ b/examples/pke/iterative-ckks-bootstrapping.py @@ -96,7 +96,7 @@ def iterative_bootstrap_example(): result = cryptocontext.Decrypt(ciphertext_after,key_pair.secretKey) result.SetLength(num_slots) - precision = calculate_approximation_error(result.GetCKKSPackedValue(),ptxt.GetCKKSPackedValue()) + precision = math.floor(calculate_approximation_error(result.GetCKKSPackedValue(),ptxt.GetCKKSPackedValue())) print(f"Bootstrapping precision after 1 iteration: {precision} bits\n") # Set the precision equal to empirically measured value after many test runs. @@ -197,7 +197,7 @@ def iterative_bootstrap_stc_example(): result = cryptocontext.Decrypt(ciphertext_after, key_pair.secretKey) result.SetLength(num_slots) - precision = calculate_approximation_error(result.GetCKKSPackedValue(), ptxt.GetCKKSPackedValue()) + precision = math.floor(calculate_approximation_error(result.GetCKKSPackedValue(), ptxt.GetCKKSPackedValue())) print(f"Bootstrapping precision after 1 iteration: {precision} bits\n") # Set the precision equal to empirically measured value after many test runs. diff --git a/examples/pke/simple-complex-numbers.py b/examples/pke/simple-complex-numbers.py index 522ef55..2defbd2 100644 --- a/examples/pke/simple-complex-numbers.py +++ b/examples/pke/simple-complex-numbers.py @@ -74,8 +74,6 @@ def SimpleComplexNumbers(): cc.EvalSubInPlace(cSubC, constComplex2) # Step 5: Decryption and output - result = Plaintext() - print("\nDecrypted complex inputs:\n") result = cc.Decrypt(keys.secretKey, c1) result.SetLength(batchSize) diff --git a/src/lib/bindings.cpp b/src/lib/bindings.cpp index 49b3c08..a10a802 100644 --- a/src/lib/bindings.cpp +++ b/src/lib/bindings.cpp @@ -384,6 +384,17 @@ void bind_crypto_context(py::module &m) { py::arg("params") = py::none(), py::arg("slots") = 0, py::doc(cc_MakeCKKSPlaintextReal_docs)) + .def("EvalAutomorphism", + [](const CC& self, + ConstCiphertext& ciphertext, + uint32_t i, + const std::shared_ptr>>& evalKeyMap) { + return self.EvalAutomorphism(ciphertext, i, *evalKeyMap); + }, + py::arg("ciphertext"), + py::arg("i"), + py::arg("evalKeyMap"), + py::doc("Applies an automorphism to a ciphertext using the given evaluation keys.")) .def("EvalRotate", &CryptoContextImpl::EvalRotate, py::arg("ciphertext"), py::arg("index"), @@ -398,22 +409,29 @@ void bind_crypto_context(py::module &m) { py::arg("ciphertext"), py::doc(cc_EvalFastRotationPreCompute_docs)) .def("EvalFastRotation", - static_cast (CC::*)(ConstCiphertext&, uint32_t, uint32_t, - const std::shared_ptr>) const>( - &CC::EvalFastRotation), + [](CryptoContext& self, + ConstCiphertext ciphertext, + uint32_t index, + uint32_t m, + ConstCiphertext digits) { + return self->EvalFastRotation(ciphertext, index, m, std::make_shared>(digits->GetElements())); + }, py::arg("ciphertext"), py::arg("index"), py::arg("m"), py::arg("digits"), py::doc(cc_EvalFastRotation_docs)) .def("EvalFastRotation", - static_cast (CC::*)(ConstCiphertext&, uint32_t, - const std::shared_ptr>) const>( - &CC::EvalFastRotation), + [](CryptoContext& self, + ConstCiphertext ciphertext, + uint32_t index, + ConstCiphertext digits) { + return self->EvalFastRotation(ciphertext, index, std::make_shared>(digits->GetElements())); + }, py::arg("ciphertext"), py::arg("index"), py::arg("digits"), - py::doc("")) // TODO (dsuponit): replace this with an actual docstring + py::doc(cc_EvalFastRotation_docs)) .def("EvalFastRotationExt", [](CryptoContext& self, ConstCiphertext ciphertext, @@ -472,6 +490,11 @@ void bind_crypto_context(py::module &m) { py::arg("ciphertext"), py::arg("scalar"), py::doc(cc_EvalAddfloat_docs)) + .def("EvalAdd", + py::overload_cast&, std::complex>(&CryptoContextImpl::EvalAdd, py::const_), + py::arg("ciphertext"), + py::arg("scalar"), + py::doc("")) .def("EvalAdd", py::overload_cast&, Plaintext&>(&CryptoContextImpl::EvalAdd, py::const_), py::arg("ciphertext"), @@ -535,6 +558,16 @@ void bind_crypto_context(py::module &m) { py::arg("scalar"), py::arg("ciphertext"), py::doc("")) // TODO (dsuponit): replace this with an actual docstring + .def("EvalSub", + py::overload_cast&, std::complex>(&CryptoContextImpl::EvalSub, py::const_), + py::arg("ciphertext"), + py::arg("scalar"), + py::doc(cc_EvalSubfloat_docs)) + .def("EvalSub", + py::overload_cast, ConstCiphertext&>(&CryptoContextImpl::EvalSub, py::const_), + py::arg("scalar"), + py::arg("ciphertext"), + py::doc("")) // TODO (dsuponit): replace this with an actual docstring .def("EvalSub", py::overload_cast&, Plaintext&>(&CryptoContextImpl::EvalSub, py::const_), py::arg("ciphertext"), @@ -601,6 +634,21 @@ void bind_crypto_context(py::module &m) { py::arg("ciphertext"), py::arg("scalar"), py::doc(cc_EvalMultfloat_docs)) + .def("EvalMult", + py::overload_cast&>(&CryptoContextImpl::EvalMult, py::const_), + py::arg("scalar"), + py::arg("ciphertext"), + py::doc("")) // TODO (dsuponit): replace this with an actual docstring + .def("EvalMult", + py::overload_cast&, std::complex>(&CryptoContextImpl::EvalMult, py::const_), + py::arg("ciphertext"), + py::arg("scalar"), + py::doc(cc_EvalMultfloat_docs)) + .def("EvalMult", + py::overload_cast, ConstCiphertext&>(&CryptoContextImpl::EvalMult, py::const_), + py::arg("scalar"), + py::arg("ciphertext"), + py::doc("")) // TODO (dsuponit): replace this with an actual docstring .def("EvalMult", py::overload_cast&, ConstPlaintext&>(&CryptoContextImpl::EvalMult, py::const_), py::arg("ciphertext"), @@ -611,11 +659,6 @@ void bind_crypto_context(py::module &m) { py::arg("plaintext"), py::arg("ciphertext"), py::doc("")) // TODO (dsuponit): replace this with an actual docstring - .def("EvalMult", - py::overload_cast&>(&CryptoContextImpl::EvalMult, py::const_), - py::arg("scalar"), - py::arg("ciphertext"), - py::doc("")) // TODO (dsuponit): replace this with an actual docstring .def("EvalMultInPlace", static_cast&, double) const>(&CC::EvalMultInPlace), py::arg("ciphertext"), @@ -1569,13 +1612,14 @@ void bind_ciphertext(py::module &m) { py::doc(cc_RemoveElement_docs)) // .def("GetHopLevel", &CiphertextImpl::GetHopLevel) // .def("SetHopLevel", &CiphertextImpl::SetHopLevel) - // .def("GetScalingFactor", &CiphertextImpl::GetScalingFactor) - // .def("SetScalingFactor", &CiphertextImpl::SetScalingFactor) + .def("GetScalingFactor", &CiphertextImpl::GetScalingFactor) + .def("SetScalingFactor", &CiphertextImpl::SetScalingFactor) .def("GetSlots", &CiphertextImpl::GetSlots) .def("SetSlots", &CiphertextImpl::SetSlots) .def("GetNoiseScaleDeg", &CiphertextImpl::GetNoiseScaleDeg) .def("SetNoiseScaleDeg", &CiphertextImpl::SetNoiseScaleDeg) .def("GetCryptoContext", &CiphertextImpl::GetCryptoContext) + .def("GetKeyTag", &CiphertextImpl::GetKeyTag) .def("GetEncodingType", &CiphertextImpl::GetEncodingType) .def("GetElements", [](const CiphertextImpl& self) -> const std::vector& { return self.GetElements(); From 727e25dcd69ae754b6fd9e34ce12fceeacb786af Mon Sep 17 00:00:00 2001 From: Dmitriy Suponitskiy Date: Wed, 18 Feb 2026 17:52:48 -0500 Subject: [PATCH 7/7] Fixes, re-arranging the code --- src/lib/bindings.cpp | 377 ++++++++++++++++++++++--------------------- 1 file changed, 197 insertions(+), 180 deletions(-) diff --git a/src/lib/bindings.cpp b/src/lib/bindings.cpp index a10a802..465f35d 100644 --- a/src/lib/bindings.cpp +++ b/src/lib/bindings.cpp @@ -49,6 +49,7 @@ #include "ciphertext_docs.h" using namespace lbcrypto; +using CC = CryptoContextImpl; namespace py = pybind11; // disable the PYBIND11 template-based conversion for this type @@ -144,86 +145,242 @@ void bind_parameters(py::module &m, const std::string name) { } template -void bind_crypto_context_templates(py::class_, std::shared_ptr>>& cls) { +void bind_crypto_context_templates(py::class_>& cls) { cls.def("EvalChebyshevSeries", - static_cast (CryptoContextImpl::*)( + static_cast (CC::*)( ConstCiphertext&, const std::vector&, double, double - ) const>(&CryptoContextImpl::EvalChebyshevSeries), + ) const>(&CC::EvalChebyshevSeries), py::arg("ciphertext"), py::arg("coefficients"), py::arg("a"), py::arg("b"), py::doc(cc_EvalChebyshevSeries_docs)) .def("EvalChebyshevSeriesLinear", - static_cast (CryptoContextImpl::*)( + static_cast (CC::*)( ConstCiphertext&, const std::vector&, double, double - ) const>(&CryptoContextImpl::EvalChebyshevSeriesLinear), + ) const>(&CC::EvalChebyshevSeriesLinear), py::arg("ciphertext"), py::arg("coefficients"), py::arg("a"), py::arg("b"), py::doc(cc_EvalChebyshevSeriesLinear_docs)) .def("EvalChebyshevSeriesPS", - static_cast (CryptoContextImpl::*)( + static_cast (CC::*)( ConstCiphertext&, const std::vector&, double, double - ) const>(&CryptoContextImpl::EvalChebyshevSeriesPS), + ) const>(&CC::EvalChebyshevSeriesPS), py::arg("ciphertext"), py::arg("coefficients"), py::arg("a"), py::arg("b"), py::doc(cc_EvalChebyshevSeriesPS_docs)) .def("EvalLinearWSum", - static_cast (CryptoContextImpl::*)( + static_cast (CC::*)( std::vector>&, const std::vector& - ) const>(&CryptoContextImpl::EvalLinearWSum), + ) const>(&CC::EvalLinearWSum), py::arg("ciphertextVec"), py::arg("constantVec"), py::doc("Evaluate a weighted sum of ciphertexts using scalar coefficients")) .def("EvalLinearWSumMutable", - static_cast (CryptoContextImpl::*)( + static_cast (CC::*)( const std::vector&, std::vector>& - ) const>(&CryptoContextImpl::EvalLinearWSumMutable), + ) const>(&CC::EvalLinearWSumMutable), py::arg("constantsVec"), py::arg("ciphertextVec"), py::doc("Evaluate a weighted sum (mutable version) with given coefficients")) .def("EvalPoly", - static_cast (CryptoContextImpl::*)( + static_cast (CC::*)( ConstCiphertext&, const std::vector& - ) const>(&CryptoContextImpl::EvalPoly), + ) const>(&CC::EvalPoly), py::arg("ciphertext"), py::arg("coefficients"), py::doc(cc_EvalPoly_docs)) .def("EvalPolyLinear", - static_cast (CryptoContextImpl::*)( + static_cast (CC::*)( ConstCiphertext&, const std::vector& - ) const>(&CryptoContextImpl::EvalPolyLinear), + ) const>(&CC::EvalPolyLinear), py::arg("ciphertext"), py::arg("coefficients"), py::doc(cc_EvalPolyLinear_docs)) .def("EvalPolyPS", - static_cast (CryptoContextImpl::*)( + static_cast (CC::*)( ConstCiphertext&, const std::vector& - ) const>(&CryptoContextImpl::EvalPolyPS), + ) const>(&CC::EvalPolyPS), py::arg("ciphertext"), py::arg("coefficients"), py::doc(cc_EvalPolyPS_docs)) ; } +void bind_eval_add_family(py::class_>& cls) { + cls.def("EvalAdd", static_cast (CC::*)(ConstCiphertext&, ConstCiphertext&) const>(&CC::EvalAdd), + py::arg("ciphertext1"), + py::arg("ciphertext2"), + py::doc(cc_EvalAdd_docs)); + cls.def("EvalAdd", static_cast (CC::*)(ConstCiphertext&, Plaintext&) const>(&CC::EvalAdd), + py::arg("ciphertext"), + py::arg("plaintext"), + py::doc(cc_EvalAddPlaintext_docs)); + cls.def("EvalAdd", static_cast (CC::*)(Plaintext&, ConstCiphertext&) const>(&CC::EvalAdd), + py::arg("plaintext"), + py::arg("ciphertext"), + py::doc("")); // TODO (dsuponit): replace this with an actual docstring +} +template +void bind_eval_add_scalar_overloads(py::class_>& cls) { + cls.def("EvalAdd", static_cast (CC::*)(ConstCiphertext&, ScalarT) const>(&CC::EvalAdd), + py::arg("ciphertext"), + py::arg("scalar"), + py::doc(cc_EvalAddfloat_docs)); + cls.def("EvalAdd", static_cast (CC::*)(ScalarT, ConstCiphertext&) const>(&CC::EvalAdd), + py::arg("scalar"), + py::arg("ciphertext"), + py::doc("")); // TODO (dsuponit): replace this with an actual docstring +} + +void bind_eval_addinplace_family(py::class_>& cls) { + cls.def("EvalAddInPlace", static_cast&, ConstCiphertext&) const>(&CC::EvalAddInPlace), + py::arg("ciphertext1"), + py::arg("ciphertext2"), + py::doc(cc_EvalAddInPlace_docs)); + cls.def("EvalAddInPlace", static_cast&, Plaintext&) const>(&CC::EvalAddInPlace), + py::arg("ciphertext"), + py::arg("plaintext"), + py::doc(cc_EvalAddInPlacePlaintext_docs)); + cls.def("EvalAddInPlace", static_cast&) const>(&CC::EvalAddInPlace), + py::arg("plaintext"), + py::arg("ciphertext"), + py::doc("")); // TODO (dsuponit): replace this with an actual docstring +} +template +void bind_eval_addinplace_scalar_overloads(py::class_>& cls) { + cls.def("EvalAddInPlace", static_cast&, ScalarT) const>(&CC::EvalAddInPlace), + py::arg("ciphertext"), + py::arg("scalar"), + py::doc("")); // TODO (dsuponit): replace this with an actual docstring + cls.def("EvalAddInPlace", static_cast&) const>(&CC::EvalAddInPlace), + py::arg("scalar"), + py::arg("ciphertext"), + py::doc("")); // TODO (dsuponit): replace this with an actual docstring +} + +void bind_eval_sub_family(py::class_>& cls) { + cls.def("EvalSub", static_cast (CC::*)(ConstCiphertext&, ConstCiphertext&) const>(&CC::EvalSub), + py::arg("ciphertext1"), + py::arg("ciphertext2"), + py::doc(cc_EvalSub_docs)); + cls.def("EvalSub", static_cast (CC::*)(ConstCiphertext&, Plaintext&) const>(&CC::EvalSub), + py::arg("ciphertext"), + py::arg("plaintext"), + py::doc(cc_EvalSubPlaintext_docs)); + cls.def("EvalSub", static_cast (CC::*)(Plaintext&, ConstCiphertext&) const>(&CC::EvalSub), + py::arg("plaintext"), + py::arg("ciphertext"), + py::doc("")); // TODO (dsuponit): replace this with an actual docstring +} +template +void bind_eval_sub_scalar_overloads(py::class_>& cls) { + cls.def("EvalSub", static_cast (CC::*)(ConstCiphertext&, ScalarT) const>(&CC::EvalSub), + py::arg("ciphertext"), + py::arg("scalar"), + py::doc(cc_EvalSubfloat_docs)); + cls.def("EvalSub", static_cast (CC::*)(ScalarT, ConstCiphertext&) const>(&CC::EvalSub), + py::arg("scalar"), + py::arg("ciphertext"), + py::doc("")); // TODO (dsuponit): replace this with an actual docstring +} + +void bind_eval_subinplace_family(py::class_>& cls) { + cls.def("EvalSubInPlace", static_cast&, ConstCiphertext&) const>(&CC::EvalSubInPlace), + py::arg("ciphertext1"), + py::arg("ciphertext2"), + py::doc(cc_EvalSubInPlace_docs)); + cls.def("EvalSubInPlace", static_cast&, ConstPlaintext&) const>(&CC::EvalSubInPlace), + py::arg("ciphertext"), + py::arg("plaintext"), + py::doc("")); // TODO (dsuponit): replace this with an actual docstring + cls.def("EvalSubInPlace", static_cast&) const>(&CC::EvalSubInPlace), + py::arg("plaintext"), + py::arg("ciphertext"), + py::doc("")); // TODO (dsuponit): replace this with an actual docstring +} +template +void bind_eval_subinplace_scalar_overloads(py::class_>& cls) { + cls.def("EvalSubInPlace", static_cast&, ScalarT) const>(&CC::EvalSubInPlace), + py::arg("ciphertext"), + py::arg("scalar"), + py::doc(cc_EvalSubInPlacefloat_docs)); + cls.def("EvalSubInPlace", static_cast&) const>(&CC::EvalSubInPlace), + py::arg("scalar"), + py::arg("ciphertext"), + py::doc("")); // TODO (dsuponit): replace this with an actual docstring +} + +void bind_eval_mult_family(py::class_>& cls) { + cls.def("EvalMult", static_cast (CC::*)(ConstCiphertext&, ConstCiphertext&) const>(&CC::EvalMult), + py::arg("ciphertext1"), + py::arg("ciphertext2"), + py::doc(cc_EvalMult_docs)); + cls.def("EvalMult", static_cast (CC::*)(ConstCiphertext&, ConstPlaintext&) const>(&CC::EvalMult), + py::arg("ciphertext"), + py::arg("plaintext"), + py::doc(cc_EvalMultPlaintext_docs)); + cls.def("EvalMult", static_cast (CC::*)(ConstPlaintext&, ConstCiphertext&) const>(&CC::EvalMult), + py::arg("plaintext"), + py::arg("ciphertext"), + py::doc("")); // TODO (dsuponit): replace this with an actual docstring +} +template +void bind_eval_mult_scalar_overloads(py::class_>& cls) { + cls.def("EvalMult", static_cast (CC::*)(ConstCiphertext&, ScalarT) const>(&CC::EvalMult), + py::arg("ciphertext"), + py::arg("scalar"), + py::doc(cc_EvalMultfloat_docs)); + cls.def("EvalMult", static_cast (CC::*)(ScalarT, ConstCiphertext&) const>(&CC::EvalMult), + py::arg("scalar"), + py::arg("ciphertext"), + py::doc("")); // TODO (dsuponit): replace this with an actual docstring +} + +// void bind_eval_multinplace_family(py::class_>& cls) { +// cls.def("EvalMultInPlace", static_cast&, ConstCiphertext&) const>(&CC::EvalMultInPlace), +// py::arg("ciphertext1"), +// py::arg("ciphertext2"), +// py::doc(cc_EvalMultInPlace_docs)); +// cls.def("EvalMultInPlace", static_cast&, Plaintext&) const>(&CC::EvalMultInPlace), +// py::arg("ciphertext"), +// py::arg("plaintext"), +// py::doc(cc_EvalMultInPlacePlaintext_docs)); +// cls.def("EvalMultInPlace", static_cast&) const>(&CC::EvalMultInPlace), +// py::arg("plaintext"), +// py::arg("ciphertext"), +// py::doc("")); // TODO (dsuponit): replace this with an actual docstring +// } +template +void bind_eval_multinplace_scalar_overloads(py::class_>& cls) { + cls.def("EvalAddInPlace", static_cast&, ScalarT) const>(&CC::EvalAddInPlace), + py::arg("ciphertext"), + py::arg("scalar"), + py::doc("")); + cls.def("EvalAddInPlace", static_cast&) const>(&CC::EvalAddInPlace), + py::arg("scalar"), + py::arg("ciphertext"), + py::doc("")); // TODO (dsuponit): replace this with an actual docstring +} + void bind_crypto_context(py::module &m) { //Parameters Type // TODO (Oliveira): If we expose Poly's and ParmType, this block will go somewhere else @@ -232,7 +389,6 @@ void bind_crypto_context(py::module &m) { py::class_(m, "ParmType"); auto cc_class = py::class_, std::shared_ptr>>(m, "CryptoContext"); - using CC = CryptoContextImpl; cc_class.def(py::init<>()) .def("GetKeyGenLevel", &CryptoContextImpl::GetKeyGenLevel, cc_GetKeyGenLevel_docs) @@ -480,51 +636,6 @@ void bind_crypto_context(py::module &m) { py::arg("oldPrivateKey"), py::arg("newPrivateKey"), py::doc(cc_KeySwitchGen_docs)) - .def("EvalAdd", - py::overload_cast&, ConstCiphertext&>(&CryptoContextImpl::EvalAdd, py::const_), - py::arg("ciphertext1"), - py::arg("ciphertext2"), - py::doc(cc_EvalAdd_docs)) - .def("EvalAdd", - py::overload_cast&, double>(&CryptoContextImpl::EvalAdd, py::const_), - py::arg("ciphertext"), - py::arg("scalar"), - py::doc(cc_EvalAddfloat_docs)) - .def("EvalAdd", - py::overload_cast&, std::complex>(&CryptoContextImpl::EvalAdd, py::const_), - py::arg("ciphertext"), - py::arg("scalar"), - py::doc("")) - .def("EvalAdd", - py::overload_cast&, Plaintext&>(&CryptoContextImpl::EvalAdd, py::const_), - py::arg("ciphertext"), - py::arg("plaintext"), - py::doc(cc_EvalAddPlaintext_docs)) - .def("EvalAddInPlace", - py::overload_cast&, ConstCiphertext&>(&CryptoContextImpl::EvalAddInPlace, py::const_), - py::arg("ciphertext1"), - py::arg("ciphertext2"), - py::doc(cc_EvalAddInPlace_docs)) - .def("EvalAddInPlace", - py::overload_cast&, Plaintext&>(&CryptoContextImpl::EvalAddInPlace, py::const_), - py::arg("ciphertext"), - py::arg("plaintext"), - py::doc(cc_EvalAddInPlacePlaintext_docs)) - .def("EvalAddInPlace", - py::overload_cast&>(&CryptoContextImpl::EvalAddInPlace, py::const_), - py::arg("plaintext"), - py::arg("ciphertext"), - py::doc("")) // TODO (dsuponit): replace this with an actual docstring - .def("EvalAddInPlace", - py::overload_cast&, double>(&CryptoContextImpl::EvalAddInPlace, py::const_), - py::arg("ciphertext"), - py::arg("scalar"), - py::doc("")) // TODO (dsuponit): replace this with an actual docstring - .def("EvalAddInPlace", - py::overload_cast&>(&CryptoContextImpl::EvalAddInPlace, py::const_), - py::arg("scalar"), - py::arg("ciphertext"), - py::doc("")) // TODO (dsuponit): replace this with an actual docstring .def("EvalAddMutable", py::overload_cast&, Ciphertext&>(&CryptoContextImpl::EvalAddMutable, py::const_), py::arg("ciphertext1"), @@ -543,68 +654,6 @@ void bind_crypto_context(py::module &m) { py::arg("ciphertext1"), py::arg("ciphertext2"), py::doc(cc_EvalAddMutableInPlace_docs)) - .def("EvalSub", - py::overload_cast&, ConstCiphertext&>(&CryptoContextImpl::EvalSub, py::const_), - py::arg("ciphertext1"), - py::arg("ciphertext2"), - py::doc(cc_EvalSub_docs)) - .def("EvalSub", - py::overload_cast&, double>(&CryptoContextImpl::EvalSub, py::const_), - py::arg("ciphertext"), - py::arg("scalar"), - py::doc(cc_EvalSubfloat_docs)) - .def("EvalSub", - py::overload_cast&>(&CryptoContextImpl::EvalSub, py::const_), - py::arg("scalar"), - py::arg("ciphertext"), - py::doc("")) // TODO (dsuponit): replace this with an actual docstring - .def("EvalSub", - py::overload_cast&, std::complex>(&CryptoContextImpl::EvalSub, py::const_), - py::arg("ciphertext"), - py::arg("scalar"), - py::doc(cc_EvalSubfloat_docs)) - .def("EvalSub", - py::overload_cast, ConstCiphertext&>(&CryptoContextImpl::EvalSub, py::const_), - py::arg("scalar"), - py::arg("ciphertext"), - py::doc("")) // TODO (dsuponit): replace this with an actual docstring - .def("EvalSub", - py::overload_cast&, Plaintext&>(&CryptoContextImpl::EvalSub, py::const_), - py::arg("ciphertext"), - py::arg("plaintext"), - py::doc(cc_EvalSubPlaintext_docs)) - .def("EvalSub", - py::overload_cast&>(&CryptoContextImpl::EvalSub, py::const_), - py::arg("plaintext"), - py::arg("ciphertext"), - py::doc("")) // TODO (dsuponit): replace this with an actual docstring - .def("EvalSubInPlace", - py::overload_cast&, ConstCiphertext&>(&CryptoContextImpl::EvalSubInPlace, py::const_), - py::arg("ciphertext1"), - py::arg("ciphertext2"), - py::doc(cc_EvalSubInPlace_docs)) - .def("EvalSubInPlace", - py::overload_cast&, double>(&CryptoContextImpl::EvalSubInPlace, py::const_), - py::arg("ciphertext"), - py::arg("scalar"), - py::doc(cc_EvalSubInPlacefloat_docs)) - .def("EvalSubInPlace", - py::overload_cast&>(&CryptoContextImpl::EvalSubInPlace, py::const_), - py::arg("scalar"), - py::arg("ciphertext"), - py::doc("")) // TODO (dsuponit): replace this with an actual docstring - .def("EvalSubInPlace", - py::overload_cast&, ConstPlaintext&>( - &CryptoContextImpl::EvalSubInPlace, py::const_), - py::arg("ciphertext"), - py::arg("plaintext"), - py::doc("")) // TODO (dsuponit): replace this with an actual docstring - .def("EvalSubInPlace", - py::overload_cast&>( - &CryptoContextImpl::EvalSubInPlace, py::const_), - py::arg("plaintext"), - py::arg("ciphertext"), - py::doc("")) // TODO (dsuponit): replace this with an actual docstring .def("EvalSubMutable", py::overload_cast&, Ciphertext&>(&CryptoContextImpl::EvalSubMutable, py::const_), py::arg("ciphertext1"), @@ -624,61 +673,6 @@ void bind_crypto_context(py::module &m) { py::arg("ciphertext1"), py::arg("ciphertext2"), py::doc(cc_EvalSubMutableInPlace_docs)) - .def("EvalMult", - py::overload_cast&, ConstCiphertext&>(&CryptoContextImpl::EvalMult, py::const_), - py::arg("ciphertext1"), - py::arg("ciphertext2"), - py::doc(cc_EvalMult_docs)) - .def("EvalMult", - py::overload_cast&, double>(&CryptoContextImpl::EvalMult, py::const_), - py::arg("ciphertext"), - py::arg("scalar"), - py::doc(cc_EvalMultfloat_docs)) - .def("EvalMult", - py::overload_cast&>(&CryptoContextImpl::EvalMult, py::const_), - py::arg("scalar"), - py::arg("ciphertext"), - py::doc("")) // TODO (dsuponit): replace this with an actual docstring - .def("EvalMult", - py::overload_cast&, std::complex>(&CryptoContextImpl::EvalMult, py::const_), - py::arg("ciphertext"), - py::arg("scalar"), - py::doc(cc_EvalMultfloat_docs)) - .def("EvalMult", - py::overload_cast, ConstCiphertext&>(&CryptoContextImpl::EvalMult, py::const_), - py::arg("scalar"), - py::arg("ciphertext"), - py::doc("")) // TODO (dsuponit): replace this with an actual docstring - .def("EvalMult", - py::overload_cast&, ConstPlaintext&>(&CryptoContextImpl::EvalMult, py::const_), - py::arg("ciphertext"), - py::arg("plaintext"), - py::doc(cc_EvalMultPlaintext_docs)) - .def("EvalMult", - py::overload_cast&>(&CryptoContextImpl::EvalMult, py::const_), - py::arg("plaintext"), - py::arg("ciphertext"), - py::doc("")) // TODO (dsuponit): replace this with an actual docstring - .def("EvalMultInPlace", - static_cast&, double) const>(&CC::EvalMultInPlace), - py::arg("ciphertext"), - py::arg("scalar"), - py::doc("")) // TODO (dsuponit): replace this with an actual docstring - .def("EvalMultInPlace", - static_cast&) const>(&CC::EvalMultInPlace), - py::arg("scalar"), - py::arg("ciphertext"), - py::doc("")) // TODO (dsuponit): replace this with an actual docstring - .def("EvalMultInPlace", - static_cast&, std::complex) const>(&CC::EvalMultInPlace), - py::arg("ciphertext"), - py::arg("scalar"), - py::doc("")) // TODO (dsuponit): replace this with an actual docstring - .def("EvalMultInPlace", - static_cast, Ciphertext&) const>(&CC::EvalMultInPlace), - py::arg("scalar"), - py::arg("ciphertext"), - py::doc("")) // TODO (dsuponit): replace this with an actual docstring .def("EvalMultMutable", py::overload_cast&, Ciphertext&>(&CryptoContextImpl::EvalMultMutable, py::const_), py::arg("ciphertext1"), @@ -1245,6 +1239,29 @@ void bind_crypto_context(py::module &m) { bind_crypto_context_templates(cc_class); bind_crypto_context_templates>(cc_class); + bind_eval_add_family(cc_class); + bind_eval_add_scalar_overloads(cc_class); + bind_eval_add_scalar_overloads>(cc_class); + + bind_eval_addinplace_family(cc_class); + bind_eval_addinplace_scalar_overloads(cc_class); + bind_eval_addinplace_scalar_overloads>(cc_class); + + bind_eval_sub_family(cc_class); + bind_eval_sub_scalar_overloads(cc_class); + bind_eval_sub_scalar_overloads>(cc_class); + + bind_eval_subinplace_family(cc_class); + bind_eval_subinplace_scalar_overloads(cc_class); + bind_eval_subinplace_scalar_overloads>(cc_class); + + bind_eval_mult_family(cc_class); + bind_eval_mult_scalar_overloads(cc_class); + bind_eval_mult_scalar_overloads>(cc_class); + + bind_eval_multinplace_scalar_overloads(cc_class); + bind_eval_multinplace_scalar_overloads>(cc_class); + // Generator Functions m.def("GenCryptoContext", &GenCryptoContext, py::arg("params"));