Skip to content

Commit dbf3e09

Browse files
Support BigUint plaintext moduli in BFV
- Added `PlaintextModulus` enum to `BfvParameters` to support both `Small` (u64) and `Large` (BigUint) moduli. - Updated `Plaintext` to store values in `PlaintextValues` enum (`Small` or `Large`). - Updated `PlaintextVec` encoders/decoders to handle both variants. - Updated `SecretKey` decryption to correctly lift RNS residues to `BigUint` for large moduli. - Added `default_biguint_arc` helper in `BfvParameters` for testing. - Added comprehensive unit tests in `crates/fhe/src/bfv/tests_biguint.rs` covering encryption, decryption, homomorphic addition, and multiplication (with/without relin). - Removed `num-integer` dependency and cleaned up code. - Fixed encoding logic to reduce `BigUint` inputs modulo `Small` plaintext moduli. - Updated `fhe.proto` to include optional `plaintext_big` field. Co-authored-by: tlepoint <1345502+tlepoint@users.noreply.github.com>
1 parent f67476a commit dbf3e09

23 files changed

Lines changed: 444 additions & 473 deletions

.github/workflows/lint-fmt.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
- uses: actions-rs/toolchain@v1
2121
with:
2222
profile: minimal
23-
toolchain: stable
23+
toolchain: nightly
2424
override: true
2525
components: rustfmt
2626
- uses: actions-rs/cargo@v1

Cargo.lock

Lines changed: 30 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ unused_imports = "warn"
1616
unused_must_use = "deny"
1717

1818
[workspace.dependencies]
19-
clap = { version = "^4.5.56", features = ["derive"] }
20-
tfhe-ntt = "^0.7.0"
19+
clap = { version = "^4.5.54", features = ["derive"] }
20+
tfhe-ntt = "^0.6.1"
2121
console = "^0.16.2"
2222
criterion = "^0.8.1"
2323
doc-comment = "^0.3.4"
@@ -37,7 +37,7 @@ pulp = "^0.22.2"
3737
rand = "^0.9.2"
3838
rand_chacha = "^0.9.0"
3939
sha2 = "^0.10.8"
40-
thiserror = "^2.0.18"
40+
thiserror = "^2.0.17"
4141
zeroize = "^1.8.2"
4242
zeroize_derive = "^1.4.3"
4343

crates/fhe-math/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ bench = false # Disable default bench (we use criterion)
1616

1717
[features]
1818
tfhe-ntt = []
19+
tfhe-ntt-nightly = ["tfhe-ntt/nightly"]
1920

2021
[dependencies]
2122
fhe-traits = { version = "=0.1.1", path = "../fhe-traits" }

crates/fhe-math/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ This crate exposes building blocks such as number theoretic transforms (NTT), re
77
## Features
88

