Skip to content
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

Merged
merged 28 commits into from
Aug 21, 2024

Conversation

Al-Kindi-0
Copy link
Contributor

Adds a sumcheck crate containing the necessary tools for the LogUp-GKR protocol.

Copy link
Collaborator

@irakliyk irakliyk left a 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).

sumcheck/Cargo.toml Show resolved Hide resolved
sumcheck/src/utils/univariate.rs Outdated Show resolved Hide resolved
sumcheck/src/prover/mod.rs Show resolved Hide resolved
sumcheck/src/utils/mod.rs Outdated Show resolved Hide resolved
sumcheck/src/prover/mod.rs Outdated Show resolved Hide resolved
sumcheck/src/verifier/mod.rs Show resolved Hide resolved
air/src/air/mod.rs Outdated Show resolved Hide resolved
Copy link
Collaborator

@irakliyk irakliyk left a 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.

Cargo.toml Outdated Show resolved Hide resolved
air/src/air/logup_gkr.rs Show resolved Hide resolved
air/src/air/logup_gkr.rs Outdated Show resolved Hide resolved
air/src/air/logup_gkr.rs Outdated Show resolved Hide resolved
air/src/air/logup_gkr.rs Outdated Show resolved Hide resolved
sumcheck/src/prover/high_degree.rs Show resolved Hide resolved
sumcheck/src/prover/high_degree.rs Outdated Show resolved Hide resolved
sumcheck/src/prover/high_degree.rs Outdated Show resolved Hide resolved
sumcheck/src/verifier/mod.rs Outdated Show resolved Hide resolved
sumcheck/src/verifier/mod.rs Outdated Show resolved Hide resolved
@Al-Kindi-0
Copy link
Contributor Author

I have added benchmarks for all the critical components and I am seeing between 3x and 4x improvements for the multi-linear related methods on an 8 core laptop.

For the sum-check, which is the main target, I am seeing the following
sum_check_para_plain

and

sum_check_para_high_degree

Could you confirm on yours?

air/src/air/logup_gkr.rs Outdated Show resolved Hide resolved
sumcheck/src/multilinear.rs Show resolved Hide resolved
sumcheck/README.md Outdated Show resolved Hide resolved
sumcheck/benches/bind_variable.rs Show resolved Hide resolved
sumcheck/benches/eq_function.rs Show resolved Hide resolved
sumcheck/benches/sum_check_high_degree.rs Outdated Show resolved Hide resolved
sumcheck/benches/sum_check_plain.rs Outdated Show resolved Hide resolved
sumcheck/src/prover/high_degree.rs Show resolved Hide resolved
Copy link
Collaborator

@irakliyk irakliyk left a 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 {
Copy link
Collaborator

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

@irakliyk
Copy link
Collaborator

Oh - could you fix the couple of lints to make the CI green? (I think one of them can be disabled).

@irakliyk
Copy link
Collaborator

I have added benchmarks for all the critical components and I am seeing between 3x and 4x improvements for the multi-linear related methods on an 8 core laptop.

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).

Screenshot 2024-08-19 at 5 18 13 PM

@Al-Kindi-0
Copy link
Contributor Author

Oh - could you fix the couple of lints to make the CI green? (I think one of them can be disabled).

Fixed!

Copy link
Contributor

@plafer plafer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(partial review)

Comment on lines 52 to 56
p0: &mut MultiLinearPoly<E>,
p1: &mut MultiLinearPoly<E>,
q0: &mut MultiLinearPoly<E>,
q1: &mut MultiLinearPoly<E>,
eq: &mut MultiLinearPoly<E>,
Copy link
Contributor

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).

claim: E,
r_sum_check: E,
log_up_randomness: Vec<E>,
mls: &mut [MultiLinearPoly<E>],
Copy link
Contributor

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>>

fn evaluate_query<F, E>(
&self,
query: &[F],
rand_values: &[E],
Copy link
Contributor

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 {
Copy link
Contributor

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

Comment on lines 97 to 106
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))
}
Copy link
Contributor

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).

Copy link
Contributor Author

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.

Copy link
Contributor

@plafer plafer left a 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

) -> Result<SumCheckProof<E>, SumCheckProverError> {
let mut round_proofs = vec![];

let mut claim = claim;
Copy link
Contributor

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

transcript.draw().map_err(|_| SumCheckProverError::FailedToGenerateChallenge)?;

// compute the new reduced round claim
let new_claim = round_poly_coefs.evaluate_using_claim(&claim, &round_challenge);
Copy link
Contributor

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)

let len = p0.num_evaluations() / 2;

#[cfg(not(feature = "concurrent"))]
let (eval_point_1, eval_point_2, eval_point_3) =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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


#[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| {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
(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],
Copy link
Contributor

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

Comment on lines 85 to 89
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;
Copy link
Contributor

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

|(a0, b0, c0), (a1, b1, c1)| (a0 + a1, b0 + b1, c0 + c1),
);

let evals = smallvec![eval_point_1, eval_point_2, eval_point_3,];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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,];

);

let evals = smallvec![eval_point_1, eval_point_2, eval_point_3,];
let poly = CompressedUnivariatePolyEvals(evals);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let poly = CompressedUnivariatePolyEvals(evals);
let compressed_round_poly = CompressedUnivariatePolyEvals(evals);


let evals = smallvec![eval_point_1, eval_point_2, eval_point_3,];
let poly = CompressedUnivariatePolyEvals(evals);
let round_poly_coefs = poly.to_poly(claim);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let round_poly_coefs = poly.to_poly(claim);
let round_poly = poly.to_poly(claim);

Copy link
Contributor

@plafer plafer left a 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!

@Al-Kindi-0
Copy link
Contributor Author

I think I have addressed all of the comments above.

@irakliyk irakliyk changed the base branch from next to logup-gkr August 21, 2024 06:15
@irakliyk irakliyk merged commit d507eb9 into facebook:logup-gkr Aug 21, 2024
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants