Skip to content

Conversation

@nsarlin-zama
Copy link
Contributor

@nsarlin-zama nsarlin-zama commented Nov 14, 2025

closes: please link all relevant issues

PR content/description

Add blind rotation using karatsuba polynomial multiplication

TODO

  • Add some tests
  • Fix comments mentioning FFT
  • Find a better heuristic for the computation buffer size

This change is Reviewable

@cla-bot cla-bot bot added the cla-signed label Nov 14, 2025
@nsarlin-zama nsarlin-zama force-pushed the ns/feat/karatsuba_br branch 4 times, most recently from e2f8c15 to 038d0e4 Compare November 25, 2025 15:47
@nsarlin-zama nsarlin-zama marked this pull request as ready for review November 25, 2025 15:48
@nsarlin-zama
Copy link
Contributor Author

During review, please check that the heuristics for the memory requirements look correct, I am not really sure about that

@nsarlin-zama nsarlin-zama force-pushed the ns/feat/karatsuba_br branch 3 times, most recently from 587e3b3 to 4d6c70f Compare November 26, 2025 10:41
Copy link
Contributor

@mayeul-zama mayeul-zama left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mayeul-zama reviewed 15 of 15 files at r1, all commit messages.
Reviewable status: all files reviewed, 8 unresolved discussions (waiting on @IceTDrinker)


tfhe/src/core_crypto/algorithms/lwe_programmable_bootstrapping/karatsuba_pbs.rs line 205 at r1 (raw file):

    lut: &mut GlweCiphertext<OutputCont>,
    bsk: &LweBootstrapKey<KeyCont>,
    stack: &mut PodStack,

I don't think we care much about avoiding allocations here (as the perf is not the main concern here)

Removing stacks and allocating would make the code simpler and less likely to fail because of errors in scratch function


tfhe/src/core_crypto/algorithms/lwe_programmable_bootstrapping/karatsuba_pbs.rs line 267 at r1 (raw file):

            // We rotate ct_1 and subtract ct_0 (first step of cmux) by performing
            // ct_1 <- (ct_0 * X^{a_hat}) - ct_0

X^{a_hat} could be written X^a_i (to match proposed comment a few lines lower)