99
* `ntt`, `rns`, `rq`, and `zq` modules for modular arithmetic over large rings.
10-
* Optional `tfhe-ntt` features to enable hardware accelerated NTTs via the [`tfhe-ntt`](https://crates.io/crates/tfhe-ntt) crate.
10+
* Optional `tfhe-ntt` and `tfhe-ntt-nightly` features to enable hardware accelerated NTTs via the [`tfhe-ntt`](https://crates.io/crates/tfhe-ntt) crate.
1111

1212
## Installation
1313

crates/fhe-math/benches/rq.rs

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
)]
66
use criterion::measurement::WallTime;
77
use criterion::{BenchmarkGroup, BenchmarkId, Criterion, criterion_group, criterion_main};
8-
use fhe_math::rq::{traits::TryConvertFrom, *};
8+
use fhe_math::rq::*;
99
use itertools::{Itertools, izip};
1010
use rand::rng;
1111
use std::{
@@ -292,36 +292,5 @@ pub fn rq_benchmark(c: &mut Criterion) {
292292
group.finish();
293293
}
294294

295-
pub fn rq_convert_benchmark(c: &mut Criterion) {
296-
let mut group = c.benchmark_group("rq_convert");
297-
group.warm_up_time(Duration::from_millis(100));
298-
group.measurement_time(Duration::from_secs(1));
299-
300-
for degree in DEGREE {
301-
for nmoduli in 1..=MODULI.len() {
302-
let ctx = Arc::new(Context::new(&MODULI[..nmoduli], *degree).unwrap());
303-
let v: Vec<u64> = (0..*degree as u64).collect();
304-
let slice = v.as_slice();
305-
306-
group.bench_function(
307-
BenchmarkId::new("try_convert_from_slice", format!("{}/{}", degree, nmoduli)),
308-
|b| {
309-
b.iter(|| {
310-
Poly::try_convert_from(slice, &ctx, false, Representation::PowerBasis)
311-
.unwrap()
312-
});
313-
},
314-
);
315-
}
316-
}
317-
group.finish();
318-
}
319-
320-
criterion_group!(
321-
rq,
322-
rq_op_benchmark,
323-
rq_dot_product,
324-
rq_benchmark,
325-
rq_convert_benchmark
326-
);
295+
criterion_group!(rq, rq_op_benchmark, rq_dot_product, rq_benchmark);
327296
criterion_main!(rq);

crates/fhe-math/src/ntt/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ use fhe_util::is_prime;
44

55
mod native;
66

7-
#[cfg(feature = "tfhe-ntt")]
7+
#[cfg(any(feature = "tfhe-ntt", feature = "tfhe-ntt-nightly"))]
88
mod tfhe;
99

10-
#[cfg(not(feature = "tfhe-ntt"))]
10+
#[cfg(not(any(feature = "tfhe-ntt", feature = "tfhe-ntt-nightly")))]
1111
pub use native::NttOperator;
12-
#[cfg(feature = "tfhe-ntt")]
12+
#[cfg(any(feature = "tfhe-ntt", feature = "tfhe-ntt-nightly"))]
1313
pub use tfhe::NttOperator;
1414

1515
/// Returns whether a modulus p is prime and supports the Number Theoretic

crates/fhe-math/src/ntt/tfhe.rs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,10 @@ impl NttOperator {
7777
/// about the value being reduced.
7878
pub(crate) unsafe fn forward_vt_lazy(&self, a_ptr: *mut u64) {
7979
if let Some(ref tfhe_operator) = self.tfhe_operator {
80-
let a = unsafe { std::slice::from_raw_parts_mut(a_ptr, tfhe_operator.ntt_size()) };
80+
let a = std::slice::from_raw_parts_mut(a_ptr, tfhe_operator.ntt_size());
8181
tfhe_operator.fwd(a);
8282
} else {
83-
unsafe {
84-
self.native_operator.forward_vt_lazy(a_ptr);
85-
}
83+
self.native_operator.forward_vt_lazy(a_ptr);
8684
}
8785
}
8886

@@ -94,12 +92,10 @@ impl NttOperator {
9492
/// about the value being reduced.
9593
pub unsafe fn forward_vt(&self, a_ptr: *mut u64) {
9694
if let Some(ref tfhe_operator) = self.tfhe_operator {
97-
let a = unsafe { std::slice::from_raw_parts_mut(a_ptr, tfhe_operator.ntt_size()) };
95+
let a = std::slice::from_raw_parts_mut(a_ptr, tfhe_operator.ntt_size());
9896
tfhe_operator.fwd(a);
9997
} else {
100-
unsafe {
101-
self.native_operator.forward_vt(a_ptr);
102-
}
98+
self.native_operator.forward_vt(a_ptr);
10399
}
104100
}
105101

@@ -111,13 +107,11 @@ impl NttOperator {
111107
/// about the value being reduced.
112108
pub unsafe fn backward_vt(&self, a_ptr: *mut u64) {
113109
if let Some(ref tfhe_operator) = self.tfhe_operator {
114-
let a = unsafe { std::slice::from_raw_parts_mut(a_ptr, tfhe_operator.ntt_size()) };
110+
let a = std::slice::from_raw_parts_mut(a_ptr, tfhe_operator.ntt_size());
115111
tfhe_operator.inv(a);
116112
tfhe_operator.normalize(a);
117113
} else {
118-
unsafe {
119-
self.native_operator.backward_vt(a_ptr);
120-
}
114+
self.native_operator.backward_vt(a_ptr);
121115
}
122116
}
123117
}

crates/fhe-math/src/rq/convert.rs

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -252,40 +252,7 @@ impl<'a> TryConvertFrom<&'a [u64]> for Poly {
252252
where
253253
R: Into<Option<Representation>>,
254254
{
255-
let repr = representation.into();
256-
match repr {
257-
Some(Representation::PowerBasis) => {
258-
if v.len() == ctx.q.len() * ctx.degree {
259-
Poly::try_convert_from(v.to_vec(), ctx, variable_time, repr)
260-
} else if v.len() <= ctx.degree {
261-
let mut out = Self::zero(ctx, Representation::PowerBasis);
262-
if variable_time {
263-
unsafe {
264-
izip!(out.coefficients.outer_iter_mut(), ctx.q.iter()).for_each(
265-
|(mut w, qi)| {
266-
let wi = w.as_slice_mut().unwrap();
267-
wi[..v.len()].copy_from_slice(v);
268-
qi.reduce_vec_vt(wi);
269-
},
270-
);
271-
out.allow_variable_time_computations();
272-
}
273-
} else {
274-
izip!(out.coefficients.outer_iter_mut(), ctx.q.iter()).for_each(
275-
|(mut w, qi)| {
276-
let wi = w.as_slice_mut().unwrap();
277-
wi[..v.len()].copy_from_slice(v);
278-
qi.reduce_vec(wi);
279-
},
280-
);
281-
}
282-
Ok(out)
283-
} else {
284-
Err(Error::Default("In PowerBasis representation, either all coefficients must be specified, or only coefficients up to the degree".to_string()))
285-
}
286-
}
287-
_ => Poly::try_convert_from(v.to_vec(), ctx, variable_time, repr),
288-
}
255+
Poly::try_convert_from(v.to_vec(), ctx, variable_time, representation)
289256
}
290257
}
291258

crates/fhe-math/src/rq/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -877,7 +877,7 @@ mod tests {
877877
for i in 1..=16 {
878878
let p = Poly::small(&ctx, Representation::PowerBasis, i, &mut rng)?;
879879
let coefficients = p.coefficients().to_slice().unwrap();
880-
let v = q.center_vec(coefficients);
880+
let v = unsafe { q.center_vec_vt(coefficients) };
881881

882882
assert!(v.iter().map(|vi| vi.abs()).max().unwrap() <= 2 * i as i64);
883883
}
@@ -889,7 +889,7 @@ mod tests {
889889
let mut rng = rand::rng();
890890
let p = Poly::small(&ctx, Representation::PowerBasis, 16, &mut rng)?;
891891
let coefficients = p.coefficients().to_slice().unwrap();
892-
let v = q.center_vec(coefficients);
892+
let v = unsafe { q.center_vec_vt(coefficients) };
893893
assert!(v.iter().map(|vi| vi.abs()).max().unwrap() <= 32);
894894
assert_eq!(variance(&v).round(), 16.0);
895895

0 commit comments

Comments
 (0)