-
Notifications
You must be signed in to change notification settings - Fork 182
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sumcheck IOP for LogUp-GKR #295
Sumcheck IOP for LogUp-GKR #295
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! This looks great! This not a full review by any means, but I left a few preliminary comments inline (mostly nits).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! Thank you! I left some more comments inline. Most of the comments are minor and the rest are things we can do in the future (i.e., adding benchmarks).
Also, let's add a simple README.md
for the wintersum check
crate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All looks good! Thank you!
/// | ||
/// This is the result of batching the `p_k` and `q_k` of section 3.2 in | ||
/// https://eprint.iacr.org/2023/1284.pdf. | ||
fn comb_func<E: FieldElement>(p0: E, p1: E, q0: E, q1: E, eq: E, r_batch: E) -> E { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given how frequently this function is used, I'd probably add #[inline(always)]
to it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added
Oh - could you fix the couple of lints to make the CI green? (I think one of them can be disabled). |
I'm seeing roughly similar numbers - though absolute numbers are faster while relative improvements between serial and concurrent are smaller (but seems to get better with size). |
Fixed! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(partial review)
sumcheck/src/prover/plain.rs
Outdated
p0: &mut MultiLinearPoly<E>, | ||
p1: &mut MultiLinearPoly<E>, | ||
q0: &mut MultiLinearPoly<E>, | ||
q1: &mut MultiLinearPoly<E>, | ||
eq: &mut MultiLinearPoly<E>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would change these variables to be
p_next: mut MultiLinearPoly<E>,
q_next: mut MultiLinearPoly<E>,
eq: mut MultiLinearPoly<E>,
and run the ML::bind_least_significant_variable()
inside this function. This is closer to the math in the docs, where we have 2 polynomials p
and q
(not 4).
Note also that we now take ownership of the multilinear polys, since AFAIK we never need to mutated result (in the GKR PR).
sumcheck/src/prover/high_degree.rs
Outdated
claim: E, | ||
r_sum_check: E, | ||
log_up_randomness: Vec<E>, | ||
mls: &mut [MultiLinearPoly<E>], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly, this can be a mut Vec<MultiLinearPoly<E>>
air/src/air/logup_gkr.rs
Outdated
fn evaluate_query<F, E>( | ||
&self, | ||
query: &[F], | ||
rand_values: &[E], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: would rename this to logup_randomness
|
||
use super::EvaluationFrame; | ||
|
||
pub trait LogUpGkrEvaluator: Clone + Sync { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add a docstring here? This trait does a lot of things and it's a little bit hard to get a handle on exactly what
sumcheck/src/multilinear.rs
Outdated
pub fn project_least_significant_variable(&self) -> (Self, Self) { | ||
let mut p0 = Vec::with_capacity(self.num_evaluations() / 2); | ||
let mut p1 = Vec::with_capacity(self.num_evaluations() / 2); | ||
for chunk in self.evaluations.chunks_exact(2) { | ||
p0.push(chunk[0]); | ||
p1.push(chunk[1]); | ||
} | ||
|
||
(MultiLinearPoly::from_evaluations(p0), MultiLinearPoly::from_evaluations(p1)) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could try rewriting this function as:
pub fn project_least_significant_variable(mut self) -> (Self, Self) {
let odds: Vec<E> = self
.evaluations
.iter()
.enumerate()
.filter_map(|(idx, x)| if idx % 2 == 1 { Some(*x) } else { None })
.collect();
// Builds the evens multilinear from the current `self.evaluations` buffer, which saves an
// allocation.
let evens = {
let evens_size = self.num_evaluations() / 2;
for write_idx in 0..evens_size {
let read_idx = write_idx * 2;
self.evaluations[write_idx] = self.evaluations[read_idx];
}
self.evaluations.truncate(evens_size);
self.evaluations
};
(Self::from_evaluations(evens), Self::from_evaluations(odds))
}
This version saves an allocation compared to the current version. I'd be curious how this affects the sum-check benchmark (after we moved the project_least_significant_variable()
call into the sum-check, as mentioned in another comment).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, thank you for the suggestion!
I haven't noticed any noticeable difference on machine (x86) but maybe it is different on other archs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
review of sumcheck_prove_plain
sumcheck/src/prover/plain.rs
Outdated
) -> Result<SumCheckProof<E>, SumCheckProverError> { | ||
let mut round_proofs = vec![]; | ||
|
||
let mut claim = claim; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can make the claim
function argument mut
and remove this statement
sumcheck/src/prover/plain.rs
Outdated
transcript.draw().map_err(|_| SumCheckProverError::FailedToGenerateChallenge)?; | ||
|
||
// compute the new reduced round claim | ||
let new_claim = round_poly_coefs.evaluate_using_claim(&claim, &round_challenge); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this statement can be moved after the calls to bind_least_significant_variable()
(and assigned to claim
directly)
sumcheck/src/prover/plain.rs
Outdated
let len = p0.num_evaluations() / 2; | ||
|
||
#[cfg(not(feature = "concurrent"))] | ||
let (eval_point_1, eval_point_2, eval_point_3) = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let (eval_point_1, eval_point_2, eval_point_3) = | |
let (round_poly_at_1, round_poly_at_2, round_poly_at_3) = |
I would make it explicit that the evaluations are ultimately for the round poly. It makes it more clear at this point in the function that we are computing the evaluations of the round polynomial.
Also, I would be helpful to make it explicit in the docstring that the degree of this sumcheck's g
is 3
sumcheck/src/prover/plain.rs
Outdated
|
||
#[cfg(not(feature = "concurrent"))] | ||
let (eval_point_1, eval_point_2, eval_point_3) = | ||
(0..len).fold((E::ZERO, E::ZERO, E::ZERO), |(a, b, c), i| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(0..len).fold((E::ZERO, E::ZERO, E::ZERO), |(a, b, c), i| { | |
(0..len).fold((E::ZERO, E::ZERO, E::ZERO), |(acc_point_1, acc_point_2, acc_point_3), i| { |
I would use more descriptive names for a
, b
and c
(and similarly for the concurrent version)
p1[2 * i + 1], | ||
q0[2 * i + 1], | ||
q1[2 * i + 1], | ||
eq[2 * i + 1], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reading this index-based evaluation is a little bit hard to read. I would be curious to see if explicitly representing the bits that we're querying improves readability, and if it affects performance.
Obviously not in this PR though
sumcheck/src/prover/plain.rs
Outdated
let mut p0_evx = p0[2 * i + 1] + p0_delta; | ||
let mut p1_evx = p1[2 * i + 1] + p1_delta; | ||
let mut q0_evx = q0[2 * i + 1] + q0_delta; | ||
let mut q1_evx = q1[2 * i + 1] + q1_delta; | ||
let mut eq_evx = eq[2 * i + 1] + eq_delta; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would rename those to e.g. p0_eval_at_x
or just p0_at_x
. I never remember what evx
stands for
sumcheck/src/prover/plain.rs
Outdated
|(a0, b0, c0), (a1, b1, c1)| (a0 + a1, b0 + b1, c0 + c1), | ||
); | ||
|
||
let evals = smallvec![eval_point_1, eval_point_2, eval_point_3,]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let evals = smallvec![eval_point_1, eval_point_2, eval_point_3,]; | |
let compressed_round_poly_evals = smallvec![eval_point_1, eval_point_2, eval_point_3,]; |
sumcheck/src/prover/plain.rs
Outdated
); | ||
|
||
let evals = smallvec![eval_point_1, eval_point_2, eval_point_3,]; | ||
let poly = CompressedUnivariatePolyEvals(evals); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let poly = CompressedUnivariatePolyEvals(evals); | |
let compressed_round_poly = CompressedUnivariatePolyEvals(evals); |
sumcheck/src/prover/plain.rs
Outdated
|
||
let evals = smallvec![eval_point_1, eval_point_2, eval_point_3,]; | ||
let poly = CompressedUnivariatePolyEvals(evals); | ||
let round_poly_coefs = poly.to_poly(claim); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let round_poly_coefs = poly.to_poly(claim); | |
let round_poly = poly.to_poly(claim); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All LGTM modulo some nits that we can take care of in subsequent PRs!
I think I have addressed all of the comments above. |
Adds a
sumcheck
crate containing the necessary tools for the LogUp-GKR protocol.