tfhe/src/core_crypto/algorithms/lwe_programmable_bootstrapping/karatsuba_pbs.rs line 281 at r1 (raw file):

            // as_mut_view is required to keep borrow rules consistent
            // second step of cmux
            karatsuba_add_external_product_assign(

Could add a comment
ct_0 <- ct_0 + ct1s_i
with ct_0 + ct1
s_i = ct_0 + ((ct_0 * X^a_i) - ct_0)s_i = if s_i= 0 { ct_0 } else {ct_0 * X^a} = ct_0 * X^(a_is_i)


tfhe/src/core_crypto/algorithms/lwe_programmable_bootstrapping/karatsuba_pbs.rs line 335 at r1 (raw file):

    let mut is_output_uninit = true;

    // ------------------------------------------------------ EXTERNAL PRODUCT IN FOURIER DOMAIN

Looks wrong after copy paste


tfhe/src/core_crypto/algorithms/lwe_programmable_bootstrapping/karatsuba_pbs.rs line 348 at r1 (raw file):

    // We loop through the levels (we reverse to match the order of the decomposition iterator.)
    ggsw.iter().for_each(|ggsw_decomp_matrix| {

Simpler to a simple for loop IMO


tfhe/src/core_crypto/algorithms/lwe_programmable_bootstrapping/karatsuba_pbs.rs line 374 at r1 (raw file):

            glwe_decomp_term.as_polynomial_list().iter()
        )
        .for_each(|(ggsw_row, glwe_poly)| {

Simpler to a simple for loop IMO


tfhe/src/core_crypto/algorithms/lwe_programmable_bootstrapping/karatsuba_pbs.rs line 375 at r1 (raw file):

        )
        .for_each(|(ggsw_row, glwe_poly)| {
            // let (fourier, substack3) =

Comments to remove I guess


tfhe/src/core_crypto/algorithms/test/lwe_programmable_bootstrapping.rs line 1231 at r1 (raw file):

            );

            programmable_bootstrap_karatsuba_lwe_ciphertext(

Makes me think could be interesting to test the blind rotate alone

It would be to compare the decrypted glwe result with the lut rotated by the decrypted modswitched input lwe

Would be a "more unit" test by removing the input noise/redundancy concern and leaving only the output noise concern

I don't think it exists for the fft blind rotate so not saying it should exits here

Copy link
Contributor Author

@nsarlin-zama nsarlin-zama left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: all files reviewed, 8 unresolved discussions (waiting on @IceTDrinker and @mayeul-zama)


tfhe/src/core_crypto/algorithms/lwe_programmable_bootstrapping/karatsuba_pbs.rs line 205 at r1 (raw file):

Previously, mayeul-zama wrote…

I don't think we care much about avoiding allocations here (as the perf is not the main concern here)

Removing stacks and allocating would make the code simpler and less likely to fail because of errors in scratch function

I need this at least for the TensorSignedDecompositionLendingIter I believe ?


tfhe/src/core_crypto/algorithms/lwe_programmable_bootstrapping/karatsuba_pbs.rs line 267 at r1 (raw file):

Previously, mayeul-zama wrote…

X^{a_hat} could be written X^a_i (to match proposed comment a few lines lower)

done


tfhe/src/core_crypto/algorithms/lwe_programmable_bootstrapping/karatsuba_pbs.rs line 281 at r1 (raw file):

Previously, mayeul-zama wrote…

Could add a comment
ct_0 <- ct_0 + ct1s_i
with ct_0 + ct1
s_i = ct_0 + ((ct_0 * X^a_i) - ct_0)s_i = if s_i= 0 { ct_0 } else {ct_0 * X^a} = ct_0 * X^(a_is_i)

done


tfhe/src/core_crypto/algorithms/lwe_programmable_bootstrapping/karatsuba_pbs.rs line 335 at r1 (raw file):

Previously, mayeul-zama wrote…

Looks wrong after copy paste

removed


tfhe/src/core_crypto/algorithms/lwe_programmable_bootstrapping/karatsuba_pbs.rs line 348 at r1 (raw file):

Previously, mayeul-zama wrote…

Simpler to a simple for loop IMO

done


tfhe/src/core_crypto/algorithms/lwe_programmable_bootstrapping/karatsuba_pbs.rs line 374 at r1 (raw file):

Previously, mayeul-zama wrote…

Simpler to a simple for loop IMO

done


tfhe/src/core_crypto/algorithms/lwe_programmable_bootstrapping/karatsuba_pbs.rs line 375 at r1 (raw file):

Previously, mayeul-zama wrote…

Comments to remove I guess

done


tfhe/src/core_crypto/algorithms/test/lwe_programmable_bootstrapping.rs line 1231 at r1 (raw file):

Previously, mayeul-zama wrote…

Makes me think could be interesting to test the blind rotate alone

It would be to compare the decrypted glwe result with the lut rotated by the decrypted modswitched input lwe

Would be a "more unit" test by removing the input noise/redundancy concern and leaving only the output noise concern

I don't think it exists for the fft blind rotate so not saying it should exits here

yes I'm not sure I will do it for karatsuba if it's not done for fft

Copy link
Contributor

@mayeul-zama mayeul-zama left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mayeul-zama reviewed 1 of 1 files at r2, all commit messages.
Reviewable status: all files reviewed, 5 unresolved discussions (waiting on @IceTDrinker and @nsarlin-zama)


tfhe/src/core_crypto/algorithms/lwe_programmable_bootstrapping/karatsuba_pbs.rs line 205 at r1 (raw file):

Previously, nsarlin-zama (Nicolas Sarlin) wrote…

I need this at least for the TensorSignedDecompositionLendingIter I believe ?

Then the pattern could be allocating a Vec, create a stack and passing it to TensorSignedDecompositionLendingIter where TensorSignedDecompositionLendingIter is needed
And using simple Vecs in other places

Would be simpler IMO. What do you think ?


tfhe/src/core_crypto/algorithms/lwe_programmable_bootstrapping/karatsuba_pbs.rs line 267 at r1 (raw file):

Previously, nsarlin-zama (Nicolas Sarlin) wrote…

done

Small detail: removing {} arount it would make reading a bit easier IMO


tfhe/src/core_crypto/algorithms/test/lwe_programmable_bootstrapping.rs line 1231 at r1 (raw file):

Previously, nsarlin-zama (Nicolas Sarlin) wrote…

yes I'm not sure I will do it for karatsuba if it's not done for fft

Fair


tfhe/src/core_crypto/algorithms/lwe_programmable_bootstrapping/karatsuba_pbs.rs line 283 at r2 (raw file):

            // ct_0 <- ct_0 + ct1s_i
            // with ct_0 + ct1s_i = ct_0 + ((ct_0 * X^a_i) - ct_0)s_i
            //                    = { ct_0 } if s_i= 0  else

if cond {} else {} is clearer the {} if cond else {} IMO


tfhe/src/core_crypto/algorithms/lwe_programmable_bootstrapping/karatsuba_pbs.rs line 284 at r2 (raw file):

            // with ct_0 + ct1s_i = ct_0 + ((ct_0 * X^a_i) - ct_0)s_i
            //                    = { ct_0 } if s_i= 0  else
            //                      {ct_0 * X^a} = ct_0 * X^(a_i * s_i)

else block in wrong line
If it makes the previous line is too long, the = could all be at line start

Also, a instead of a_i (mistake in my suggested snippet :) )

Copy link
Contributor Author

@nsarlin-zama nsarlin-zama left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: all files reviewed, 5 unresolved discussions (waiting on @IceTDrinker and @mayeul-zama)


tfhe/src/core_crypto/algorithms/lwe_programmable_bootstrapping/karatsuba_pbs.rs line 205 at r1 (raw file):

Previously, mayeul-zama wrote…

Then the pattern could be allocating a Vec, create a stack and passing it to TensorSignedDecompositionLendingIter where TensorSignedDecompositionLendingIter is needed
And using simple Vecs in other places

Would be simpler IMO. What do you think ?

on the other hands it makes this code more similar to the fft one, so by keeping them close it's less likely to have a bug on one side and not on the other ?
And TensorSignedDecompositionLendingIter is where I'm less confident about the stack requirements...


tfhe/src/core_crypto/algorithms/lwe_programmable_bootstrapping/karatsuba_pbs.rs line 267 at r1 (raw file):

Previously, mayeul-zama wrote…

Small detail: removing {} arount it would make reading a bit easier IMO

I guess the {} comes from Latex but agree it's more readable without


tfhe/src/core_crypto/algorithms/lwe_programmable_bootstrapping/karatsuba_pbs.rs line 283 at r2 (raw file):

Previously, mayeul-zama wrote…

if cond {} else {} is clearer the {} if cond else {} IMO

I was kind of emulating the math notation like this:
ByYOt.png

Hence the cases on different lines. Since it's not really an assignment I find it weird to have an "algorithmic" notation but maybe it's just me.

I reorganised it a bit.


tfhe/src/core_crypto/algorithms/lwe_programmable_bootstrapping/karatsuba_pbs.rs line 284 at r2 (raw file):

Previously, mayeul-zama wrote…

else block in wrong line
If it makes the previous line is too long, the = could all be at line start

Also, a instead of a_i (mistake in my suggested snippet :) )

done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants