-
Notifications
You must be signed in to change notification settings - Fork 169
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
Ns/feat/atomic patterns #2024
base: main
Are you sure you want to change the base?
Ns/feat/atomic patterns #2024
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we foresee that the ServerKeyAtomicPattern
is going to have a dynamic variant ServerKeyAtomicPattern::Dynamic(Box<dyn AtomicPattern>)
if yes, should the ServerKey just store a Box directly ?
Reviewed 9 of 45 files at r1, all commit messages.
Reviewable status: 9 of 45 files reviewed, 4 unresolved discussions (waiting on @nsarlin-zama)
tfhe/src/high_level_api/backward_compatibility/integers.rs
line 76 at r1 (raw file):
.as_view() .try_into() .map(|sk| old_sk_decompress(sk, a))
We could have let key = sk.key.key.key.as_view().try_into()?;
at the beginning of the fn, and use the key in the par_iter.map
Code quote:
.try_into()
.map(|sk| old_sk_decompress(sk, a))
tfhe/src/high_level_api/backward_compatibility/integers.rs
line 122 at r1 (raw file):
.as_view() .try_into() .map(|sk| old_sk_decompress(sk, a))
Same here, we could do`let key = sk.key.key.key.as_view().try_into()?;
Code quote:
sk.key
.key
.key
.as_view()
.try_into()
.map(|sk| old_sk_decompress(sk, a))
tfhe/src/shortint/atomic_pattern.rs
line 50 at r1 (raw file):
} pub trait AtomicPatternMutOperations {
I would have pub trait AtomicPatternMutOperations: AtomicPatternOperations
so that having the mut version also gives all the non mul ops
tfhe/src/shortint/server_key/mod.rs
line 406 at r1 (raw file):
impl<AP: Clone> GenericServerKey<&AP> { pub fn into_owned(&self) -> GenericServerKey<AP> {
Generally into_
method take self
, so I think a better name would be to_owned
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes the dynamic one is in progress
enum dispatch is supposedly more performant than vtable lookups, and some things are easier to do with enums, like serialization
Reviewable status: 9 of 45 files reviewed, 4 unresolved discussions (waiting on @tmontaigu)
tfhe/src/high_level_api/backward_compatibility/integers.rs
line 76 at r1 (raw file):
Previously, tmontaigu (tmontaigu) wrote…
We could have
let key = sk.key.key.key.as_view().try_into()?;
at the beginning of the fn, and use the key in the par_iter.map
good idea !
tfhe/src/shortint/atomic_pattern.rs
line 50 at r1 (raw file):
Previously, tmontaigu (tmontaigu) wrote…
I would have
pub trait AtomicPatternMutOperations: AtomicPatternOperations
so that having the mut version also gives all the non mul ops
ok I will do that!
tfhe/src/shortint/server_key/mod.rs
line 406 at r1 (raw file):
Previously, tmontaigu (tmontaigu) wrote…
Generally
into_
method takeself
, so I think a better name would beto_owned
ok, but to_owned is generally associated with the ToOwned
trait, so maybe there is a third option that is less confusing ?
41c1d18
to
a413178
Compare
b11e47a
to
1fc1441
Compare
14a920c
to
600a1b7
Compare
20843dd
to
4a759f6
Compare
4a759f6
to
74d237b
Compare
74d237b
to
7707a5b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed 8 of 45 files at r1, 5 of 14 files at r2, 51 of 74 files at r3, 36 of 56 files at r4, 179 of 179 files at r5, all commit messages.
Reviewable status: all files reviewed, 8 unresolved discussions (waiting on @mayeul-zama and @nsarlin-zama)
tfhe/src/shortint/server_key/mod.rs
line 406 at r1 (raw file):
Previously, nsarlin-zama (Nicolas Sarlin) wrote…
ok, but to_owned is generally associated with the
ToOwned
trait, so maybe there is a third option that is less confusing ?
I don't have one
tfhe/src/core_crypto/entities/compressed_modulus_switched_lwe_ciphertext.rs
line 78 at r3 (raw file):
log_modulus: CiphertextModulusLog, ) -> Self where
minor suggestion:
we could have kept the Cont
as the only generic and have
where
Cont: Container,
Cont::Element: UnsignedInteger + CastInto<PackingScalar>
Then in the assert Cont::Element::BITS
should work or the more verbose <Cont::Element as UnsignedInteger>::BITS
tfhe/src/shortint/atomic_pattern/mod.rs
line 143 at r3 (raw file):
impl< AP: 'static
I tend to think that a where clause would look better
tfhe/src/shortint/parameters/mod.rs
line 464 at r5 (raw file):
ShortintParameterSetInner::KS32PBS(params) => { let distribution = params.lwe_noise_distribution(); match distribution {
Isn't there a From
that is implemented ? I think I saw one 🤔
tfhe/src/shortint/engine/mod.rs
line 41 at r3 (raw file):
// This buffer will be converted when needed into temporary lwe ciphertexts, eventually by // splitting u128 blocks into smaller scalars buffer: Vec<u128>,
Since this now acts as a buffer that not always returns slices of u64, should this be a Vec ?
Was u128 chosen because it would give some alignment properties ?
There was a problem hiding this 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 @mayeul-zama and @tmontaigu)
tfhe/src/shortint/engine/mod.rs
line 41 at r3 (raw file):
Previously, tmontaigu (tmontaigu) wrote…
Since this now acts as a buffer that not always returns slices of u64, should this be a Vec ?
Was u128 chosen because it would give some alignment properties ?
Yes, the idea is to allocate with the strictest alignment so that any smaller scalar will also work.
What do you suggest instead of a vec? There might be a cleaner/simpler way that I missed!
tfhe/src/shortint/parameters/mod.rs
line 464 at r5 (raw file):
Previously, tmontaigu (tmontaigu) wrote…
Isn't there a
From
that is implemented ? I think I saw one 🤔
yes you're right, I forgot about it
d9949dd
to
6336cfc
Compare
There was a problem hiding this 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, 4 unresolved discussions (waiting on @mayeul-zama and @tmontaigu)
tfhe/src/core_crypto/entities/compressed_modulus_switched_lwe_ciphertext.rs
line 78 at r3 (raw file):
Previously, tmontaigu (tmontaigu) wrote…
minor suggestion:
we could have kept theCont
as the only generic and havewhere Cont: Container, Cont::Element: UnsignedInteger + CastInto<PackingScalar>Then in the assert
Cont::Element::BITS
should work or the more verbose<Cont::Element as UnsignedInteger>::BITS
done
tfhe/src/shortint/atomic_pattern/mod.rs
line 143 at r3 (raw file):
Previously, tmontaigu (tmontaigu) wrote…
I tend to think that a where clause would look better
done
tfhe/src/shortint/server_key/mod.rs
line 406 at r1 (raw file):
Previously, tmontaigu (tmontaigu) wrote…
I don't have one
I renamed it to simply sk.owned()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewable status: 30 of 230 files reviewed, 4 unresolved discussions (waiting on @mayeul-zama and @tmontaigu)
tfhe/src/shortint/parameters/mod.rs
line 464 at r5 (raw file):
Previously, nsarlin-zama (Nicolas Sarlin) wrote…
yes you're right, I forgot about it
fixed
Since only one kind is used at a time we don't need do allocate both
This allows to call it without having to define a max_noise_level
6336cfc
to
f2161a4
Compare
@@ -430,6 +430,30 @@ pub struct ServerKey { | |||
pub pbs_order: PBSOrder, | |||
} | |||
|
|||
/// Represents the number of elements in a [`LookupTable`] represented by a Glwe ciphertext | |||
#[derive(Copy, Clone, Debug)] | |||
pub struct LookupTableSize { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can be used for any GLWE, not only LUTs
So it could be called GlweParameters
Not sure having this type is very useful
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is supposed to be a bit more high level that the Glwe. Lookup table is at the shortint level and Glwe is at the core level. In theory I think you could use shortint without knowing what a glwe is.
The main goal is to reduce the number of parameters in the lut creation functions, because these two always go together.
And in the AtomicPattern
, since we always use them together we can do let size = ap.lookup_table_size()
in one call instead of having to do:
let glwe_size = ap.glwe_size();
let polynomial_size = ap.polynomial_size();
This is not crucial however!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modifications to this file in commit "insert the AP inside the ServerKey" could be part of commit "create atomic pattern trait and enum"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modifications to this file in commit "insert the AP inside the ServerKey" could be part of commit "create atomic pattern trait and enum"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@@ -48,7 +50,7 @@ pub enum AtomicPatternKind { | |||
/// The atomic pattern can be seen as a black box that will apply a lookup table and refresh the | |||
/// ciphertext noise to a nominal level. Between applications of the AP, it is possible to do a | |||
/// certain number of linear operations. | |||
pub trait AtomicPattern { | |||
pub trait AtomicPattern: std::fmt::Debug { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is Debug
necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without I cannot derive Debug for AtomicPatternServerKey
, and this is needed for the ServerKey
. I can certainly move the bound to the DynamicAtomicPattern trait if you prefer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved it to the DAP trait
}; | ||
|
||
LweCiphertextMutView::from_container(buffer, ciphertext_modulus) | ||
// This should not panic as long as `Scalar::BITS` is a divisor of 128 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe that could be asserted in this function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a panic with a more explicit error message
@@ -455,7 +457,11 @@ impl<C: Container<Element = u64>> ParameterSetConformant for LweKeyswitchKey<C> | |||
ciphertext_modulus, | |||
} = self; | |||
|
|||
*ciphertext_modulus == parameter_set.ciphertext_modulus | |||
let Ok(parameters_modulus) = parameter_set.ciphertext_modulus.try_to() else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this try_to
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a method on the CiphertextModulus:
pub fn try_to<ScalarTo: UnsignedInteger + CastInto<u128>>( |
Because in the parameters the CiphertextModulus is u64 and in LweKeyswitchKey
it is a generic, so this checks that they are compatible
@@ -433,6 +459,9 @@ impl ShortintParameterSet { | |||
ShortintParameterSetInner::PBSOnly(params) => params.lwe_noise_distribution(), | |||
ShortintParameterSetInner::WopbsOnly(params) => params.lwe_noise_distribution, | |||
ShortintParameterSetInner::PBSAndWopbs(params, _) => params.lwe_noise_distribution(), | |||
ShortintParameterSetInner::KS32PBS(params) => { | |||
params.lwe_noise_distribution().to_u64_distribution() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this function should not return a hardcoded DynamicDistribution<u64>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you have in mind ? I can't change the return type dynamically.
But imo this is not a problem because the generic is only used in the TUniform
, and a u32 bound is also a u64 bound so all DynamicDistribution<u32>
are valid DynamicDistribution<u64>
f2161a4
to
3693454
Compare
3693454
to
fc43be6
Compare
closes: https://github.com/zama-ai/tfhe-rs-internal/issues/643
PR content/description
This PR adds the notion of Atomic Pattern to the shortint layer. This is currently a draft to be able to discuss on the design.
Atomic Pattern
An atomic pattern is a sequence of homomorphic operations that can be executed indefinitely. In TFHE, the standard atomic pattern is the chain of 5 linear operations + KS + PBS. In TFHE-rs, this is implemented at the shortint level by the
ServerKey::apply_lookup_table
(to be precise this is only the KS/PBS part). The goal of the PR is to add a genericity layer to be able to easily switch atomic pattern without having to rewrite higher level operations.Implementation
(The names of the trait and types are not definitive)
Currently the shortint
ServerKey
is represented by this structure:We can split these fields in 2 parts:
To do that, this PR first adds a trait
AtomicPattern
. This trait defines the operations that should be supported by all the atomic patterns. It is dyn compatible to allows having atomic patterns as trait objects:This trait is first implemented for the "classical" (CJP) atomic pattern:
From there we have an enum of atomic pattern specific keys that all implement this trait:
The enum also implements
AtomicPattern
(the "enum dispatch" design pattern).Finally, we have the "GenericServerKey" (name not definitive) defined as follow:
Some type aliases are defined to make it more usable:
ServerKey
is the one that is used in the upper layers, this reduces the breaking changes. Every methods that use the ServerKey for lookup tables and the shortint encoding are usable without (almost) any modification.However some features (such as the WoPBS) don't fit well in the atomic pattern concept (as I understand it)
For these features, this design allows to create impl blocks that only work for one specific atomic pattern, by using the
ClassicalServerKey
type. To go from one type to the other,ClassicalServerKey
implementsTryFrom<ServerKey>
.To make this more efficient, we have 2 "View" types that allow conversions without having to clone the keys:
In the future, it should be easy to extend the set of supported AP for a feature.
For example we can have an
OprfServerKeyAtomicPattern
enum with only the subset of ap that support the oprf, and define a typeOprfServerKey = GenericServerKey<OprfServerKeyAtomicPattern>;
Dynamic AP
The
GenericServerKey
enum has a last specific variant:This allows to create a server key for an AP defined by the user. the trait
DynamicAtomicPattern
is sealed but automatically impl for most of the types that implement AtomicPattern. This trait allows to implClone
andPartialEq
for the dynamic case.KS32
This PR also implements the
DP-KS-PBS-AP-with-32-bit-KS-modulus
atomic pattern to have an example of what can be done using this structures.Check-list:
This change is