optimize uni poly interpolation (#19)

This commit is contained in:
zhenfei
2022-05-16 20:40:46 -04:00
committed by GitHub
parent 2812d2bce2
commit 7651e39f7a
3 changed files with 76 additions and 24 deletions

View File

@@ -26,8 +26,8 @@ path = "benches/bench.rs"
harness = false harness = false
[features] [features]
# default = [ "parallel", "print-trace" ] default = [ "parallel", "print-trace" ]
default = [ "parallel" ] # default = [ "parallel" ]
parallel = [ parallel = [
"rayon", "rayon",
"ark-std/parallel", "ark-std/parallel",

View File

@@ -116,7 +116,7 @@ impl<F: PrimeField> SumCheckVerifier<F> for IOPVerifierState<F> {
self.max_degree + 1 self.max_degree + 1
))); )));
} }
Ok(interpolate_uni_poly::<F>(&evaluations, challenge)) interpolate_uni_poly::<F>(&evaluations, challenge)
}) })
.collect::<Result<Vec<_>, PolyIOPErrors>>()?; .collect::<Result<Vec<_>, PolyIOPErrors>>()?;
@@ -134,7 +134,7 @@ impl<F: PrimeField> SumCheckVerifier<F> for IOPVerifierState<F> {
self.max_degree + 1 self.max_degree + 1
))); )));
} }
Ok(interpolate_uni_poly::<F>(&evaluations, challenge)) interpolate_uni_poly::<F>(&evaluations, challenge)
}) })
.collect::<Result<Vec<_>, PolyIOPErrors>>()?; .collect::<Result<Vec<_>, PolyIOPErrors>>()?;
// insert the asserted_sum to the first position of the expected vector // insert the asserted_sum to the first position of the expected vector
@@ -162,23 +162,75 @@ impl<F: PrimeField> SumCheckVerifier<F> for IOPVerifierState<F> {
} }
/// Interpolate a uni-variate degree-`p_i.len()-1` polynomial and evaluate this /// Interpolate a uni-variate degree-`p_i.len()-1` polynomial and evaluate this
/// polynomial at `eval_at`. /// polynomial at `eval_at`:
pub(crate) fn interpolate_uni_poly<F: PrimeField>(p_i: &[F], eval_at: F) -> F { /// \sum_{i=0}^len p_i * (\prod_{j!=i} (eval_at - j)/(i-j) )
let start = start_timer!(|| "sum check interpolate uni poly"); /// This implementation is linear in number of inputs in terms of field
let mut result = F::zero(); /// operations. It also has a quadratic term in primitive operations which is
let mut i = F::zero(); /// negligible compared to field operations.
for term in p_i.iter() { pub(crate) fn interpolate_uni_poly<F: PrimeField>(
let mut term = *term; p_i: &[F],
let mut j = F::zero(); eval_at: F,
for _ in 0..p_i.len() { ) -> Result<F, PolyIOPErrors> {
if j != i { let start = start_timer!(|| "sum check interpolate uni poly opt");
term = term * (eval_at - j) / (i - j)
} let mut res = F::zero();
j += F::one();
} // prod = \prod_{j!=i} (eval_at - j)
i += F::one(); let mut evals = vec![];
result += term; let len = p_i.len();
let mut prod = eval_at;
evals.push(eval_at);
for e in 1..len {
let tmp = eval_at - F::from(e as u64);
evals.push(tmp);
prod *= tmp;
} }
for i in 0..len {
let divisor = get_divisor(i, len)?;
let divisor_f = {
if divisor < 0 {
-F::from((-divisor) as u128)
} else {
F::from(divisor as u128)
}
};
res += p_i[i] * prod / (divisor_f * evals[i]);
}
end_timer!(start); end_timer!(start);
result Ok(res)
}
/// Compute \prod_{j!=i)^len (i-j). This function takes O(n^2) number of
/// primitive operations which is negligible compared to field operations.
// We know
// - factorial(20) ~ 2^61
// - factorial(33) ~ 2^123
// so we will be able to store the result for len<=20 with i64;
// for len<=33 with i128; and we do not currently support len>33.
#[inline]
fn get_divisor(i: usize, len: usize) -> Result<i128, PolyIOPErrors> {
if len <= 20 {
let mut res = 1i64;
for j in 0..len {
if j != i {
res *= i as i64 - j as i64;
}
}
Ok(res as i128)
} else if len <= 33 {
let mut res = 1i128;
for j in 0..len {
if j != i {
res *= i as i128 - j as i128;
}
}
Ok(res)
} else {
Err(PolyIOPErrors::InvalidParameters(
"Do not support number variable > 33".to_string(),
))
}
} }

View File

@@ -205,7 +205,7 @@ impl<F: PrimeField> VirtualPolynomial<F> {
num_products: usize, num_products: usize,
rng: &mut R, rng: &mut R,
) -> Result<(Self, F), PolyIOPErrors> { ) -> Result<(Self, F), PolyIOPErrors> {
let start = start_timer!("sample random virtual polynomial"); let start = start_timer!(|| "sample random virtual polynomial");
let mut sum = F::zero(); let mut sum = F::zero();
let mut poly = VirtualPolynomial::new(nv); let mut poly = VirtualPolynomial::new(nv);
@@ -252,7 +252,7 @@ fn random_mle_list<F: PrimeField, R: RngCore>(
degree: usize, degree: usize,
rng: &mut R, rng: &mut R,
) -> (Vec<Rc<DenseMultilinearExtension<F>>>, F) { ) -> (Vec<Rc<DenseMultilinearExtension<F>>>, F) {
let start = start_timer!("sample random mle list"); let start = start_timer!(|| "sample random mle list");
let mut multiplicands = Vec::with_capacity(degree); let mut multiplicands = Vec::with_capacity(degree);
for _ in 0..degree { for _ in 0..degree {
multiplicands.push(Vec::with_capacity(1 << nv)) multiplicands.push(Vec::with_capacity(1 << nv))
@@ -285,7 +285,7 @@ pub fn random_zero_mle_list<F: PrimeField, R: RngCore>(
degree: usize, degree: usize,
rng: &mut R, rng: &mut R,
) -> Vec<Rc<DenseMultilinearExtension<F>>> { ) -> Vec<Rc<DenseMultilinearExtension<F>>> {
let start = start_timer!("sample random zero mle list"); let start = start_timer!(|| "sample random zero mle list");
let mut multiplicands = Vec::with_capacity(degree); let mut multiplicands = Vec::with_capacity(degree);
for _ in 0..degree { for _ in 0..degree {