binius_math/ntt/
domain_context.rs1use binius_field::BinaryField;
6
7use super::DomainContext;
8use crate::BinarySubspace;
9
10fn generate_evals_from_subspace<F: BinaryField>(subspace: &BinarySubspace<F>) -> Vec<Vec<F>> {
13 let mut evals = Vec::with_capacity(subspace.dim());
14
15 evals.push(subspace.basis().to_vec());
17 for i in 1..subspace.dim() {
18 evals.push(Vec::new());
20 for k in 1..evals[i - 1].len() {
21 let val = evals[i - 1][k] * (evals[i - 1][k] + evals[i - 1][0]);
25 evals[i].push(val);
26 }
27 }
28
29 for evals_i in evals.iter_mut() {
31 let w_i_b_i_inverse = evals_i[0].invert().unwrap();
32 for eval_i_j in evals_i.iter_mut() {
33 *eval_i_j *= w_i_b_i_inverse;
34 }
35 }
36
37 assert_eq!(evals.len(), subspace.dim());
38
39 evals
40}
41
42#[derive(Debug)]
49pub struct GenericOnTheFly<F> {
50 evals: Vec<Vec<F>>,
52}
53
54impl<F: BinaryField> GenericOnTheFly<F> {
55 pub fn generate_from_subspace(subspace: &BinarySubspace<F>) -> Self {
59 let evals = generate_evals_from_subspace(subspace);
60 Self { evals }
61 }
62}
63
64impl<F: BinaryField> DomainContext for GenericOnTheFly<F> {
65 type Field = F;
66
67 fn log_domain_size(&self) -> usize {
68 self.evals.len()
69 }
70
71 fn subspace(&self, i: usize) -> BinarySubspace<F> {
72 if i == 0 {
73 return BinarySubspace::with_dim(0).unwrap();
74 }
75 BinarySubspace::new_unchecked(self.evals[self.log_domain_size() - i].clone())
76 }
77
78 fn twiddle(&self, layer: usize, mut block: usize) -> F {
79 let v = &self.evals[self.log_domain_size() - layer - 1];
80 let mut twiddle = F::ZERO;
81
82 let mut i = 1;
83 while block != 0 {
84 twiddle += v[i] * binius_field::BinaryField1b::from((block & 1) == 1);
85 i += 1;
86 block >>= 1;
87 }
88
89 twiddle
90 }
91}
92
93#[derive(Debug)]
95pub struct GenericPreExpanded<F> {
96 evals: Vec<Vec<F>>,
98 expanded: Vec<Vec<F>>,
104}
105
106impl<F: BinaryField> GenericPreExpanded<F> {
107 pub fn generate_from_subspace(subspace: &BinarySubspace<F>) -> Self {
111 let evals = generate_evals_from_subspace(subspace);
112
113 let mut expanded = Vec::with_capacity(evals.len());
114 for basis in evals.iter().rev() {
115 let mut expanded_i = Vec::with_capacity(1 << (basis.len() - 1));
116 expanded_i.push(F::ZERO);
117 for i in 1..basis.len() {
118 for j in 0..expanded_i.len() {
119 expanded_i.push(expanded_i[j] + basis[i]);
120 }
121 }
122 assert_eq!(expanded_i.len(), 1 << (basis.len() - 1));
123 expanded.push(expanded_i)
124 }
125 assert_eq!(expanded.len(), evals.len());
126
127 Self { evals, expanded }
128 }
129}
130
131impl<F: BinaryField> DomainContext for GenericPreExpanded<F> {
132 type Field = F;
133
134 fn log_domain_size(&self) -> usize {
135 self.evals.len()
136 }
137
138 fn subspace(&self, i: usize) -> BinarySubspace<F> {
139 if i == 0 {
140 return BinarySubspace::with_dim(0).unwrap();
141 }
142 BinarySubspace::new_unchecked(self.evals[self.log_domain_size() - i].clone())
143 }
144
145 fn twiddle(&self, layer: usize, block: usize) -> F {
146 self.expanded[layer][block]
147 }
148}
149
150pub trait TraceOneElement {
152 fn trace_one_element() -> Self;
154}
155
156impl TraceOneElement for binius_field::BinaryField128bGhash {
157 fn trace_one_element() -> Self {
158 Self::new(1 << 121)
159 }
160}
161
162fn gao_mateer_basis<F: BinaryField + TraceOneElement>(num_basis_elements: usize) -> Vec<F> {
170 assert!(F::N_BITS.is_power_of_two());
171
172 let mut beta = F::trace_one_element();
175
176 for _i in 0..(F::N_BITS - num_basis_elements) {
178 beta = beta.square() + beta;
179 }
180
181 let mut basis = vec![F::ZERO; num_basis_elements];
183 basis[num_basis_elements - 1] = beta;
184 for i in (1..num_basis_elements).rev() {
185 basis[i - 1] = basis[i].square() + basis[i];
186 }
187
188 assert_eq!(basis[0], F::ONE);
191
192 basis
193}
194
195#[derive(Debug)]
223pub struct GaoMateerOnTheFly<F> {
224 basis: Vec<F>,
226}
227
228impl<F: BinaryField + TraceOneElement> GaoMateerOnTheFly<F> {
229 pub fn generate(log_domain_size: usize) -> Self {
239 let basis: Vec<F> = gao_mateer_basis(log_domain_size);
240
241 Self { basis }
242 }
243}
244
245impl<F: BinaryField> DomainContext for GaoMateerOnTheFly<F> {
246 type Field = F;
247
248 fn log_domain_size(&self) -> usize {
249 self.basis.len()
250 }
251
252 fn subspace(&self, i: usize) -> BinarySubspace<F> {
253 BinarySubspace::new_unchecked(self.basis[..i].to_vec())
254 }
255
256 fn twiddle(&self, _layer: usize, mut block: usize) -> F {
257 let mut twiddle = F::ZERO;
258
259 let mut i = 1;
260 while block != 0 {
261 twiddle += self.basis[i] * binius_field::BinaryField1b::from((block & 1) == 1);
262 i += 1;
263 block >>= 1;
264 }
265
266 twiddle
267 }
268}
269
270#[derive(Debug)]
275pub struct GaoMateerPreExpanded<F> {
276 basis: Vec<F>,
278 expanded: Vec<F>,
283}
284
285impl<F: BinaryField + TraceOneElement> GaoMateerPreExpanded<F> {
286 pub fn generate(log_domain_size: usize) -> Self {
296 let basis: Vec<F> = gao_mateer_basis(log_domain_size);
297
298 let mut expanded = Vec::with_capacity(1 << log_domain_size);
299 expanded.push(F::ZERO);
300 for i in 1..log_domain_size {
301 for j in 0..expanded.len() {
302 expanded.push(expanded[j] + basis[i]);
303 }
304 }
305 assert_eq!(expanded.len(), 1usize << (log_domain_size - 1));
306
307 Self { basis, expanded }
308 }
309}
310
311impl<F: BinaryField> DomainContext for GaoMateerPreExpanded<F> {
312 type Field = F;
313
314 fn log_domain_size(&self) -> usize {
315 self.basis.len()
316 }
317
318 fn subspace(&self, i: usize) -> BinarySubspace<F> {
319 BinarySubspace::new_unchecked(self.basis[..i].to_vec())
320 }
321
322 fn twiddle(&self, _layer: usize, block: usize) -> F {
323 self.expanded[block]
324 }
325}
326
327#[cfg(test)]
328mod tests {
329 use super::*;
330
331 fn test_equivalence<F: BinaryField>(
332 dc_1: &impl DomainContext<Field = F>,
333 dc_2: &impl DomainContext<Field = F>,
334 log_domain_size: usize,
335 ) {
336 assert_eq!(dc_1.log_domain_size(), log_domain_size);
337 assert_eq!(dc_2.log_domain_size(), log_domain_size);
338
339 for i in 0..log_domain_size {
340 assert_eq!(dc_1.subspace(i), dc_2.subspace(i));
341
342 for block in 0..1 << i {
343 assert_eq!(dc_1.twiddle(i, block), dc_2.twiddle(i, block));
344 }
345 }
346 assert_eq!(dc_1.subspace(log_domain_size), dc_2.subspace(log_domain_size))
347 }
348
349 #[test]
350 fn test_generic() {
351 const LOG_SIZE: usize = 5;
352 type F = binius_field::BinaryField128bGhash;
353
354 let subspace = BinarySubspace::with_dim(LOG_SIZE).unwrap();
355
356 let dc_otf = GenericOnTheFly::<F>::generate_from_subspace(&subspace);
357 let dc_pre = GenericPreExpanded::<F>::generate_from_subspace(&subspace);
358
359 test_equivalence(&dc_otf, &dc_pre, LOG_SIZE);
360 }
361
362 #[test]
363 fn test_gao_mateer() {
364 const LOG_SIZE: usize = 5;
365 type F = binius_field::BinaryField128bGhash;
366
367 let dc_gm_otf = GaoMateerOnTheFly::<F>::generate(LOG_SIZE);
368 let dc_gm_pre = GaoMateerPreExpanded::<F>::generate(LOG_SIZE);
369 let dc_generic_otf =
370 GenericOnTheFly::<F>::generate_from_subspace(&dc_gm_otf.subspace(LOG_SIZE));
371
372 test_equivalence(&dc_gm_otf, &dc_gm_pre, LOG_SIZE);
373 test_equivalence(&dc_gm_otf, &dc_generic_otf, LOG_SIZE);
374 }
375}