p3_interpolation/
lib.rs
1#![no_std]
4
5extern crate alloc;
6
7use alloc::vec::Vec;
8
9use p3_field::{
10 batch_multiplicative_inverse, scale_vec, two_adic_coset_zerofier, ExtensionField, TwoAdicField,
11};
12use p3_matrix::Matrix;
13use p3_maybe_rayon::prelude::*;
14use p3_util::log2_strict_usize;
15
16pub fn interpolate_subgroup<F, EF, Mat>(subgroup_evals: &Mat, point: EF) -> Vec<EF>
21where
22 F: TwoAdicField,
23 EF: ExtensionField<F> + TwoAdicField,
24 Mat: Matrix<F>,
25{
26 interpolate_coset(subgroup_evals, F::ONE, point, None)
27}
28
29pub fn interpolate_coset<F, EF, Mat>(
35 coset_evals: &Mat,
36 shift: F,
37 point: EF,
38 diff_invs: Option<&[EF]>,
39) -> Vec<EF>
40where
41 F: TwoAdicField,
42 EF: ExtensionField<F> + TwoAdicField,
43 Mat: Matrix<F>,
44{
45 let height = coset_evals.height();
48 let log_height = log2_strict_usize(height);
49
50 let g = F::two_adic_generator(log_height).powers().take(height);
51 let col_scale: Vec<_> = if let Some(diff_invs) = diff_invs {
52 g.zip(diff_invs)
53 .map(|(sg, &diff_inv)| diff_inv * sg)
54 .collect()
55 } else {
56 let subgroup = g.collect::<Vec<_>>();
57 let diffs: Vec<EF> = subgroup
58 .par_iter()
59 .map(|&subgroup_i| point - subgroup_i * shift)
60 .collect();
61 let diff_invs = batch_multiplicative_inverse(&diffs);
62 subgroup
63 .par_iter()
64 .zip(diff_invs)
65 .map(|(&sg, diff_inv)| diff_inv * sg)
66 .collect()
67 };
68 let sum = coset_evals.columnwise_dot_product(&col_scale);
69
70 let zerofier = two_adic_coset_zerofier::<EF>(log_height, EF::from_base(shift), point);
71 let denominator = F::from_canonical_usize(height) * shift.exp_u64(height as u64 - 1);
72 scale_vec(zerofier * denominator.inverse(), sum)
73}
74
75#[cfg(test)]
76mod tests {
77 use alloc::vec;
78 use alloc::vec::Vec;
79
80 use p3_baby_bear::BabyBear;
81 use p3_field::{batch_multiplicative_inverse, Field, FieldAlgebra};
82 use p3_matrix::dense::RowMajorMatrix;
83 use p3_util::log2_strict_usize;
84
85 use crate::{interpolate_coset, interpolate_subgroup};
86
87 #[test]
88 fn test_interpolate_subgroup() {
89 type F = BabyBear;
91 let evals = [
92 6, 886605102, 1443543107, 708307799, 2, 556938009, 569722818, 1874680944,
93 ]
94 .map(F::from_canonical_u32);
95 let evals_mat = RowMajorMatrix::new(evals.to_vec(), 1);
96 let point = F::from_canonical_u32(100);
97 let result = interpolate_subgroup(&evals_mat, point);
98 assert_eq!(result, vec![F::from_canonical_u32(10203)]);
99 }
100
101 #[test]
102 fn test_interpolate_coset() {
103 type F = BabyBear;
105 let shift = F::GENERATOR;
106 let evals = [
107 1026, 129027310, 457985035, 994890337, 902, 1988942953, 1555278970, 913671254,
108 ]
109 .map(F::from_canonical_u32);
110 let evals_mat = RowMajorMatrix::new(evals.to_vec(), 1);
111 let point = F::from_canonical_u32(100);
112 let result = interpolate_coset(&evals_mat, shift, point, None);
113 assert_eq!(result, vec![F::from_canonical_u32(10203)]);
114
115 use p3_field::TwoAdicField;
116 let n = evals.len();
117 let k = log2_strict_usize(n);
118
119 let denom: Vec<_> = F::two_adic_generator(k)
120 .shifted_powers(shift)
121 .take(n)
122 .map(|w| point - w)
123 .collect();
124
125 let denom = batch_multiplicative_inverse(&denom);
126 let result = interpolate_coset(&evals_mat, shift, point, Some(&denom));
127 assert_eq!(result, vec![F::from_canonical_u32(10203)]);
128 }
129}