added Map based on FnvHashMap, and AutoPermMap, generalized gal_el
Jean-Philippe Bossuat jeanphilippe.bossuat@gmail.com
Thu, 16 Jan 2025 16:08:22 +0100
10 files changed,
111 insertions(+),
45 deletions(-)
M
Cargo.lock
→
Cargo.lock
@@ -199,6 +199,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] name = "getrandom" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index"@@ -309,6 +315,7 @@ "prime_factorization",
"rand_distr", "sampling", "sprs", + "utils", ] [[package]]@@ -732,6 +739,13 @@ name = "unicode-ident"
version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "utils" +version = "0.1.0" +dependencies = [ + "fnv", +] [[package]] name = "walkdir"
M
Cargo.toml
→
Cargo.toml
@@ -1,2 +1,2 @@
[workspace] -members = ["math", "sampling"] +members = ["math", "sampling", "utils"]
M
math/Cargo.toml
→
math/Cargo.toml
@@ -16,6 +16,7 @@ rand_distr = "0.4.3"
sprs = "0.11.2" sampling = { path = "../sampling" } +utils = { path = "../utils" } [[bench]] name = "ntt"
M
math/src/automorphism.rs
→
math/src/automorphism.rs
@@ -1,4 +1,29 @@
use crate::modulus::WordOps; +use crate::ring::Ring; +use num::Unsigned; +use utils::map::Map; + +pub struct AutoPermMap(Map<usize, AutoPerm>); + +impl AutoPermMap { + pub fn new() -> Self { + Self { + 0: Map::<usize, AutoPerm>::new(), + } + } + + pub fn insert(&mut self, perm: AutoPerm) { + self.0.insert(perm.gal_el, perm); + } + + pub fn gen<O: Unsigned, const NTT: bool>(&mut self, ring: &Ring<O>, gen_1: usize, gen_2: bool) { + self.insert(AutoPerm::new::<O, NTT>(ring, gen_1, gen_2)) + } + + pub fn get(&self, gal_el: &usize) -> Option<&AutoPerm> { + self.0.get(gal_el) + } +} pub struct AutoPerm { pub gal_el: usize,@@ -9,29 +34,21 @@ impl AutoPerm {
/// Returns a lookup table for the automorphism X^{i} -> X^{i * k mod nth_root}. /// Method will panic if n or nth_root are not power-of-two. /// Method will panic if gal_el is not coprime with nth_root. - pub fn new<const NTT: bool>(n: usize, gal_el: usize, nth_root: usize) -> Self { - assert!(n & (n - 1) == 0, "invalid n={}: not a power-of-two", n); - assert!( - nth_root & (nth_root - 1) == 0, - "invalid nth_root={}: not a power-of-two", - n - ); - assert!( - gal_el & 1 == 1, - "invalid gal_el={}: not coprime with nth_root={}", - gal_el, - nth_root - ); + pub fn new<O: Unsigned, const NTT: bool>(ring: &Ring<O>, gen_1: usize, gen_2: bool) -> Self { + let n = ring.n(); + let cyclotomic_order = ring.cyclotomic_order(); + + let gal_el = ring.galois_element(gen_1, gen_2); let mut permutation: Vec<usize> = Vec::with_capacity(n); if NTT { - let mask = nth_root - 1; - let log_nth_root_half: u32 = nth_root.log2() as u32 - 1; + let mask = cyclotomic_order - 1; + let log_cyclotomic_order_half: u32 = cyclotomic_order.log2() as u32 - 1; for i in 0..n { - let i_rev: usize = 2 * i.reverse_bits_msb(log_nth_root_half) + 1; + let i_rev: usize = 2 * i.reverse_bits_msb(log_cyclotomic_order_half) + 1; let gal_el_i: usize = ((gal_el * i_rev) & mask) >> 1; - permutation.push(gal_el_i.reverse_bits_msb(log_nth_root_half)); + permutation.push(gal_el_i.reverse_bits_msb(log_cyclotomic_order_half)); } } else { let log_n: usize = n.log2();
M
math/src/ring.rs
→
math/src/ring.rs
@@ -1,15 +1,16 @@
pub mod impl_u64; - use crate::dft::DFT; use crate::modulus::prime::Prime; use crate::modulus::WordOps; use crate::poly::{Poly, PolyRNS}; +use crate::GALOISGENERATOR; use num::traits::Unsigned; use std::rc::Rc; pub struct Ring<O: Unsigned> { pub n: usize, pub modulus: Prime<O>, + pub cyclotomic_order: usize, pub dft: Box<dyn DFT<O>>, }@@ -24,6 +25,33 @@ }
pub fn new_poly(&self) -> Poly<u64> { Poly::<u64>::new(self.n()) + } + + pub fn cyclotomic_order(&self) -> usize { + self.cyclotomic_order + } + + // Returns GALOISGENERATOR^gen_1 * (-1)^gen_2 mod 2^log_nth_root. + pub fn galois_element(&self, gen_1: usize, gen_2: bool) -> usize { + let mut gal_el: usize = 1; + let mut gen_1_pow: usize = GALOISGENERATOR; + let mut e: usize = gen_1; + while e > 0 { + if e & 1 == 1 { + gal_el = gal_el.wrapping_mul(gen_1_pow); + } + + gen_1_pow = gen_1_pow.wrapping_mul(gen_1_pow); + e >>= 1; + } + + let nth_root = 1 << self.cyclotomic_order; + gal_el &= nth_root - 1; + + if gen_2 { + return nth_root - gal_el; + } + gal_el } }
M
math/src/ring/impl_u64/ring.rs
→
math/src/ring/impl_u64/ring.rs
@@ -7,7 +7,6 @@ use crate::modulus::{BARRETT, REDUCEMOD};
use crate::poly::Poly; use crate::ring::Ring; use crate::CHUNK; -use crate::GALOISGENERATOR; use num_bigint::BigInt; use num_traits::ToPrimitive;@@ -17,6 +16,7 @@ let prime: Prime<u64> = Prime::<u64>::new(q_base, q_power);
Self { n: n, modulus: prime.clone(), + cyclotomic_order: n << 1, dft: Box::new(Table::<u64>::new(prime, n << 1)), } }@@ -39,29 +39,6 @@ a.0.iter_mut()
.step_by(step) .enumerate() .for_each(|(i, v)| *v = (&coeffs[i] % &q_big).to_u64().unwrap()); - } - - // Returns GALOISGENERATOR^gen_1 * (-1)^gen_2 mod 2^log_nth_root. - pub fn galois_element(&self, gen_1: usize, gen_2: bool, log_nth_root: usize) -> usize { - let mut gal_el: usize = 1; - let mut gen_1_pow: usize = GALOISGENERATOR; - let mut e: usize = gen_1; - while e > 0 { - if e & 1 == 1 { - gal_el = gal_el.wrapping_mul(gen_1_pow); - } - - gen_1_pow = gen_1_pow.wrapping_mul(gen_1_pow); - e >>= 1; - } - - let nth_root = 1 << log_nth_root; - gal_el &= nth_root - 1; - - if gen_2 { - return nth_root - gal_el; - } - gal_el } }
M
math/tests/automorphism.rs
→
math/tests/automorphism.rs
@@ -77,9 +77,10 @@ if NTT {
ring.ntt_inplace::<false>(&mut p0); } - let gal_el: usize = 2 * nth_root - 1; + let gen_1 = 0; + let gen_2 = true; - let auto_perm = AutoPerm::new::<NTT>(n, gal_el, nth_root); + let auto_perm = AutoPerm::new::<_, NTT>(&ring, gen_1, gen_2); ring.a_apply_automorphism_from_perm_into_b::<NTT>(&p0, &auto_perm, &mut p1);
A
utils/Cargo.toml
@@ -0,0 +1,7 @@
+[package] +name = "utils" +version = "0.1.0" +edition = "2021" + +[dependencies] +fnv = "1.0.7"
A
utils/src/map.rs
@@ -0,0 +1,20 @@
+use fnv::FnvHashMap; +use std::hash::Hash; + +pub struct Map<K, V>(pub FnvHashMap<K, V>); + +impl<K: Eq + Hash, V> Map<K, V> { + pub fn new() -> Self { + Self { + 0: FnvHashMap::<K, V>::default(), + } + } + + pub fn insert(&mut self, k: K, data: V) -> Option<V> { + self.0.insert(k, data) + } + + pub fn get(&self, k: &K) -> Option<&V> { + self.0.get(k) + } +}