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

experiment fanin > 2 in product argument #788

Open
2 of 3 tasks
hero78119 opened this issue Dec 26, 2024 · 0 comments
Open
2 of 3 tasks

experiment fanin > 2 in product argument #788

hero78119 opened this issue Dec 26, 2024 · 0 comments
Labels
enhancement New feature or request

Comments

@hero78119
Copy link
Collaborator

hero78119 commented Dec 26, 2024

Context

Currently in ceno, product argument configure with fanin=2, e.g. read set witness. with respective sumcheck arguments $p[\vec{\gamma}] = \sum_{\vec{b}} eq(\vec{\gamma}, \vec{b}) p_{1}[\vec{b}] * p_{2}[\vec{b}]$. The reason is because to simplify, in sumcheck prover_v2 we only support degree <= 3 implementation so far.

We can extend product argument and experiment > 2 fanins on product and benchmark. To eliminate the limitation of degree sumcheck prover, we can use rust macro to generate the boilerplate code of arbitrary degree implementation.

performance gain

Intuitively

  1. compiler can do more optimisation https://github.com/scroll-tech/ceno/blob/master/sumcheck/src/prover.rs#L469-L473 if goes from f1 * f2 to f1 * f2 * f3 * f4
  2. layer of tower sumcheck product argument can be half, for fanin = 2 -> fanin = 4, number of product argument layers can be divided by 2

Suggested step

@hero78119 hero78119 added the enhancement New feature or request label Dec 26, 2024
github-merge-queue bot pushed a commit that referenced this issue Jan 2, 2025
Previously to quick verify idea and avoid massive change, there are new
functionality with suffix `_v2`. After experiment with good result, long
time ago all logic already stick to v2 version and no longer use v1.
This PR clean up all leftover v1 version, do renaming and file
replacement without modify existing logic.

In summary
- `sumcheck/src/prover_v2.rs` ->  `sumcheck/src/prover.rs`
- `multilinear_extensions/src/virtual_poly_v2.rs` ->
`multilinear_extensions/src/virtual_poly.rs`
- clean up all `V2` suffix

This addressed previous out-dated PR
#162, and as a preparation for
#788,
#702
@zemse zemse mentioned this issue Jan 16, 2025
github-merge-queue bot pushed a commit that referenced this issue Jan 22, 2025
Issue https://github.com/scroll-tech/ceno/issues/788.

