From 151a0b2c1e1226a614770e134834527ecab13e33 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 02:18:32 +0000 Subject: [PATCH 1/6] Initial plan From 9ce89237ce372e5a25ec03bba9f58632657faf3e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 02:36:27 +0000 Subject: [PATCH 2/6] docs(sumcheck): clarify concrete main-sumcheck expressions Agent-Logs-Url: https://github.com/scroll-tech/ceno/sessions/244b2603-bbd7-43bf-a6a1-23d0f5219026 Co-authored-by: kunxian-xia <1082586+kunxian-xia@users.noreply.github.com> --- gkr_iop/src/gkr/layer/cpu/mod.rs | 4 +++- gkr_iop/src/gkr/layer/gpu/mod.rs | 4 +++- gkr_iop/src/gkr/layer/zerocheck_layer.rs | 7 ++++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/gkr_iop/src/gkr/layer/cpu/mod.rs b/gkr_iop/src/gkr/layer/cpu/mod.rs index de12967a1..aad98cb71 100644 --- a/gkr_iop/src/gkr/layer/cpu/mod.rs +++ b/gkr_iop/src/gkr/layer/cpu/mod.rs @@ -129,7 +129,9 @@ impl> ZerocheckLayerProver selector_ctxs.len() ); - // Main sumcheck: constraints are fully unified in out_sel_and_eval_exprs. + // Main sumcheck polynomial shape: + // Σ_g sel_g(x) * (Σ_j α_{2+offset(g,j)} * expr_{g,j}(x)) + // where selector groups `(sel_g, expr_{g,*})` come from `out_sel_and_eval_exprs`. let span = entered_span!("build_out_points_eq", profiling_4 = true); let main_sumcheck_challenges = chain!( challenges.iter().copied(), diff --git a/gkr_iop/src/gkr/layer/gpu/mod.rs b/gkr_iop/src/gkr/layer/gpu/mod.rs index 14372729e..44acce7ee 100644 --- a/gkr_iop/src/gkr/layer/gpu/mod.rs +++ b/gkr_iop/src/gkr/layer/gpu/mod.rs @@ -98,7 +98,9 @@ impl> ZerocheckLayerProver out_points.len(), ); - // Main sumcheck: constraints are fully unified in out_sel_and_eval_exprs. + // Main sumcheck polynomial shape: + // Σ_g sel_g(x) * (Σ_j α_{2+offset(g,j)} * expr_{g,j}(x)) + // where selector groups `(sel_g, expr_{g,*})` come from `out_sel_and_eval_exprs`. let main_sumcheck_challenges = chain!( challenges.iter().copied(), get_challenge_pows(layer.exprs.len(), transcript) diff --git a/gkr_iop/src/gkr/layer/zerocheck_layer.rs b/gkr_iop/src/gkr/layer/zerocheck_layer.rs index 2ac2cb2b6..89834d456 100644 --- a/gkr_iop/src/gkr/layer/zerocheck_layer.rs +++ b/gkr_iop/src/gkr/layer/zerocheck_layer.rs @@ -143,7 +143,12 @@ impl ZerocheckLayer for Layer { }) .collect::>(); - // build main sumcheck expression + // Build the concrete main-sumcheck polynomial: + // For each selector group g with expressions expr_{g,0..k-1}, + // group_expr_g(x) = sel_g(x) * Σ_j (α_{2+offset(g,j)} * expr_{g,j}(x)) + // and the final polynomial is: + // main_sumcheck_expr(x) = Σ_g group_expr_g(x). + // `rlc_zero_expr` returns those per-group `group_expr_g`, then we sum them here. let alpha_pows_expr = (2..) .take(self.exprs.len()) .map(|id| Expression::Challenge(id as ChallengeId, 1, E::ONE, E::ZERO)) From 8bfca68ae1ca73f0faaf1eec13e58c171bbfdd2a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 03:22:43 +0000 Subject: [PATCH 3/6] docs(sumcheck): spell out smaller batched sumcheck expressions Agent-Logs-Url: https://github.com/scroll-tech/ceno/sessions/171ba071-eaef-4381-9685-45c0c5450609 Co-authored-by: kunxian-xia <1082586+kunxian-xia@users.noreply.github.com> --- gkr_iop/src/gkr/layer/cpu/mod.rs | 8 +++++--- gkr_iop/src/gkr/layer/gpu/mod.rs | 8 +++++--- gkr_iop/src/gkr/layer/zerocheck_layer.rs | 14 ++++++++------ 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/gkr_iop/src/gkr/layer/cpu/mod.rs b/gkr_iop/src/gkr/layer/cpu/mod.rs index aad98cb71..4f78ce8f7 100644 --- a/gkr_iop/src/gkr/layer/cpu/mod.rs +++ b/gkr_iop/src/gkr/layer/cpu/mod.rs @@ -129,9 +129,11 @@ impl> ZerocheckLayerProver selector_ctxs.len() ); - // Main sumcheck polynomial shape: - // Σ_g sel_g(x) * (Σ_j α_{2+offset(g,j)} * expr_{g,j}(x)) - // where selector groups `(sel_g, expr_{g,*})` come from `out_sel_and_eval_exprs`. + // Main sumcheck batches smaller selector-group sumchecks. + // Per group g (from `out_sel_and_eval_exprs`): + // p_g(x) = sel_g(x) * Σ_j (α_{2+offset(g,j)} * expr_{g,j}(x)), + // S_g = Σ_{x in {0,1}^n} p_g(x). + // The batched polynomial is p(x) = Σ_g p_g(x), so Σ_x p(x) = Σ_g S_g. let span = entered_span!("build_out_points_eq", profiling_4 = true); let main_sumcheck_challenges = chain!( challenges.iter().copied(), diff --git a/gkr_iop/src/gkr/layer/gpu/mod.rs b/gkr_iop/src/gkr/layer/gpu/mod.rs index 44acce7ee..c508c8c78 100644 --- a/gkr_iop/src/gkr/layer/gpu/mod.rs +++ b/gkr_iop/src/gkr/layer/gpu/mod.rs @@ -98,9 +98,11 @@ impl> ZerocheckLayerProver out_points.len(), ); - // Main sumcheck polynomial shape: - // Σ_g sel_g(x) * (Σ_j α_{2+offset(g,j)} * expr_{g,j}(x)) - // where selector groups `(sel_g, expr_{g,*})` come from `out_sel_and_eval_exprs`. + // Main sumcheck batches smaller selector-group sumchecks. + // Per group g (from `out_sel_and_eval_exprs`): + // p_g(x) = sel_g(x) * Σ_j (α_{2+offset(g,j)} * expr_{g,j}(x)), + // S_g = Σ_{x in {0,1}^n} p_g(x). + // The batched polynomial is p(x) = Σ_g p_g(x), so Σ_x p(x) = Σ_g S_g. let main_sumcheck_challenges = chain!( challenges.iter().copied(), get_challenge_pows(layer.exprs.len(), transcript) diff --git a/gkr_iop/src/gkr/layer/zerocheck_layer.rs b/gkr_iop/src/gkr/layer/zerocheck_layer.rs index 89834d456..cf74cd931 100644 --- a/gkr_iop/src/gkr/layer/zerocheck_layer.rs +++ b/gkr_iop/src/gkr/layer/zerocheck_layer.rs @@ -143,12 +143,14 @@ impl ZerocheckLayer for Layer { }) .collect::>(); - // Build the concrete main-sumcheck polynomial: - // For each selector group g with expressions expr_{g,0..k-1}, - // group_expr_g(x) = sel_g(x) * Σ_j (α_{2+offset(g,j)} * expr_{g,j}(x)) - // and the final polynomial is: - // main_sumcheck_expr(x) = Σ_g group_expr_g(x). - // `rlc_zero_expr` returns those per-group `group_expr_g`, then we sum them here. + // Build the concrete main-sumcheck polynomial by batching smaller sumchecks. + // For each selector group g with expressions expr_{g,0..k-1}, define: + // p_g(x) = sel_g(x) * Σ_j (α_{2+offset(g,j)} * expr_{g,j}(x)). + // The corresponding smaller sumcheck target is: + // S_g = Σ_{x in {0,1}^n} p_g(x). + // Main sumcheck batches them into: + // p(x) = Σ_g p_g(x), so Σ_{x in {0,1}^n} p(x) = Σ_g S_g. + // `rlc_zero_expr` returns the per-group p_g terms, then we sum them into p. let alpha_pows_expr = (2..) .take(self.exprs.len()) .map(|id| Expression::Challenge(id as ChallengeId, 1, E::ONE, E::ZERO)) From 6d17c1975469a8f671a555048f519435db4a0d6f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 04:03:40 +0000 Subject: [PATCH 4/6] docs(sumcheck): note each batched zerocheck has zero target sum Agent-Logs-Url: https://github.com/scroll-tech/ceno/sessions/a272823d-c7eb-4dad-a2f2-3905058f7ed0 Co-authored-by: kunxian-xia <1082586+kunxian-xia@users.noreply.github.com> --- gkr_iop/src/gkr/layer/cpu/mod.rs | 3 ++- gkr_iop/src/gkr/layer/gpu/mod.rs | 3 ++- gkr_iop/src/gkr/layer/zerocheck_layer.rs | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/gkr_iop/src/gkr/layer/cpu/mod.rs b/gkr_iop/src/gkr/layer/cpu/mod.rs index 4f78ce8f7..9269ff856 100644 --- a/gkr_iop/src/gkr/layer/cpu/mod.rs +++ b/gkr_iop/src/gkr/layer/cpu/mod.rs @@ -133,7 +133,8 @@ impl> ZerocheckLayerProver // Per group g (from `out_sel_and_eval_exprs`): // p_g(x) = sel_g(x) * Σ_j (α_{2+offset(g,j)} * expr_{g,j}(x)), // S_g = Σ_{x in {0,1}^n} p_g(x). - // The batched polynomial is p(x) = Σ_g p_g(x), so Σ_x p(x) = Σ_g S_g. + // For zerocheck constraints (from each chip), S_g is expected to be 0. + // The batched polynomial is p(x) = Σ_g p_g(x), so Σ_x p(x) = Σ_g S_g = 0. let span = entered_span!("build_out_points_eq", profiling_4 = true); let main_sumcheck_challenges = chain!( challenges.iter().copied(), diff --git a/gkr_iop/src/gkr/layer/gpu/mod.rs b/gkr_iop/src/gkr/layer/gpu/mod.rs index c508c8c78..072f89156 100644 --- a/gkr_iop/src/gkr/layer/gpu/mod.rs +++ b/gkr_iop/src/gkr/layer/gpu/mod.rs @@ -102,7 +102,8 @@ impl> ZerocheckLayerProver // Per group g (from `out_sel_and_eval_exprs`): // p_g(x) = sel_g(x) * Σ_j (α_{2+offset(g,j)} * expr_{g,j}(x)), // S_g = Σ_{x in {0,1}^n} p_g(x). - // The batched polynomial is p(x) = Σ_g p_g(x), so Σ_x p(x) = Σ_g S_g. + // For zerocheck constraints (from each chip), S_g is expected to be 0. + // The batched polynomial is p(x) = Σ_g p_g(x), so Σ_x p(x) = Σ_g S_g = 0. let main_sumcheck_challenges = chain!( challenges.iter().copied(), get_challenge_pows(layer.exprs.len(), transcript) diff --git a/gkr_iop/src/gkr/layer/zerocheck_layer.rs b/gkr_iop/src/gkr/layer/zerocheck_layer.rs index cf74cd931..23dd1ac8a 100644 --- a/gkr_iop/src/gkr/layer/zerocheck_layer.rs +++ b/gkr_iop/src/gkr/layer/zerocheck_layer.rs @@ -148,8 +148,9 @@ impl ZerocheckLayer for Layer { // p_g(x) = sel_g(x) * Σ_j (α_{2+offset(g,j)} * expr_{g,j}(x)). // The corresponding smaller sumcheck target is: // S_g = Σ_{x in {0,1}^n} p_g(x). + // For zerocheck constraints contributed by each chip, the expected target is S_g = 0. // Main sumcheck batches them into: - // p(x) = Σ_g p_g(x), so Σ_{x in {0,1}^n} p(x) = Σ_g S_g. + // p(x) = Σ_g p_g(x), so Σ_{x in {0,1}^n} p(x) = Σ_g S_g = 0. // `rlc_zero_expr` returns the per-group p_g terms, then we sum them into p. let alpha_pows_expr = (2..) .take(self.exprs.len()) From 75082ddd699b39c48c7df287cf004a33741d534c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 04:26:22 +0000 Subject: [PATCH 5/6] docs(layer): explain output eval group construction in from_circuit_builder Agent-Logs-Url: https://github.com/scroll-tech/ceno/sessions/4816d2bc-fdbc-4b58-9b54-ad62a2d5eeb8 Co-authored-by: kunxian-xia <1082586+kunxian-xia@users.noreply.github.com> --- gkr_iop/src/gkr/layer.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/gkr_iop/src/gkr/layer.rs b/gkr_iop/src/gkr/layer.rs index ebf279b11..a2a421bd6 100644 --- a/gkr_iop/src/gkr/layer.rs +++ b/gkr_iop/src/gkr/layer.rs @@ -337,6 +337,20 @@ impl Layer { assert_eq!(w_record_evals.len(), w_len); assert_eq!(lookup_evals.len(), lk_len); assert_eq!(zero_evals.len(), zero_len); + // Construction of output-evaluation groups used by the main zerocheck: + // - Read group (`r_selector`): sel_r(x) * (r_i(x) - 1) = (claim^r_i - 1) + // - Write group (`w_selector`): sel_w(x) * (w_i(x) - 1) = (claim^w_i - 1) + // - Lookup group (`lk_selector`): sel_lk(x) * f_i(x) = claim^lk_i + // where f_i is normalized to absorb lookup padding alpha: + // non-negated: f_i = lookup_i - alpha, claim^lk_i = claim_i - alpha + // negated: f_i = lookup_i + alpha, claim^lk_i = alpha - claim_i + // - Rotation groups (3 groups): left/right/target claims, each one eq-selected. + // - ECC bridge groups (5 groups): x/y/slope/x3/y3 claims, each one selector-separated. + // - Zero group (`zero_selector`): sel_0(x) * z_i(x) = 0 (encoded via EvalExpression::Zero). + // + // The final batched main-sumcheck polynomial is formed as: + // p(x) = Σ_g sel_g(x) * (Σ_i α^{offset(g,i)} * (expr_{g,i}(x) - eval_{g,i})). + // Here, `expr_evals` below constructs (g -> {(expr_{g,i}, eval_{g,i})_i}) for all groups. let rotation_expr_len = cb.cs.rotations.len() * ROTATION_OPENING_COUNT; let ecc_bridge_expr_len = if cb.cs.ec_point_exprs.is_empty() { From 9e005d5e8b8068daae2f03e273561caaf02f7f21 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 04:30:00 +0000 Subject: [PATCH 6/6] docs(layer): clarify offset indexing in batched sumcheck formula Agent-Logs-Url: https://github.com/scroll-tech/ceno/sessions/4816d2bc-fdbc-4b58-9b54-ad62a2d5eeb8 Co-authored-by: kunxian-xia <1082586+kunxian-xia@users.noreply.github.com> --- gkr_iop/src/gkr/layer.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gkr_iop/src/gkr/layer.rs b/gkr_iop/src/gkr/layer.rs index a2a421bd6..4e0fa3020 100644 --- a/gkr_iop/src/gkr/layer.rs +++ b/gkr_iop/src/gkr/layer.rs @@ -350,6 +350,8 @@ impl Layer { // // The final batched main-sumcheck polynomial is formed as: // p(x) = Σ_g sel_g(x) * (Σ_i α^{offset(g,i)} * (expr_{g,i}(x) - eval_{g,i})). + // `offset(g,i)` is the global challenge-power index of the i-th expression in group g, + // i.e. the contiguous position after flattening all groups in `expr_evals` order. // Here, `expr_evals` below constructs (g -> {(expr_{g,i}, eval_{g,i})_i}) for all groups. let rotation_expr_len = cb.cs.rotations.len() * ROTATION_OPENING_COUNT;