From 6550542073fdc7d4216960431ae5ab12a8c6221c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Sanz=20Gonz=C3=A1lez?= Date: Mon, 1 Dec 2025 13:55:18 +0100 Subject: [PATCH] Fix evaluation of linear functions with `EvalPoly` The `internalEvalPolyLinearWithPrecomp` contained an off-by-one error that caused it to throw an exception when the polynomial to evaluate was a linear function. This patch fixes that mistake, and adds a test that evaluates a linear polynomial on a CKKS ciphertext. Bug discovered and patched at AccelCom, Barcelona Supercomputing Center. --- .../lib/scheme/ckksrns/ckksrns-advancedshe.cpp | 5 +++-- src/pke/unittest/utckksrns/UnitTestCKKSrns.cpp | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/pke/lib/scheme/ckksrns/ckksrns-advancedshe.cpp b/src/pke/lib/scheme/ckksrns/ckksrns-advancedshe.cpp index d9b59abd6..8262923bb 100644 --- a/src/pke/lib/scheme/ckksrns/ckksrns-advancedshe.cpp +++ b/src/pke/lib/scheme/ckksrns/ckksrns-advancedshe.cpp @@ -331,9 +331,10 @@ std::shared_ptr> AdvancedSHECKKSRNS::EvalPowers( template static inline Ciphertext internalEvalPolyLinearWithPrecomp(std::vector>& powers, const std::vector& coefficients) { + if (coefficients.size() < 2) + OPENFHE_THROW("EvalPolyLinear: The coefficients vector should contain at least 2 elements"); + uint32_t k = coefficients.size() - 1; - if (k <= 1) - OPENFHE_THROW("The coefficients vector should contain at least 2 elements"); if (!IsNotEqualZero(coefficients[k])) OPENFHE_THROW("EvalPolyLinear: The highest-order coefficient cannot be set to 0."); diff --git a/src/pke/unittest/utckksrns/UnitTestCKKSrns.cpp b/src/pke/unittest/utckksrns/UnitTestCKKSrns.cpp index 91240c0b4..e6526d89b 100644 --- a/src/pke/unittest/utckksrns/UnitTestCKKSrns.cpp +++ b/src/pke/unittest/utckksrns/UnitTestCKKSrns.cpp @@ -1801,6 +1801,9 @@ class UTCKKSRNS : public ::testing::TestWithParam { // x + x^2 - x^3 // low-degree function to check linear implementation std::vector coefficients5{0, 1, 1, -1}; + // 1 + 2x + // linear function to check linear implementation + std::vector coefficients6{1.0, 2.0}; Plaintext plaintext1 = cc->MakeCKKSPackedPlaintext(input); @@ -1819,6 +1822,9 @@ class UTCKKSRNS : public ::testing::TestWithParam { std::vector> output5{0.625, 0.847, 0.9809999999, 0.995125, 0.990543}; Plaintext plaintextResult5 = cc->MakeCKKSPackedPlaintext(output5); + std::vector> output6{2.0, 2.4, 2.8, 2.9, 2.86}; + Plaintext plaintextResult6 = cc->MakeCKKSPackedPlaintext(output6); + // Generate encryption keys KeyPair kp = cc->KeyGen(); // Generate multiplication keys @@ -1878,6 +1884,16 @@ class UTCKKSRNS : public ::testing::TestWithParam { << " - we get: " << results5->GetCKKSPackedValue(); checkEquality(plaintextResult5->GetCKKSPackedValue(), results5->GetCKKSPackedValue(), epsHigh, failmsg + " EvalPoly for low-degree polynomial failed: " + buffer5.str()); + + Ciphertext cResult6 = cc->EvalPolyLinear(ciphertext1, coefficients6); + Plaintext results6; + cc->Decrypt(kp.secretKey, cResult6, &results6); + results6->SetLength(encodedLength); + std::stringstream buffer6; + buffer6 << "should be: " << plaintextResult6->GetCKKSPackedValue() + << " - we get: " << results6->GetCKKSPackedValue(); + checkEquality(plaintextResult6->GetCKKSPackedValue(), results6->GetCKKSPackedValue(), epsHigh, + failmsg + " EvalPoly for linear polynomial failed: " + buffer6.str()); } catch (std::exception& e) { std::cerr << "Exception thrown from " << __func__ << "(): " << e.what() << std::endl;