Currently, the step 2 in the sumcheck prover has [manual implementations
for degree 1, 2 &
3](https://github.com/scroll-tech/ceno/blob/7919da29ff5b20d868c8ac4f74dcbab3fcc5739a/sumcheck/src/prover.rs#L432-L534).
This requires manual implementation for degree 4.

- This PR adds a macro that generates boilerplate for sumcheck degree to
prevent boilerplate code
- Add support for degree 4 and 5
- Activate tests that were ignored

## Code generation

Using cargo expand

<details>
  <summary>code for degree 3</summary>

```rust
let _result: AdditiveArray<_, 4> = {
    let product_access = |_| &self.poly.flattened_ml_extensions[0];
    let f1 = product_access(0usize);
    let f2 = product_access(1usize);
    let f3 = product_access(2usize);
    let (f1, f2, f3) = match (
        &f1.evaluations(),
        &f2.evaluations(),
        &f3.evaluations(),
    ) {
        (FieldType::Base(_), FieldType::Ext(_), FieldType::Ext(_)) => {
            (f2, f3, f1)
        }
        (FieldType::Ext(_), FieldType::Base(_), FieldType::Ext(_)) => {
            (f1, f3, f2)
        }
        (FieldType::Base(_), FieldType::Base(_), FieldType::Ext(_)) => {
            (f3, f1, f2)
        }
        (FieldType::Base(_), FieldType::Ext(_), FieldType::Base(_)) => {
            (f2, f1, f3)
        }
        _ => (f1, f2, f3),
    };
    match (&f1.evaluations(), &f2.evaluations(), &f3.evaluations()) {
        (FieldType::Ext(v1), FieldType::Ext(v2), FieldType::Ext(v3)) => {
            let v1 = if let Some((start, offset)) = f1.evaluations_range() {
                &v1[start..][..offset]
            } else {
                &v1[..]
            };
            let v2 = if let Some((start, offset)) = f2.evaluations_range() {
                &v2[start..][..offset]
            } else {
                &v2[..]
            };
            let v3 = if let Some((start, offset)) = f3.evaluations_range() {
                &v3[start..][..offset]
            } else {
                &v3[..]
            };
            let res = (0..largest_even_below(v1.len()))
                .step_by(2)
                .rev()
                .map(|b| {
                    let c1_0 = v1[b + 1] - v1[b];
                    let c1_1 = c1_0 + c1_0;
                    let c2_0 = v2[b + 1] - v2[b];
                    let c2_1 = c2_0 + c2_0;
                    let c3_0 = v3[b + 1] - v3[b];
                    let c3_1 = c3_0 + c3_0;
                    AdditiveArray([
                        ((v1[b]) * (v2[b])) * (v3[b]),
                        ((v1[b + 1]) * (v2[b + 1])) * (v3[b + 1]),
                        ((c1_0 + v1[b + 1]) * (c2_0 + v2[b + 1]))
                            * (c3_0 + v3[b + 1]),
                        ((c1_1 + v1[b + 1]) * (c2_1 + v2[b + 1]))
                            * (c3_1 + v3[b + 1]),
                    ])
                })
                .sum::<AdditiveArray<_, 4usize>>();
            let res = if v1.len() == 1 {
                let b = 0;
                AdditiveArray::<
                    _,
                    4usize,
                >([((v1[b]) * (v2[b])) * (v3[b]); 4usize])
            } else {
                res
            };
            let num_vars_multiplicity = self.poly.aux_info.max_num_variables
                - (ceil_log2(v1.len()).max(1) + self.round - 1);
            if num_vars_multiplicity > 0 {
                AdditiveArray(
                    res
                        .0
                        .map(|e| e * E::BaseField::from(1 << num_vars_multiplicity)),
                )
            } else {
                res
            }
        }
        (FieldType::Ext(v1), FieldType::Ext(v2), FieldType::Base(v3)) => {
            let v1 = if let Some((start, offset)) = f1.evaluations_range() {
                &v1[start..][..offset]
            } else {
                &v1[..]
            };
            let v2 = if let Some((start, offset)) = f2.evaluations_range() {
                &v2[start..][..offset]
            } else {
                &v2[..]
            };
            let v3 = if let Some((start, offset)) = f3.evaluations_range() {
                &v3[start..][..offset]
            } else {
                &v3[..]
            };
            let res = (0..largest_even_below(v1.len()))
                .step_by(2)
                .rev()
                .map(|b| {
                    let c1_0 = v1[b + 1] - v1[b];
                    let c1_1 = c1_0 + c1_0;
                    let c2_0 = v2[b + 1] - v2[b];
                    let c2_1 = c2_0 + c2_0;
                    let c3_0 = v3[b + 1] - v3[b];
                    let c3_1 = c3_0 + c3_0;
                    AdditiveArray([
                        ((v1[b]) * (v2[b])) * (v3[b]),
                        ((v1[b + 1]) * (v2[b + 1])) * (v3[b + 1]),
                        ((c1_0 + v1[b + 1]) * (c2_0 + v2[b + 1]))
                            * (c3_0 + v3[b + 1]),
                        ((c1_1 + v1[b + 1]) * (c2_1 + v2[b + 1]))
                            * (c3_1 + v3[b + 1]),
                    ])
                })
                .sum::<AdditiveArray<_, 4usize>>();
            let res = if v1.len() == 1 {
                let b = 0;
                AdditiveArray::<
                    _,
                    4usize,
                >([((v1[b]) * (v2[b])) * (v3[b]); 4usize])
            } else {
                res
            };
            let num_vars_multiplicity = self.poly.aux_info.max_num_variables
                - (ceil_log2(v1.len()).max(1) + self.round - 1);
            if num_vars_multiplicity > 0 {
                AdditiveArray(
                    res
                        .0
                        .map(|e| e * E::BaseField::from(1 << num_vars_multiplicity)),
                )
            } else {
                res
            }
        }
        (FieldType::Ext(v1), FieldType::Base(v2), FieldType::Base(v3)) => {
            let v1 = if let Some((start, offset)) = f1.evaluations_range() {
                &v1[start..][..offset]
            } else {
                &v1[..]
            };
            let v2 = if let Some((start, offset)) = f2.evaluations_range() {
                &v2[start..][..offset]
            } else {
                &v2[..]
            };
            let v3 = if let Some((start, offset)) = f3.evaluations_range() {
                &v3[start..][..offset]
            } else {
                &v3[..]
            };
            let res = (0..largest_even_below(v1.len()))
                .step_by(2)
                .rev()
                .map(|b| {
                    let c1_0 = v1[b + 1] - v1[b];
                    let c1_1 = c1_0 + c1_0;
                    let c2_0 = v2[b + 1] - v2[b];
                    let c2_1 = c2_0 + c2_0;
                    let c3_0 = v3[b + 1] - v3[b];
                    let c3_1 = c3_0 + c3_0;
                    AdditiveArray([
                        ((v1[b]) * (v2[b])) * (v3[b]),
                        ((v1[b + 1]) * (v2[b + 1])) * (v3[b + 1]),
                        ((c1_0 + v1[b + 1]) * (c2_0 + v2[b + 1]))
                            * (c3_0 + v3[b + 1]),
                        ((c1_1 + v1[b + 1]) * (c2_1 + v2[b + 1]))
                            * (c3_1 + v3[b + 1]),
                    ])
                })
                .sum::<AdditiveArray<_, 4usize>>();
            let res = if v1.len() == 1 {
                let b = 0;
                AdditiveArray::<
                    _,
                    4usize,
                >([((v1[b]) * (v2[b])) * (v3[b]); 4usize])
            } else {
                res
            };
            let num_vars_multiplicity = self.poly.aux_info.max_num_variables
                - (ceil_log2(v1.len()).max(1) + self.round - 1);
            if num_vars_multiplicity > 0 {
                AdditiveArray(
                    res
                        .0
                        .map(|e| e * E::BaseField::from(1 << num_vars_multiplicity)),
                )
            } else {
                res
            }
        }
        (FieldType::Base(v1), FieldType::Base(v2), FieldType::Base(v3)) => {
            let v1 = if let Some((start, offset)) = f1.evaluations_range() {
                &v1[start..][..offset]
            } else {
                &v1[..]
            };
            let v2 = if let Some((start, offset)) = f2.evaluations_range() {
                &v2[start..][..offset]
            } else {
                &v2[..]
            };
            let v3 = if let Some((start, offset)) = f3.evaluations_range() {
                &v3[start..][..offset]
            } else {
                &v3[..]
            };
            let result = {
                let res = (0..largest_even_below(v1.len()))
                    .step_by(2)
                    .rev()
                    .map(|b| {
                        let c1_0 = v1[b + 1] - v1[b];
                        let c1_1 = c1_0 + c1_0;
                        let c2_0 = v2[b + 1] - v2[b];
                        let c2_1 = c2_0 + c2_0;
                        let c3_0 = v3[b + 1] - v3[b];
                        let c3_1 = c3_0 + c3_0;
                        AdditiveArray([
                            ((v1[b]) * (v2[b])) * (v3[b]),
                            ((v1[b + 1]) * (v2[b + 1])) * (v3[b + 1]),
                            ((c1_0 + v1[b + 1]) * (c2_0 + v2[b + 1]))
                                * (c3_0 + v3[b + 1]),
                            ((c1_1 + v1[b + 1]) * (c2_1 + v2[b + 1]))
                                * (c3_1 + v3[b + 1]),
                        ])
                    })
                    .sum::<AdditiveArray<_, 4usize>>();
                let res = if v1.len() == 1 {
                    let b = 0;
                    AdditiveArray::<
                        _,
                        4usize,
                    >([((v1[b]) * (v2[b])) * (v3[b]); 4usize])
                } else {
                    res
                };
                let num_vars_multiplicity = self.poly.aux_info.max_num_variables
                    - (ceil_log2(v1.len()).max(1) + self.round - 1);
                if num_vars_multiplicity > 0 {
                    AdditiveArray(
                        res
                            .0
                            .map(|e| e * E::BaseField::from(1 << num_vars_multiplicity)),
                    )
                } else {
                    res
                }
            };
            AdditiveArray(result.0.map(E::from))
        }
        _ => ::core::panicking::panic("internal error: entered unreachable code"),
    }
};
```
</details>


<details>
  <summary>code for degree 4</summary>

```rust
let _result: AdditiveArray<_, 5> = {
    let product_access = |_| &self.poly.flattened_ml_extensions[0];
    let f1 = product_access(0usize);
    let f2 = product_access(1usize);
    let f3 = product_access(2usize);
    let f4 = product_access(3usize);
    let (f1, f2, f3, f4) = match (
        &f1.evaluations(),
        &f2.evaluations(),
        &f3.evaluations(),
        &f4.evaluations(),
    ) {
        (
            FieldType::Base(_),
            FieldType::Ext(_),
            FieldType::Ext(_),
            FieldType::Ext(_),
        ) => (f2, f3, f4, f1),
        (
            FieldType::Ext(_),
            FieldType::Base(_),
            FieldType::Ext(_),
            FieldType::Ext(_),
        ) => (f1, f3, f4, f2),
        (
            FieldType::Base(_),
            FieldType::Base(_),
            FieldType::Ext(_),
            FieldType::Ext(_),
        ) => (f3, f4, f1, f2),
        (
            FieldType::Ext(_),
            FieldType::Ext(_),
            FieldType::Base(_),
            FieldType::Ext(_),
        ) => (f1, f2, f4, f3),
        (
            FieldType::Base(_),
            FieldType::Ext(_),
            FieldType::Base(_),
            FieldType::Ext(_),
        ) => (f2, f4, f1, f3),
        (
            FieldType::Ext(_),
            FieldType::Base(_),
            FieldType::Base(_),
            FieldType::Ext(_),
        ) => (f1, f4, f2, f3),
        (
            FieldType::Base(_),
            FieldType::Base(_),
            FieldType::Base(_),
            FieldType::Ext(_),
        ) => (f4, f1, f2, f3),
        (
            FieldType::Base(_),
            FieldType::Ext(_),
            FieldType::Ext(_),
            FieldType::Base(_),
        ) => (f2, f3, f1, f4),
        (
            FieldType::Ext(_),
            FieldType::Base(_),
            FieldType::Ext(_),
            FieldType::Base(_),
        ) => (f1, f3, f2, f4),
        (
            FieldType::Base(_),
            FieldType::Base(_),
            FieldType::Ext(_),
            FieldType::Base(_),
        ) => (f3, f1, f2, f4),
        (
            FieldType::Base(_),
            FieldType::Ext(_),
            FieldType::Base(_),
            FieldType::Base(_),
        ) => (f2, f1, f3, f4),
        _ => (f1, f2, f3, f4),
    };
    match (
        &f1.evaluations(),
        &f2.evaluations(),
        &f3.evaluations(),
        &f4.evaluations(),
    ) {
        (
            FieldType::Ext(v1),
            FieldType::Ext(v2),
            FieldType::Ext(v3),
            FieldType::Ext(v4),
        ) => {
            let v1 = if let Some((start, offset)) = f1.evaluations_range() {
                &v1[start..][..offset]
            } else {
                &v1[..]
            };
            let v2 = if let Some((start, offset)) = f2.evaluations_range() {
                &v2[start..][..offset]
            } else {
                &v2[..]
            };
            let v3 = if let Some((start, offset)) = f3.evaluations_range() {
                &v3[start..][..offset]
            } else {
                &v3[..]
            };
            let v4 = if let Some((start, offset)) = f4.evaluations_range() {
                &v4[start..][..offset]
            } else {
                &v4[..]
            };
            let res = (0..largest_even_below(v1.len()))
                .step_by(2)
                .rev()
                .map(|b| {
                    let c1_0 = v1[b + 1] - v1[b];
                    let c1_1 = c1_0 + c1_0;
                    let c2_0 = v2[b + 1] - v2[b];
                    let c2_1 = c2_0 + c2_0;
                    let c3_0 = v3[b + 1] - v3[b];
                    let c3_1 = c3_0 + c3_0;
                    let c4_0 = v4[b + 1] - v4[b];
                    let c4_1 = c4_0 + c4_0;
                    AdditiveArray([
                        (((v1[b]) * (v2[b])) * (v3[b])) * (v4[b]),
                        (((v1[b + 1]) * (v2[b + 1])) * (v3[b + 1])) * (v4[b + 1]),
                        (((c1_0 + v1[b + 1]) * (c2_0 + v2[b + 1]))
                            * (c3_0 + v3[b + 1])) * (c4_0 + v4[b + 1]),
                        (((c1_1 + v1[b + 1]) * (c2_1 + v2[b + 1]))
                            * (c3_1 + v3[b + 1])) * (c4_1 + v4[b + 1]),
                        (((c1_0 + c1_1 + v1[b + 1]) * (c2_0 + c2_1 + v2[b + 1]))
                            * (c3_0 + c3_1 + v3[b + 1])) * (c4_0 + c4_1 + v4[b + 1]),
                    ])
                })
                .sum::<AdditiveArray<_, 5usize>>();
            let res = if v1.len() == 1 {
                let b = 0;
                AdditiveArray::<
                    _,
                    5usize,
                >([(((v1[b]) * (v2[b])) * (v3[b])) * (v4[b]); 5usize])
            } else {
                res
            };
            let num_vars_multiplicity = self.poly.aux_info.max_num_variables
                - (ceil_log2(v1.len()).max(1) + self.round - 1);
            if num_vars_multiplicity > 0 {
                AdditiveArray(
                    res
                        .0
                        .map(|e| e * E::BaseField::from(1 << num_vars_multiplicity)),
                )
            } else {
                res
            }
        }
        (
            FieldType::Ext(v1),
            FieldType::Ext(v2),
            FieldType::Ext(v3),
            FieldType::Base(v4),
        ) => {
            let v1 = if let Some((start, offset)) = f1.evaluations_range() {
                &v1[start..][..offset]
            } else {
                &v1[..]
            };
            let v2 = if let Some((start, offset)) = f2.evaluations_range() {
                &v2[start..][..offset]
            } else {
                &v2[..]
            };
            let v3 = if let Some((start, offset)) = f3.evaluations_range() {
                &v3[start..][..offset]
            } else {
                &v3[..]
            };
            let v4 = if let Some((start, offset)) = f4.evaluations_range() {
                &v4[start..][..offset]
            } else {
                &v4[..]
            };
            let res = (0..largest_even_below(v1.len()))
                .step_by(2)
                .rev()
                .map(|b| {
                    let c1_0 = v1[b + 1] - v1[b];
                    let c1_1 = c1_0 + c1_0;
                    let c2_0 = v2[b + 1] - v2[b];
                    let c2_1 = c2_0 + c2_0;
                    let c3_0 = v3[b + 1] - v3[b];
                    let c3_1 = c3_0 + c3_0;
                    let c4_0 = v4[b + 1] - v4[b];
                    let c4_1 = c4_0 + c4_0;
                    AdditiveArray([
                        (((v1[b]) * (v2[b])) * (v3[b])) * (v4[b]),
                        (((v1[b + 1]) * (v2[b + 1])) * (v3[b + 1])) * (v4[b + 1]),
                        (((c1_0 + v1[b + 1]) * (c2_0 + v2[b + 1]))
                            * (c3_0 + v3[b + 1])) * (c4_0 + v4[b + 1]),
                        (((c1_1 + v1[b + 1]) * (c2_1 + v2[b + 1]))
                            * (c3_1 + v3[b + 1])) * (c4_1 + v4[b + 1]),
                        (((c1_0 + c1_1 + v1[b + 1]) * (c2_0 + c2_1 + v2[b + 1]))
                            * (c3_0 + c3_1 + v3[b + 1])) * (c4_0 + c4_1 + v4[b + 1]),
                    ])
                })
                .sum::<AdditiveArray<_, 5usize>>();
            let res = if v1.len() == 1 {
                let b = 0;
                AdditiveArray::<
                    _,
                    5usize,
                >([(((v1[b]) * (v2[b])) * (v3[b])) * (v4[b]); 5usize])
            } else {
                res
            };
            let num_vars_multiplicity = self.poly.aux_info.max_num_variables
                - (ceil_log2(v1.len()).max(1) + self.round - 1);
            if num_vars_multiplicity > 0 {
                AdditiveArray(
                    res
                        .0
                        .map(|e| e * E::BaseField::from(1 << num_vars_multiplicity)),
                )
            } else {
                res
            }
        }
        (
            FieldType::Ext(v1),
            FieldType::Ext(v2),
            FieldType::Base(v3),
            FieldType::Base(v4),
        ) => {
            let v1 = if let Some((start, offset)) = f1.evaluations_range() {
                &v1[start..][..offset]
            } else {
                &v1[..]
            };
            let v2 = if let Some((start, offset)) = f2.evaluations_range() {
                &v2[start..][..offset]
            } else {
                &v2[..]
            };
            let v3 = if let Some((start, offset)) = f3.evaluations_range() {
                &v3[start..][..offset]
            } else {
                &v3[..]
            };
            let v4 = if let Some((start, offset)) = f4.evaluations_range() {
                &v4[start..][..offset]
            } else {
                &v4[..]
            };
            let res = (0..largest_even_below(v1.len()))
                .step_by(2)
                .rev()
                .map(|b| {
                    let c1_0 = v1[b + 1] - v1[b];
                    let c1_1 = c1_0 + c1_0;
                    let c2_0 = v2[b + 1] - v2[b];
                    let c2_1 = c2_0 + c2_0;
                    let c3_0 = v3[b + 1] - v3[b];
                    let c3_1 = c3_0 + c3_0;
                    let c4_0 = v4[b + 1] - v4[b];
                    let c4_1 = c4_0 + c4_0;
                    AdditiveArray([
                        (((v1[b]) * (v2[b])) * (v3[b])) * (v4[b]),
                        (((v1[b + 1]) * (v2[b + 1])) * (v3[b + 1])) * (v4[b + 1]),
                        (((c1_0 + v1[b + 1]) * (c2_0 + v2[b + 1]))
                            * (c3_0 + v3[b + 1])) * (c4_0 + v4[b + 1]),
                        (((c1_1 + v1[b + 1]) * (c2_1 + v2[b + 1]))
                            * (c3_1 + v3[b + 1])) * (c4_1 + v4[b + 1]),
                        (((c1_0 + c1_1 + v1[b + 1]) * (c2_0 + c2_1 + v2[b + 1]))
                            * (c3_0 + c3_1 + v3[b + 1])) * (c4_0 + c4_1 + v4[b + 1]),
                    ])
                })
                .sum::<AdditiveArray<_, 5usize>>();
            let res = if v1.len() == 1 {
                let b = 0;
                AdditiveArray::<
                    _,
                    5usize,
                >([(((v1[b]) * (v2[b])) * (v3[b])) * (v4[b]); 5usize])
            } else {
                res
            };
            let num_vars_multiplicity = self.poly.aux_info.max_num_variables
                - (ceil_log2(v1.len()).max(1) + self.round - 1);
            if num_vars_multiplicity > 0 {
                AdditiveArray(
                    res
                        .0
                        .map(|e| e * E::BaseField::from(1 << num_vars_multiplicity)),
                )
            } else {
                res
            }
        }
        (
            FieldType::Ext(v1),
            FieldType::Base(v2),
            FieldType::Base(v3),
            FieldType::Base(v4),
        ) => {
            let v1 = if let Some((start, offset)) = f1.evaluations_range() {
                &v1[start..][..offset]
            } else {
                &v1[..]
            };
            let v2 = if let Some((start, offset)) = f2.evaluations_range() {
                &v2[start..][..offset]
            } else {
                &v2[..]
            };
            let v3 = if let Some((start, offset)) = f3.evaluations_range() {
                &v3[start..][..offset]
            } else {
                &v3[..]
            };
            let v4 = if let Some((start, offset)) = f4.evaluations_range() {
                &v4[start..][..offset]
            } else {
                &v4[..]
            };
            let res = (0..largest_even_below(v1.len()))
                .step_by(2)
                .rev()
                .map(|b| {
                    let c1_0 = v1[b + 1] - v1[b];
                    let c1_1 = c1_0 + c1_0;
                    let c2_0 = v2[b + 1] - v2[b];
                    let c2_1 = c2_0 + c2_0;
                    let c3_0 = v3[b + 1] - v3[b];
                    let c3_1 = c3_0 + c3_0;
                    let c4_0 = v4[b + 1] - v4[b];
                    let c4_1 = c4_0 + c4_0;
                    AdditiveArray([
                        (((v1[b]) * (v2[b])) * (v3[b])) * (v4[b]),
                        (((v1[b + 1]) * (v2[b + 1])) * (v3[b + 1])) * (v4[b + 1]),
                        (((c1_0 + v1[b + 1]) * (c2_0 + v2[b + 1]))
                            * (c3_0 + v3[b + 1])) * (c4_0 + v4[b + 1]),
                        (((c1_1 + v1[b + 1]) * (c2_1 + v2[b + 1]))
                            * (c3_1 + v3[b + 1])) * (c4_1 + v4[b + 1]),
                        (((c1_0 + c1_1 + v1[b + 1]) * (c2_0 + c2_1 + v2[b + 1]))
                            * (c3_0 + c3_1 + v3[b + 1])) * (c4_0 + c4_1 + v4[b + 1]),
                    ])
                })
                .sum::<AdditiveArray<_, 5usize>>();
            let res = if v1.len() == 1 {
                let b = 0;
                AdditiveArray::<
                    _,
                    5usize,
                >([(((v1[b]) * (v2[b])) * (v3[b])) * (v4[b]); 5usize])
            } else {
                res
            };
            let num_vars_multiplicity = self.poly.aux_info.max_num_variables
                - (ceil_log2(v1.len()).max(1) + self.round - 1);
            if num_vars_multiplicity > 0 {
                AdditiveArray(
                    res
                        .0
                        .map(|e| e * E::BaseField::from(1 << num_vars_multiplicity)),
                )
            } else {
                res
            }
        }
        (
            FieldType::Base(v1),
            FieldType::Base(v2),
            FieldType::Base(v3),
            FieldType::Base(v4),
        ) => {
            let v1 = if let Some((start, offset)) = f1.evaluations_range() {
                &v1[start..][..offset]
            } else {
                &v1[..]
            };
            let v2 = if let Some((start, offset)) = f2.evaluations_range() {
                &v2[start..][..offset]
            } else {
                &v2[..]
            };
            let v3 = if let Some((start, offset)) = f3.evaluations_range() {
                &v3[start..][..offset]
            } else {
                &v3[..]
            };
            let v4 = if let Some((start, offset)) = f4.evaluations_range() {
                &v4[start..][..offset]
            } else {
                &v4[..]
            };
            let result = {
                let res = (0..largest_even_below(v1.len()))
                    .step_by(2)
                    .rev()
                    .map(|b| {
                        let c1_0 = v1[b + 1] - v1[b];
                        let c1_1 = c1_0 + c1_0;
                        let c2_0 = v2[b + 1] - v2[b];
                        let c2_1 = c2_0 + c2_0;
                        let c3_0 = v3[b + 1] - v3[b];
                        let c3_1 = c3_0 + c3_0;
                        let c4_0 = v4[b + 1] - v4[b];
                        let c4_1 = c4_0 + c4_0;
                        AdditiveArray([
                            (((v1[b]) * (v2[b])) * (v3[b])) * (v4[b]),
                            (((v1[b + 1]) * (v2[b + 1])) * (v3[b + 1])) * (v4[b + 1]),
                            (((c1_0 + v1[b + 1]) * (c2_0 + v2[b + 1]))
                                * (c3_0 + v3[b + 1])) * (c4_0 + v4[b + 1]),
                            (((c1_1 + v1[b + 1]) * (c2_1 + v2[b + 1]))
                                * (c3_1 + v3[b + 1])) * (c4_1 + v4[b + 1]),
                            (((c1_0 + c1_1 + v1[b + 1]) * (c2_0 + c2_1 + v2[b + 1]))
                                * (c3_0 + c3_1 + v3[b + 1])) * (c4_0 + c4_1 + v4[b + 1]),
                        ])
                    })
                    .sum::<AdditiveArray<_, 5usize>>();
                let res = if v1.len() == 1 {
                    let b = 0;
                    AdditiveArray::<
                        _,
                        5usize,
                    >([(((v1[b]) * (v2[b])) * (v3[b])) * (v4[b]); 5usize])
                } else {
                    res
                };
                let num_vars_multiplicity = self.poly.aux_info.max_num_variables
                    - (ceil_log2(v1.len()).max(1) + self.round - 1);
                if num_vars_multiplicity > 0 {
                    AdditiveArray(
                        res
                            .0
                            .map(|e| e * E::BaseField::from(1 << num_vars_multiplicity)),
                    )
                } else {
                    res
                }
            };
            AdditiveArray(result.0.map(E::from))
        }
        _ => ::core::panicking::panic("internal error: entered unreachable code"),
    }
};
```
</details>


<details>
  <summary>code for degree 5</summary>

```rs
let _result: AdditiveArray<_, 6> = {
  let product_access = |_| &self.poly.flattened_ml_extensions[0];
  let f1 = product_access(0usize);
  let f2 = product_access(1usize);
  let f3 = product_access(2usize);
  let f4 = product_access(3usize);
  let f5 = product_access(4usize);
  let (f1, f2, f3, f4, f5) = match (
      &f1.evaluations(),
      &f2.evaluations(),
      &f3.evaluations(),
      &f4.evaluations(),
      &f5.evaluations(),
  ) {
      (
          FieldType::Base(_),
          FieldType::Ext(_),
          FieldType::Ext(_),
          FieldType::Ext(_),
          FieldType::Ext(_),
      ) => (f2, f3, f4, f5, f1),
      (
          FieldType::Ext(_),
          FieldType::Base(_),
          FieldType::Ext(_),
          FieldType::Ext(_),
          FieldType::Ext(_),
      ) => (f1, f3, f4, f5, f2),
      (
          FieldType::Base(_),
          FieldType::Base(_),
          FieldType::Ext(_),
          FieldType::Ext(_),
          FieldType::Ext(_),
      ) => (f3, f4, f5, f1, f2),
      (
          FieldType::Ext(_),
          FieldType::Ext(_),
          FieldType::Base(_),
          FieldType::Ext(_),
          FieldType::Ext(_),
      ) => (f1, f2, f4, f5, f3),
      (
          FieldType::Base(_),
          FieldType::Ext(_),
          FieldType::Base(_),
          FieldType::Ext(_),
          FieldType::Ext(_),
      ) => (f2, f4, f5, f1, f3),
      (
          FieldType::Ext(_),
          FieldType::Base(_),
          FieldType::Base(_),
          FieldType::Ext(_),
          FieldType::Ext(_),
      ) => (f1, f4, f5, f2, f3),
      (
          FieldType::Base(_),
          FieldType::Base(_),
          FieldType::Base(_),
          FieldType::Ext(_),
          FieldType::Ext(_),
      ) => (f4, f5, f1, f2, f3),
      (
          FieldType::Ext(_),
          FieldType::Ext(_),
          FieldType::Ext(_),
          FieldType::Base(_),
          FieldType::Ext(_),
      ) => (f1, f2, f3, f5, f4),
      (
          FieldType::Base(_),
          FieldType::Ext(_),
          FieldType::Ext(_),
          FieldType::Base(_),
          FieldType::Ext(_),
      ) => (f2, f3, f5, f1, f4),
      (
          FieldType::Ext(_),
          FieldType::Base(_),
          FieldType::Ext(_),
          FieldType::Base(_),
          FieldType::Ext(_),
      ) => (f1, f3, f5, f2, f4),
      (
          FieldType::Base(_),
          FieldType::Base(_),
          FieldType::Ext(_),
          FieldType::Base(_),
          FieldType::Ext(_),
      ) => (f3, f5, f1, f2, f4),
      (
          FieldType::Ext(_),
          FieldType::Ext(_),
          FieldType::Base(_),
          FieldType::Base(_),
          FieldType::Ext(_),
      ) => (f1, f2, f5, f3, f4),
      (
          FieldType::Base(_),
          FieldType::Ext(_),
          FieldType::Base(_),
          FieldType::Base(_),
          FieldType::Ext(_),
      ) => (f2, f5, f1, f3, f4),
      (
          FieldType::Ext(_),
          FieldType::Base(_),
          FieldType::Base(_),
          FieldType::Base(_),
          FieldType::Ext(_),
      ) => (f1, f5, f2, f3, f4),
      (
          FieldType::Base(_),
          FieldType::Base(_),
          FieldType::Base(_),
          FieldType::Base(_),
          FieldType::Ext(_),
      ) => (f5, f1, f2, f3, f4),
      (
          FieldType::Base(_),
          FieldType::Ext(_),
          FieldType::Ext(_),
          FieldType::Ext(_),
          FieldType::Base(_),
      ) => (f2, f3, f4, f1, f5),
      (
          FieldType::Ext(_),
          FieldType::Base(_),
          FieldType::Ext(_),
          FieldType::Ext(_),
          FieldType::Base(_),
      ) => (f1, f3, f4, f2, f5),
      (
          FieldType::Base(_),
          FieldType::Base(_),
          FieldType::Ext(_),
          FieldType::Ext(_),
          FieldType::Base(_),
      ) => (f3, f4, f1, f2, f5),
      (
          FieldType::Ext(_),
          FieldType::Ext(_),
          FieldType::Base(_),
          FieldType::Ext(_),
          FieldType::Base(_),
      ) => (f1, f2, f4, f3, f5),
      (
          FieldType::Base(_),
          FieldType::Ext(_),
          FieldType::Base(_),
          FieldType::Ext(_),
          FieldType::Base(_),
      ) => (f2, f4, f1, f3, f5),
      (
          FieldType::Ext(_),
          FieldType::Base(_),
          FieldType::Base(_),
          FieldType::Ext(_),
          FieldType::Base(_),
      ) => (f1, f4, f2, f3, f5),
      (
          FieldType::Base(_),
          FieldType::Base(_),
          FieldType::Base(_),
          FieldType::Ext(_),
          FieldType::Base(_),
      ) => (f4, f1, f2, f3, f5),
      (
          FieldType::Base(_),
          FieldType::Ext(_),
          FieldType::Ext(_),
          FieldType::Base(_),
          FieldType::Base(_),
      ) => (f2, f3, f1, f4, f5),
      (
          FieldType::Ext(_),
          FieldType::Base(_),
          FieldType::Ext(_),
          FieldType::Base(_),
          FieldType::Base(_),
      ) => (f1, f3, f2, f4, f5),
      (
          FieldType::Base(_),
          FieldType::Base(_),
          FieldType::Ext(_),
          FieldType::Base(_),
          FieldType::Base(_),
      ) => (f3, f1, f2, f4, f5),
      (
          FieldType::Base(_),
          FieldType::Ext(_),
          FieldType::Base(_),
          FieldType::Base(_),
          FieldType::Base(_),
      ) => (f2, f1, f3, f4, f5),
      _ => (f1, f2, f3, f4, f5),
  };
  match (
      &f1.evaluations(),
      &f2.evaluations(),
      &f3.evaluations(),
      &f4.evaluations(),
      &f5.evaluations(),
  ) {
      (
          FieldType::Ext(v1),
          FieldType::Ext(v2),
          FieldType::Ext(v3),
          FieldType::Ext(v4),
          FieldType::Ext(v5),
      ) => {
          let v1 = if let Some((start, offset)) = f1.evaluations_range() {
              &v1[start..][..offset]
          } else {
              &v1[..]
          };
          let v2 = if let Some((start, offset)) = f2.evaluations_range() {
              &v2[start..][..offset]
          } else {
              &v2[..]
          };
          let v3 = if let Some((start, offset)) = f3.evaluations_range() {
              &v3[start..][..offset]
          } else {
              &v3[..]
          };
          let v4 = if let Some((start, offset)) = f4.evaluations_range() {
              &v4[start..][..offset]
          } else {
              &v4[..]
          };
          let v5 = if let Some((start, offset)) = f5.evaluations_range() {
              &v5[start..][..offset]
          } else {
              &v5[..]
          };
          let res = (0..largest_even_below(v1.len()))
              .step_by(2)
              .rev()
              .map(|b| {
                  let c1_0 = v1[b + 1] - v1[b];
                  let c1_1 = c1_0 + c1_0;
                  let c1_2 = c1_1 + c1_1;
                  let c2_0 = v2[b + 1] - v2[b];
                  let c2_1 = c2_0 + c2_0;
                  let c2_2 = c2_1 + c2_1;
                  let c3_0 = v3[b + 1] - v3[b];
                  let c3_1 = c3_0 + c3_0;
                  let c3_2 = c3_1 + c3_1;
                  let c4_0 = v4[b + 1] - v4[b];
                  let c4_1 = c4_0 + c4_0;
                  let c4_2 = c4_1 + c4_1;
                  let c5_0 = v5[b + 1] - v5[b];
                  let c5_1 = c5_0 + c5_0;
                  let c5_2 = c5_1 + c5_1;
                  AdditiveArray([
                      ((((v1[b]) * (v2[b])) * (v3[b])) * (v4[b])) * (v5[b]),
                      ((((v1[b + 1]) * (v2[b + 1])) * (v3[b + 1])) * (v4[b + 1]))
                          * (v5[b + 1]),
                      ((((c1_0 + v1[b + 1]) * (c2_0 + v2[b + 1]))
                          * (c3_0 + v3[b + 1])) * (c4_0 + v4[b + 1]))
                          * (c5_0 + v5[b + 1]),
                      ((((c1_1 + v1[b + 1]) * (c2_1 + v2[b + 1]))
                          * (c3_1 + v3[b + 1])) * (c4_1 + v4[b + 1]))
                          * (c5_1 + v5[b + 1]),
                      ((((c1_0 + c1_1 + v1[b + 1]) * (c2_0 + c2_1 + v2[b + 1]))
                          * (c3_0 + c3_1 + v3[b + 1])) * (c4_0 + c4_1 + v4[b + 1]))
                          * (c5_0 + c5_1 + v5[b + 1]),
                      ((((c1_2 + v1[b + 1]) * (c2_2 + v2[b + 1]))
                          * (c3_2 + v3[b + 1])) * (c4_2 + v4[b + 1]))
                          * (c5_2 + v5[b + 1]),
                  ])
              })
              .sum::<AdditiveArray<_, 6usize>>();
          let res = if v1.len() == 1 {
              let b = 0;
              AdditiveArray::<
                  _,
                  6usize,
              >(
                  [((((v1[b]) * (v2[b])) * (v3[b])) * (v4[b]))
                      * (v5[b]); 6usize],
              )
          } else {
              res
          };
          let num_vars_multiplicity = self.poly.aux_info.max_num_variables
              - (ceil_log2(v1.len()).max(1) + self.round - 1);
          if num_vars_multiplicity > 0 {
              AdditiveArray(
                  res
                      .0
                      .map(|e| e * E::BaseField::from(1 << num_vars_multiplicity)),
              )
          } else {
              res
          }
      }
      (
          FieldType::Ext(v1),
          FieldType::Ext(v2),
          FieldType::Ext(v3),
          FieldType::Ext(v4),
          FieldType::Base(v5),
      ) => {
          let v1 = if let Some((start, offset)) = f1.evaluations_range() {
              &v1[start..][..offset]
          } else {
              &v1[..]
          };
          let v2 = if let Some((start, offset)) = f2.evaluations_range() {
              &v2[start..][..offset]
          } else {
              &v2[..]
          };
          let v3 = if let Some((start, offset)) = f3.evaluations_range() {
              &v3[start..][..offset]
          } else {
              &v3[..]
          };
          let v4 = if let Some((start, offset)) = f4.evaluations_range() {
              &v4[start..][..offset]
          } else {
              &v4[..]
          };
          let v5 = if let Some((start, offset)) = f5.evaluations_range() {
              &v5[start..][..offset]
          } else {
              &v5[..]
          };
          let res = (0..largest_even_below(v1.len()))
              .step_by(2)
              .rev()
              .map(|b| {
                  let c1_0 = v1[b + 1] - v1[b];
                  let c1_1 = c1_0 + c1_0;
                  let c1_2 = c1_1 + c1_1;
                  let c2_0 = v2[b + 1] - v2[b];
                  let c2_1 = c2_0 + c2_0;
                  let c2_2 = c2_1 + c2_1;
                  let c3_0 = v3[b + 1] - v3[b];
                  let c3_1 = c3_0 + c3_0;
                  let c3_2 = c3_1 + c3_1;
                  let c4_0 = v4[b + 1] - v4[b];
                  let c4_1 = c4_0 + c4_0;
                  let c4_2 = c4_1 + c4_1;
                  let c5_0 = v5[b + 1] - v5[b];
                  let c5_1 = c5_0 + c5_0;
                  let c5_2 = c5_1 + c5_1;
                  AdditiveArray([
                      ((((v1[b]) * (v2[b])) * (v3[b])) * (v4[b])) * (v5[b]),
                      ((((v1[b + 1]) * (v2[b + 1])) * (v3[b + 1])) * (v4[b + 1]))
                          * (v5[b + 1]),
                      ((((c1_0 + v1[b + 1]) * (c2_0 + v2[b + 1]))
                          * (c3_0 + v3[b + 1])) * (c4_0 + v4[b + 1]))
                          * (c5_0 + v5[b + 1]),
                      ((((c1_1 + v1[b + 1]) * (c2_1 + v2[b + 1]))
                          * (c3_1 + v3[b + 1])) * (c4_1 + v4[b + 1]))
                          * (c5_1 + v5[b + 1]),
                      ((((c1_0 + c1_1 + v1[b + 1]) * (c2_0 + c2_1 + v2[b + 1]))
                          * (c3_0 + c3_1 + v3[b + 1])) * (c4_0 + c4_1 + v4[b + 1]))
                          * (c5_0 + c5_1 + v5[b + 1]),
                      ((((c1_2 + v1[b + 1]) * (c2_2 + v2[b + 1]))
                          * (c3_2 + v3[b + 1])) * (c4_2 + v4[b + 1]))
                          * (c5_2 + v5[b + 1]),
                  ])
              })
              .sum::<AdditiveArray<_, 6usize>>();
          let res = if v1.len() == 1 {
              let b = 0;
              AdditiveArray::<
                  _,
                  6usize,
              >(
                  [((((v1[b]) * (v2[b])) * (v3[b])) * (v4[b]))
                      * (v5[b]); 6usize],
              )
          } else {
              res
          };
          let num_vars_multiplicity = self.poly.aux_info.max_num_variables
              - (ceil_log2(v1.len()).max(1) + self.round - 1);
          if num_vars_multiplicity > 0 {
              AdditiveArray(
                  res
                      .0
                      .map(|e| e * E::BaseField::from(1 << num_vars_multiplicity)),
              )
          } else {
              res
          }
      }
      (
          FieldType::Ext(v1),
          FieldType::Ext(v2),
          FieldType::Ext(v3),
          FieldType::Base(v4),
          FieldType::Base(v5),
      ) => {
          let v1 = if let Some((start, offset)) = f1.evaluations_range() {
              &v1[start..][..offset]
          } else {
              &v1[..]
          };
          let v2 = if let Some((start, offset)) = f2.evaluations_range() {
              &v2[start..][..offset]
          } else {
              &v2[..]
          };
          let v3 = if let Some((start, offset)) = f3.evaluations_range() {
              &v3[start..][..offset]
          } else {
              &v3[..]
          };
          let v4 = if let Some((start, offset)) = f4.evaluations_range() {
              &v4[start..][..offset]
          } else {
              &v4[..]
          };
          let v5 = if let Some((start, offset)) = f5.evaluations_range() {
              &v5[start..][..offset]
          } else {
              &v5[..]
          };
          let res = (0..largest_even_below(v1.len()))
              .step_by(2)
              .rev()
              .map(|b| {
                  let c1_0 = v1[b + 1] - v1[b];
                  let c1_1 = c1_0 + c1_0;
                  let c1_2 = c1_1 + c1_1;
                  let c2_0 = v2[b + 1] - v2[b];
                  let c2_1 = c2_0 + c2_0;
                  let c2_2 = c2_1 + c2_1;
                  let c3_0 = v3[b + 1] - v3[b];
                  let c3_1 = c3_0 + c3_0;
                  let c3_2 = c3_1 + c3_1;
                  let c4_0 = v4[b + 1] - v4[b];
                  let c4_1 = c4_0 + c4_0;
                  let c4_2 = c4_1 + c4_1;
                  let c5_0 = v5[b + 1] - v5[b];
                  let c5_1 = c5_0 + c5_0;
                  let c5_2 = c5_1 + c5_1;
                  AdditiveArray([
                      ((((v1[b]) * (v2[b])) * (v3[b])) * (v4[b])) * (v5[b]),
                      ((((v1[b + 1]) * (v2[b + 1])) * (v3[b + 1])) * (v4[b + 1]))
                          * (v5[b + 1]),
                      ((((c1_0 + v1[b + 1]) * (c2_0 + v2[b + 1]))
                          * (c3_0 + v3[b + 1])) * (c4_0 + v4[b + 1]))
                          * (c5_0 + v5[b + 1]),
                      ((((c1_1 + v1[b + 1]) * (c2_1 + v2[b + 1]))
                          * (c3_1 + v3[b + 1])) * (c4_1 + v4[b + 1]))
                          * (c5_1 + v5[b + 1]),
                      ((((c1_0 + c1_1 + v1[b + 1]) * (c2_0 + c2_1 + v2[b + 1]))
                          * (c3_0 + c3_1 + v3[b + 1])) * (c4_0 + c4_1 + v4[b + 1]))
                          * (c5_0 + c5_1 + v5[b + 1]),
                      ((((c1_2 + v1[b + 1]) * (c2_2 + v2[b + 1]))
                          * (c3_2 + v3[b + 1])) * (c4_2 + v4[b + 1]))
                          * (c5_2 + v5[b + 1]),
                  ])
              })
              .sum::<AdditiveArray<_, 6usize>>();
          let res = if v1.len() == 1 {
              let b = 0;
              AdditiveArray::<
                  _,
                  6usize,
              >(
                  [((((v1[b]) * (v2[b])) * (v3[b])) * (v4[b]))
                      * (v5[b]); 6usize],
              )
          } else {
              res
          };
          let num_vars_multiplicity = self.poly.aux_info.max_num_variables
              - (ceil_log2(v1.len()).max(1) + self.round - 1);
          if num_vars_multiplicity > 0 {
              AdditiveArray(
                  res
                      .0
                      .map(|e| e * E::BaseField::from(1 << num_vars_multiplicity)),
              )
          } else {
              res
          }
      }
      (
          FieldType::Ext(v1),
          FieldType::Ext(v2),
          FieldType::Base(v3),
          FieldType::Base(v4),
          FieldType::Base(v5),
      ) => {
          let v1 = if let Some((start, offset)) = f1.evaluations_range() {
              &v1[start..][..offset]
          } else {
              &v1[..]
          };
          let v2 = if let Some((start, offset)) = f2.evaluations_range() {
              &v2[start..][..offset]
          } else {
              &v2[..]
          };
          let v3 = if let Some((start, offset)) = f3.evaluations_range() {
              &v3[start..][..offset]
          } else {
              &v3[..]
          };
          let v4 = if let Some((start, offset)) = f4.evaluations_range() {
              &v4[start..][..offset]
          } else {
              &v4[..]
          };
          let v5 = if let Some((start, offset)) = f5.evaluations_range() {
              &v5[start..][..offset]
          } else {
              &v5[..]
          };
          let res = (0..largest_even_below(v1.len()))
              .step_by(2)
              .rev()
              .map(|b| {
                  let c1_0 = v1[b + 1] - v1[b];
                  let c1_1 = c1_0 + c1_0;
                  let c1_2 = c1_1 + c1_1;
                  let c2_0 = v2[b + 1] - v2[b];
                  let c2_1 = c2_0 + c2_0;
                  let c2_2 = c2_1 + c2_1;
                  let c3_0 = v3[b + 1] - v3[b];
                  let c3_1 = c3_0 + c3_0;
                  let c3_2 = c3_1 + c3_1;
                  let c4_0 = v4[b + 1] - v4[b];
                  let c4_1 = c4_0 + c4_0;
                  let c4_2 = c4_1 + c4_1;
                  let c5_0 = v5[b + 1] - v5[b];
                  let c5_1 = c5_0 + c5_0;
                  let c5_2 = c5_1 + c5_1;
                  AdditiveArray([
                      ((((v1[b]) * (v2[b])) * (v3[b])) * (v4[b])) * (v5[b]),
                      ((((v1[b + 1]) * (v2[b + 1])) * (v3[b + 1])) * (v4[b + 1]))
                          * (v5[b + 1]),
                      ((((c1_0 + v1[b + 1]) * (c2_0 + v2[b + 1]))
                          * (c3_0 + v3[b + 1])) * (c4_0 + v4[b + 1]))
                          * (c5_0 + v5[b + 1]),
                      ((((c1_1 + v1[b + 1]) * (c2_1 + v2[b + 1]))
                          * (c3_1 + v3[b + 1])) * (c4_1 + v4[b + 1]))
                          * (c5_1 + v5[b + 1]),
                      ((((c1_0 + c1_1 + v1[b + 1]) * (c2_0 + c2_1 + v2[b + 1]))
                          * (c3_0 + c3_1 + v3[b + 1])) * (c4_0 + c4_1 + v4[b + 1]))
                          * (c5_0 + c5_1 + v5[b + 1]),
                      ((((c1_2 + v1[b + 1]) * (c2_2 + v2[b + 1]))
                          * (c3_2 + v3[b + 1])) * (c4_2 + v4[b + 1]))
                          * (c5_2 + v5[b + 1]),
                  ])
              })
              .sum::<AdditiveArray<_, 6usize>>();
          let res = if v1.len() == 1 {
              let b = 0;
              AdditiveArray::<
                  _,
                  6usize,
              >(
                  [((((v1[b]) * (v2[b])) * (v3[b])) * (v4[b]))
                      * (v5[b]); 6usize],
              )
          } else {
              res
          };
          let num_vars_multiplicity = self.poly.aux_info.max_num_variables
              - (ceil_log2(v1.len()).max(1) + self.round - 1);
          if num_vars_multiplicity > 0 {
              AdditiveArray(
                  res
                      .0
                      .map(|e| e * E::BaseField::from(1 << num_vars_multiplicity)),
              )
          } else {
              res
          }
      }
      (
          FieldType::Ext(v1),
          FieldType::Base(v2),
          FieldType::Base(v3),
          FieldType::Base(v4),
          FieldType::Base(v5),
      ) => {
          let v1 = if let Some((start, offset)) = f1.evaluations_range() {
              &v1[start..][..offset]
          } else {
              &v1[..]
          };
          let v2 = if let Some((start, offset)) = f2.evaluations_range() {
              &v2[start..][..offset]
          } else {
              &v2[..]
          };
          let v3 = if let Some((start, offset)) = f3.evaluations_range() {
              &v3[start..][..offset]
          } else {
              &v3[..]
          };
          let v4 = if let Some((start, offset)) = f4.evaluations_range() {
              &v4[start..][..offset]
          } else {
              &v4[..]
          };
          let v5 = if let Some((start, offset)) = f5.evaluations_range() {
              &v5[start..][..offset]
          } else {
              &v5[..]
          };
          let res = (0..largest_even_below(v1.len()))
              .step_by(2)
              .rev()
              .map(|b| {
                  let c1_0 = v1[b + 1] - v1[b];
                  let c1_1 = c1_0 + c1_0;
                  let c1_2 = c1_1 + c1_1;
                  let c2_0 = v2[b + 1] - v2[b];
                  let c2_1 = c2_0 + c2_0;
                  let c2_2 = c2_1 + c2_1;
                  let c3_0 = v3[b + 1] - v3[b];
                  let c3_1 = c3_0 + c3_0;
                  let c3_2 = c3_1 + c3_1;
                  let c4_0 = v4[b + 1] - v4[b];
                  let c4_1 = c4_0 + c4_0;
                  let c4_2 = c4_1 + c4_1;
                  let c5_0 = v5[b + 1] - v5[b];
                  let c5_1 = c5_0 + c5_0;
                  let c5_2 = c5_1 + c5_1;
                  AdditiveArray([
                      ((((v1[b]) * (v2[b])) * (v3[b])) * (v4[b])) * (v5[b]),
                      ((((v1[b + 1]) * (v2[b + 1])) * (v3[b + 1])) * (v4[b + 1]))
                          * (v5[b + 1]),
                      ((((c1_0 + v1[b + 1]) * (c2_0 + v2[b + 1]))
                          * (c3_0 + v3[b + 1])) * (c4_0 + v4[b + 1]))
                          * (c5_0 + v5[b + 1]),
                      ((((c1_1 + v1[b + 1]) * (c2_1 + v2[b + 1]))
                          * (c3_1 + v3[b + 1])) * (c4_1 + v4[b + 1]))
                          * (c5_1 + v5[b + 1]),
                      ((((c1_0 + c1_1 + v1[b + 1]) * (c2_0 + c2_1 + v2[b + 1]))
                          * (c3_0 + c3_1 + v3[b + 1])) * (c4_0 + c4_1 + v4[b + 1]))
                          * (c5_0 + c5_1 + v5[b + 1]),
                      ((((c1_2 + v1[b + 1]) * (c2_2 + v2[b + 1]))
                          * (c3_2 + v3[b + 1])) * (c4_2 + v4[b + 1]))
                          * (c5_2 + v5[b + 1]),
                  ])
              })
              .sum::<AdditiveArray<_, 6usize>>();
          let res = if v1.len() == 1 {
              let b = 0;
              AdditiveArray::<
                  _,
                  6usize,
              >(
                  [((((v1[b]) * (v2[b])) * (v3[b])) * (v4[b]))
                      * (v5[b]); 6usize],
              )
          } else {
              res
          };
          let num_vars_multiplicity = self.poly.aux_info.max_num_variables
              - (ceil_log2(v1.len()).max(1) + self.round - 1);
          if num_vars_multiplicity > 0 {
              AdditiveArray(
                  res
                      .0
                      .map(|e| e * E::BaseField::from(1 << num_vars_multiplicity)),
              )
          } else {
              res
          }
      }
      (
          FieldType::Base(v1),
          FieldType::Base(v2),
          FieldType::Base(v3),
          FieldType::Base(v4),
          FieldType::Base(v5),
      ) => {
          let v1 = if let Some((start, offset)) = f1.evaluations_range() {
              &v1[start..][..offset]
          } else {
              &v1[..]
          };
          let v2 = if let Some((start, offset)) = f2.evaluations_range() {
              &v2[start..][..offset]
          } else {
              &v2[..]
          };
          let v3 = if let Some((start, offset)) = f3.evaluations_range() {
              &v3[start..][..offset]
          } else {
              &v3[..]
          };
          let v4 = if let Some((start, offset)) = f4.evaluations_range() {
              &v4[start..][..offset]
          } else {
              &v4[..]
          };
          let v5 = if let Some((start, offset)) = f5.evaluations_range() {
              &v5[start..][..offset]
          } else {
              &v5[..]
          };
          let result = {
              let res = (0..largest_even_below(v1.len()))
                  .step_by(2)
                  .rev()
                  .map(|b| {
                      let c1_0 = v1[b + 1] - v1[b];
                      let c1_1 = c1_0 + c1_0;
                      let c1_2 = c1_1 + c1_1;
                      let c2_0 = v2[b + 1] - v2[b];
                      let c2_1 = c2_0 + c2_0;
                      let c2_2 = c2_1 + c2_1;
                      let c3_0 = v3[b + 1] - v3[b];
                      let c3_1 = c3_0 + c3_0;
                      let c3_2 = c3_1 + c3_1;
                      let c4_0 = v4[b + 1] - v4[b];
                      let c4_1 = c4_0 + c4_0;
                      let c4_2 = c4_1 + c4_1;
                      let c5_0 = v5[b + 1] - v5[b];
                      let c5_1 = c5_0 + c5_0;
                      let c5_2 = c5_1 + c5_1;
                      AdditiveArray([
                          ((((v1[b]) * (v2[b])) * (v3[b])) * (v4[b])) * (v5[b]),
                          ((((v1[b + 1]) * (v2[b + 1])) * (v3[b + 1])) * (v4[b + 1]))
                              * (v5[b + 1]),
                          ((((c1_0 + v1[b + 1]) * (c2_0 + v2[b + 1]))
                              * (c3_0 + v3[b + 1])) * (c4_0 + v4[b + 1]))
                              * (c5_0 + v5[b + 1]),
                          ((((c1_1 + v1[b + 1]) * (c2_1 + v2[b + 1]))
                              * (c3_1 + v3[b + 1])) * (c4_1 + v4[b + 1]))
                              * (c5_1 + v5[b + 1]),
                          ((((c1_0 + c1_1 + v1[b + 1]) * (c2_0 + c2_1 + v2[b + 1]))
                              * (c3_0 + c3_1 + v3[b + 1])) * (c4_0 + c4_1 + v4[b + 1]))
                              * (c5_0 + c5_1 + v5[b + 1]),
                          ((((c1_2 + v1[b + 1]) * (c2_2 + v2[b + 1]))
                              * (c3_2 + v3[b + 1])) * (c4_2 + v4[b + 1]))
                              * (c5_2 + v5[b + 1]),
                      ])
                  })
                  .sum::<AdditiveArray<_, 6usize>>();
              let res = if v1.len() == 1 {
                  let b = 0;
                  AdditiveArray::<
                      _,
                      6usize,
                  >(
                      [((((v1[b]) * (v2[b])) * (v3[b])) * (v4[b]))
                          * (v5[b]); 6usize],
                  )
              } else {
                  res
              };
              let num_vars_multiplicity = self.poly.aux_info.max_num_variables
                  - (ceil_log2(v1.len()).max(1) + self.round - 1);
              if num_vars_multiplicity > 0 {
                  AdditiveArray(
                      res
                          .0
                          .map(|e| e * E::BaseField::from(1 << num_vars_multiplicity)),
                  )
              } else {
                  res
              }
          };
          AdditiveArray(result.0.map(E::from))
      }
      _ => ::core::panicking::panic("internal error: entered unreachable code"),
  }
};
```
</details>


<!--
## Benchmark comparison

The following 3 benchmarks compare the logic generated by macro in this
PR with existing logic on master branch.

<details>
<summary>cargo bench comparision for degree 1 - bench master first and
then bench my branch</summary>


```
Running benches/devirgo_sumcheck.rs (/Users/sohamzemse/Workspace/pse-work/ceno/target/release/deps/devirgo_sumcheck-48f9aca3940a3f6c)
Gnuplot not found, using plotters backend
Benchmarking sumcheck_nv_25/prove_sumcheck/sumcheck_nv_25: Warming up for 3.0000 s
Warning: Unable to complete 10 samples in 5.0s. You may wish to increase target time to 6.2s.
sumcheck_nv_25/prove_sumcheck/sumcheck_nv_25
                        time:   [111.18 ms 111.61 ms 112.07 ms]
                        change: [-3.9473% -0.8698% +1.0914%] (p = 0.72 > 0.05)
                        No change in performance detected.

Benchmarking sumcheck_nv_26/prove_sumcheck/sumcheck_nv_26: Warming up for 3.0000 s
Warning: Unable to complete 10 samples in 5.0s. You may wish to increase target time to 12.5s.
sumcheck_nv_26/prove_sumcheck/sumcheck_nv_26
                        time:   [215.93 ms 216.47 ms 217.05 ms]
                        change: [-2.6853% -1.5474% -0.5137%] (p = 0.01 < 0.05)
                        Change within noise threshold.

Benchmarking devirgo_nv_25/prove_sumcheck/devirgo_nv_25: Warming up for 3.0000 s
Warning: Unable to complete 10 samples in 5.0s. You may wish to increase target time to 5.4s.
devirgo_nv_25/prove_sumcheck/devirgo_nv_25
                        time:   [33.635 ms 35.796 ms 38.431 ms]
                        change: [-20.299% -12.399% -3.2239%] (p = 0.02 < 0.05)
                        Performance has improved.
Found 2 outliers among 10 measurements (20.00%)
  1 (10.00%) high mild
  1 (10.00%) high severe

Benchmarking devirgo_nv_26/prove_sumcheck/devirgo_nv_26: Warming up for 3.0000 s
Warning: Unable to complete 10 samples in 5.0s. You may wish to increase target time to 11.0s.
devirgo_nv_26/prove_sumcheck/devirgo_nv_26
                        time:   [71.127 ms 75.905 ms 80.883 ms]
                        change: [-6.0512% +2.9059% +12.785%] (p = 0.55 > 0.05)
                        No change in performance detected.
```
</details>


<details>
<summary>cargo bench comparision for degree 2 - bench master first and
then bench my branch</summary>

```
Benchmarking sumcheck_nv_25/prove_sumcheck/sumcheck_nv_25: Warming up for 3.0000 s
Warning: Unable to complete 10 samples in 5.0s. You may wish to increase target time to 12.0s.
sumcheck_nv_25/prove_sumcheck/sumcheck_nv_25
                        time:   [242.91 ms 245.26 ms 248.03 ms]
                        change: [-4.9108% -1.8643% +0.6385%] (p = 0.26 > 0.05)
                        No change in performance detected.
Found 1 outliers among 10 measurements (10.00%)
  1 (10.00%) high mild

Benchmarking sumcheck_nv_26/prove_sumcheck/sumcheck_nv_26: Warming up for 3.0000 s
Warning: Unable to complete 10 samples in 5.0s. You may wish to increase target time to 23.5s.
sumcheck_nv_26/prove_sumcheck/sumcheck_nv_26
                        time:   [485.83 ms 490.86 ms 496.68 ms]
                        change: [-0.4415% +0.8268% +2.3151%] (p = 0.26 > 0.05)
                        No change in performance detected.
Found 1 outliers among 10 measurements (10.00%)
  1 (10.00%) high mild

Benchmarking devirgo_nv_25/prove_sumcheck/devirgo_nv_25: Warming up for 3.0000 s
Warning: Unable to complete 10 samples in 5.0s. You may wish to increase target time to 10.4s.
devirgo_nv_25/prove_sumcheck/devirgo_nv_25
                        time:   [97.346 ms 98.824 ms 100.51 ms]
                        change: [-3.8624% -1.2210% +1.3721%] (p = 0.40 > 0.05)
                        No change in performance detected.

Benchmarking devirgo_nv_26/prove_sumcheck/devirgo_nv_26: Warming up for 3.0000 s
Warning: Unable to complete 10 samples in 5.0s. You may wish to increase target time to 20.6s.
devirgo_nv_26/prove_sumcheck/devirgo_nv_26
                        time:   [206.15 ms 208.47 ms 210.90 ms]
                        change: [-2.8229% -1.1624% +0.3723%] (p = 0.20 > 0.05)
                        No change in performance detected.
```
</details>


<details>
<summary>cargo bench comparision for degree 3 - bench master first and
then bench my branch</summary>

Not sure why performance is improved in degree 3 but not much in degree
1 & 2, I ran cargo bench on master and then on my branch to confirm
multiple times

```
Benchmarking sumcheck_nv_25/prove_sumcheck/sumcheck_nv_25: Warming up for 3.0000 s
Warning: Unable to complete 10 samples in 5.0s. You may wish to increase target time to 17.5s.
sumcheck_nv_25/prove_sumcheck/sumcheck_nv_25
                        time:   [420.40 ms 422.68 ms 425.05 ms]
                        change: [-68.747% -68.536% -68.351%] (p = 0.00 < 0.05)
                        Performance has improved.

Benchmarking sumcheck_nv_26/prove_sumcheck/sumcheck_nv_26: Warming up for 3.0000 s
Warning: Unable to complete 10 samples in 5.0s. You may wish to increase target time to 35.9s.
sumcheck_nv_26/prove_sumcheck/sumcheck_nv_26
                        time:   [830.88 ms 833.34 ms 835.85 ms]
                        change: [-69.212% -69.110% -69.000%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 3 outliers among 10 measurements (30.00%)
  1 (10.00%) low mild
  2 (20.00%) high mild

Benchmarking devirgo_nv_25/prove_sumcheck/devirgo_nv_25: Warming up for 3.0000 s
Warning: Unable to complete 10 samples in 5.0s. You may wish to increase target time to 15.5s.
devirgo_nv_25/prove_sumcheck/devirgo_nv_25
                        time:   [220.39 ms 223.11 ms 225.94 ms]
                        change: [+1.8364% +4.0055% +6.3570%] (p = 0.00 < 0.05)
                        Performance has regressed.

Benchmarking devirgo_nv_26/prove_sumcheck/devirgo_nv_26: Warming up for 3.0000 s
Warning: Unable to complete 10 samples in 5.0s. You may wish to increase target time to 31.5s.
devirgo_nv_26/prove_sumcheck/devirgo_nv_26
                        time:   [432.01 ms 435.79 ms 440.05 ms]
                        change: [-11.019% -3.3941% +2.8531%] (p = 0.50 > 0.05)
                        No change in performance detected.
```
</details>


## Benchmark compare sumcheck degree = 2 vs degree = 4

<details>
<summary>cargo bench comparison between degree 2 and degree 4</summary>

```
   Running benches/devirgo_sumcheck.rs (/Users/sohamzemse/Workspace/pse-work/ceno/target/release/deps/devirgo_sumcheck-48f9aca3940a3f6c)
Gnuplot not found, using plotters backend
Benchmarking sumcheck_nv_25/prove_sumcheck/sumcheck_nv_25: Warming up for 3.0000 s
Warning: Unable to complete 10 samples in 5.0s. You may wish to increase target time to 24.1s.
sumcheck_nv_25/prove_sumcheck/sumcheck_nv_25
                        time:   [648.62 ms 652.93 ms 656.96 ms]
                        change: [+155.69% +159.72% +163.25%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 1 outliers among 10 measurements (10.00%)
  1 (10.00%) low mild

Benchmarking sumcheck_nv_26/prove_sumcheck/sumcheck_nv_26: Warming up for 3.0000 s
Warning: Unable to complete 10 samples in 5.0s. You may wish to increase target time to 49.6s.
sumcheck_nv_26/prove_sumcheck/sumcheck_nv_26
                        time:   [1.2941 s 1.3023 s 1.3116 s]
                        change: [+155.31% +158.89% +162.38%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 2 outliers among 10 measurements (20.00%)
  1 (10.00%) low mild
  1 (10.00%) high mild

Benchmarking devirgo_nv_25/prove_sumcheck/devirgo_nv_25: Warming up for 3.0000 s
Warning: Unable to complete 10 samples in 5.0s. You may wish to increase target time to 22.4s.
devirgo_nv_25/prove_sumcheck/devirgo_nv_25
                        time:   [402.16 ms 409.58 ms 421.65 ms]
                        change: [+280.84% +298.76% +317.64%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 1 outliers among 10 measurements (10.00%)
  1 (10.00%) high severe

Benchmarking devirgo_nv_26/prove_sumcheck/devirgo_nv_26: Warming up for 3.0000 s
Warning: Unable to complete 10 samples in 5.0s. You may wish to increase target time to 44.1s.
devirgo_nv_26/prove_sumcheck/devirgo_nv_26
                        time:   [790.19 ms 797.18 ms 806.07 ms]
                        change: [+263.67% +273.29% +282.60%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 1 outliers among 10 measurements (10.00%)
  1 (10.00%) high severe
```
</details>
-->

---------

Co-authored-by: sm.wu <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant