From d4d239c1030e00c418cfb269a1a1022314bbb70d Mon Sep 17 00:00:00 2001 From: Jay White Date: Wed, 4 Sep 2024 12:47:55 -0400 Subject: [PATCH] feat: add function converting evaluations to coefficients (#125) # Rationale for this change In order for the sumcheck verifier to be able to be verify proofs more efficiently (and for the sake of the solidity port, with less code), the proof should consist of polynomial coefficients rather than evaluations. This PR adds the code to make the conversion. A later PR will do the switch. # What changes are included in this PR? * A `interpolate_evaluations_to_reverse_coefficients` method is added. * Tests for this method are added. # Are these changes tested? Yes --- .../src/base/polynomial/interpolate.rs | 51 ++++++++++++- .../src/base/polynomial/interpolate_test.rs | 73 ++++++++++++++++++- .../proof-of-sql/src/base/polynomial/mod.rs | 3 +- 3 files changed, 121 insertions(+), 6 deletions(-) diff --git a/crates/proof-of-sql/src/base/polynomial/interpolate.rs b/crates/proof-of-sql/src/base/polynomial/interpolate.rs index 0c356a874..bfca71758 100644 --- a/crates/proof-of-sql/src/base/polynomial/interpolate.rs +++ b/crates/proof-of-sql/src/base/polynomial/interpolate.rs @@ -1,10 +1,10 @@ -use core::cmp::PartialEq; /** * Adapted from arkworks * * See third_party/license/arkworks.LICENSE */ -use core::ops::{AddAssign, Mul, MulAssign, SubAssign}; +use core::ops::{Add, AddAssign, Mul, MulAssign, SubAssign}; +use core::{cmp::PartialEq, iter::Product}; use num_traits::{Inv, One, Zero}; /// Interpolate a uni-variate degree-`polynomial.len()-1` polynomial and evaluate this @@ -67,3 +67,50 @@ where } sum * product } + +/// Let d be the evals.len() - 1 and let f be the polynomial such that f(i) = evals[i]. +/// The output of this function is the vector of coefficients of f, leading coefficient first. +/// That is, `f(x) = evals[j]*x^(d-j)``. +#[allow(dead_code)] +pub fn interpolate_evaluations_to_reverse_coefficients(evals: &[S]) -> Vec +where + S: Zero + + Copy + + From + + Mul + + Add + + Inv> + + Product, +{ + let n = evals.len().max(1) - 1; + evals + .iter() + .enumerate() + .map(|(idx, &eval_i)| { + let i = idx as i32; + let mut scaled_lagrange_basis = vec![S::zero(); n + 1]; + // First compute the constant factor of this lagrange basis polynomial: + scaled_lagrange_basis[0] = (i - n as i32..0) + .chain(1..=i) + .map(S::from) + .product::() + .inv() + .unwrap() + * eval_i; + // Then multiply by the appropriate linear terms: + // for j in 0..=n if j != i { + for neg_j in (-(n as i32)..-i).chain(1 - i..=0).map(S::from) { + for k in (0..n).rev() { + scaled_lagrange_basis[k + 1] = + scaled_lagrange_basis[k + 1] + neg_j * scaled_lagrange_basis[k]; + } + } + scaled_lagrange_basis + }) + // Finally, sum up all the resulting polynomials + .reduce(|mut acc, b| { + acc.iter_mut().zip(b).for_each(|(a, b)| *a = *a + b); + acc + }) + .unwrap_or(vec![]) +} diff --git a/crates/proof-of-sql/src/base/polynomial/interpolate_test.rs b/crates/proof-of-sql/src/base/polynomial/interpolate_test.rs index 910d49df9..99e3d2dc3 100644 --- a/crates/proof-of-sql/src/base/polynomial/interpolate_test.rs +++ b/crates/proof-of-sql/src/base/polynomial/interpolate_test.rs @@ -3,10 +3,10 @@ * * See third_party/license/arkworks.LICENSE */ -use crate::base::polynomial::interpolate::*; -use crate::base::scalar::Curve25519Scalar; +use super::interpolate::*; +use crate::base::scalar::{Curve25519Scalar, Curve25519Scalar as S}; use ark_std::UniformRand; -use num_traits::Zero; +use num_traits::{Inv, Zero}; #[test] fn test_interpolate_uni_poly_for_random_polynomials() { @@ -93,3 +93,70 @@ fn interpolate_uni_poly_gives_correct_value_for_known_evaluation() { ); } } + +#[test] +fn we_can_interpolate_evaluations_to_reverse_coefficients_with_empty_input() { + assert_eq!( + interpolate_evaluations_to_reverse_coefficients(&[] as &[S]), + vec![] + ); +} + +#[test] +fn we_can_interpolate_evaluations_to_reverse_coefficients_with_degree_0() { + assert_eq!( + interpolate_evaluations_to_reverse_coefficients(&[S::from(2)]), + vec![S::from(2)] + ); +} + +#[test] +fn we_can_interpolate_evaluations_to_reverse_coefficients_with_degree_1() { + assert_eq!( + interpolate_evaluations_to_reverse_coefficients(&[S::from(2), S::from(3)]), + vec![S::from(1), S::from(2)] + ); +} + +#[test] +fn we_can_interpolate_evaluations_to_reverse_coefficients_with_degree_2() { + assert_eq!( + interpolate_evaluations_to_reverse_coefficients(&[S::from(2), S::from(3), S::from(5)]), + vec![ + S::from(1) * S::from(2).inv().unwrap(), + S::from(1) * S::from(2).inv().unwrap(), + S::from(2) + ] + ); +} + +#[test] +fn we_can_interpolate_evaluations_to_reverse_coefficients_with_degree_3() { + assert_eq!( + interpolate_evaluations_to_reverse_coefficients(&[ + S::from(2), + S::from(3), + S::from(5), + S::from(7) + ]), + vec![ + S::from(-1) * S::from(6).inv().unwrap(), + S::from(1), + S::from(1) * S::from(6).inv().unwrap(), + S::from(2) + ] + ); +} + +#[test] +fn we_can_interpolate_evaluations_to_reverse_coefficients_with_degree_3_degenerate_evals() { + assert_eq!( + interpolate_evaluations_to_reverse_coefficients(&[ + S::from(1), + S::from(3), + S::from(5), + S::from(7) + ]), + vec![S::from(0), S::from(0), S::from(2), S::from(1)] + ); +} diff --git a/crates/proof-of-sql/src/base/polynomial/mod.rs b/crates/proof-of-sql/src/base/polynomial/mod.rs index ce7a848b2..3959b576d 100644 --- a/crates/proof-of-sql/src/base/polynomial/mod.rs +++ b/crates/proof-of-sql/src/base/polynomial/mod.rs @@ -6,7 +6,8 @@ mod composite_polynomial_test; mod interpolate; #[cfg(test)] mod interpolate_test; -pub use interpolate::interpolate_uni_poly; +#[allow(unused_imports)] +pub use interpolate::{interpolate_evaluations_to_reverse_coefficients, interpolate_uni_poly}; mod evaluation_vector; pub use evaluation_vector::compute_evaluation_vector;