zorch.sumcheck.univariate_skip¶
Univariate skip (Gruen's trick, SWIRL-generalized): collapse the first
skip_rounds sumcheck rounds into ONE univariate round over a multiplicative subgroup
D of order 2^skip_rounds.
The standard multilinear sumcheck over H_{skip_rounds+n} runs skip_rounds+n rounds, each binding one boolean variable in the extension field. The skip instead runs the sumcheck over the hyperprism D × H_n: round 0 is a single univariate round over D — the boolean prefix {0,1}^skip_rounds is identified index-for-index with the |D| subgroup points, so each factor's prefix values are the values of a degree-<|D| univariate on D. Round 0 sends s₀(Z) = Σ_{x∈H_n} combine(factors)(Z, x) in ascending-coefficient form; the verifier checks c == Σ_{z∈D} s₀(z) (domain.subgroup_sum), samples r₀ ∈ F_ext, and the claim reduces to s₀(r₀). Round and challenge count drop from skip_rounds+n to 1+n.
Round 0 is base-field work: the factors are base-field, and their D-coefficients come
out of an iNTT (domain.subgroup_to_coeffs), the low-degree extension onto the superset
the degree-degree·(|D|−1) message needs comes out of an NTT (domain.subgroup_evals),
and s₀'s coefficients come out of a final iNTT — extension arithmetic starts only once
r₀ is bound at round 1. The 1..n tail is the ordinary eval-form sumcheck over the bound
extension-field state: the very same StandardRound, given the shared challenge
policy it folds under (verifier dual verifier.SumcheckRound); any engine built on
StandardRound (e.g. sqrt_space.prove_sqrt_space) serves as the tail too.
skip_rounds == 0 is a strict opt-in off switch: it delegates to the plain
StandardRound run (verifier dual verifier.SumcheckRound), byte-identical to a sumcheck
that never knew about the feature.
UnivariateSkipProof
dataclass
¶
The distinct subgroup message followed by ordinary sumcheck messages.
Source code in zorch/sumcheck/univariate_skip.py
226 227 228 229 230 231 | |
PrismEvaluationClaim
dataclass
¶
Claim reduced at one prism-prefix coordinate and the remaining MLE point.
Source code in zorch/sumcheck/univariate_skip.py
234 235 236 237 238 239 | |
UnivariateSkipProver ¶
Bases: ProverStage[SumClaim, SumcheckWitness, PrismEvaluationClaim, UnivariateSkipProof]
Prove univariate-skip sumcheck with a prism evaluation result.
Source code in zorch/sumcheck/univariate_skip.py
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 | |
UnivariateSkipVerifier ¶
Bases: VerifierStage[SumClaim, PrismEvaluationClaim, UnivariateSkipProof]
Verify univariate-skip sumcheck.
Source code in zorch/sumcheck/univariate_skip.py
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 | |
round0_message ¶
round0_message(
p_initial: Array,
skip_rounds: int,
summand: SumcheckSummand,
) -> tuple[Array, Array]
The round-0 univariate message s₀ (ascending coefficients, degree
degree·(|D|−1)) and the factors' D-coefficients used to bind r₀.
Identify the skip_rounds most-significant boolean variables with the
|D| = 2^skip_rounds subgroup points (MSB-first, matching StandardRound's fold):
reshape each factor to (|D|, H_n) and iNTT the D axis to its degree-<|D|
Z-coefficients. s₀(Z) = Σ_{x∈H_n} combine(factors)(Z, x); its degree outgrows |D|,
so the factors are low-degree-extended onto the order-M superset (M the next power
of two past the degree) before combining, then s₀'s values there are iNTT'd back to
coefficients. All base-field.
Jitted (skip_rounds/summand static) so the NTTs + combine + Σ fuse into one
dispatch instead of a per-op eager chain.
Source code in zorch/sumcheck/univariate_skip.py
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | |
skip_round0 ¶
skip_round0(
p_initial: Array,
claim: RunningClaim,
skip_rounds: int,
transcript: Transcript,
summand: SumcheckSummand,
challenges: ChallengePolicy,
) -> tuple[FoldingClaim, Transcript, Array]
Round 0 of the univariate skip: emit s₀, observe it, sample r₀ ∈ F_ext, and bind
the prism at r₀. Returns the bound extension MLE state (m, H_n), the transcript, and
the round-0 message. The caller runs any n-round sumcheck tail over the state —
StandardRound (prove_univariate_skip) or a memory-optimized engine
(sqrt_space.prove_sqrt_space) — so the skip stacks with the other round-cost
levers.
Source code in zorch/sumcheck/univariate_skip.py
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | |
prove_univariate_skip ¶
prove_univariate_skip(
p_initial: Array,
claim: Array,
skip_rounds: int,
transcript: Transcript,
summand: SumcheckSummand | None = None,
*,
challenges: ChallengePolicy
) -> tuple[FoldingClaim, Transcript, list[Array]]
Prove the sumcheck with the first skip_rounds rounds collapsed into one
univariate round over the order-2^skip_rounds subgroup. summand defaults to the
product over the factors; challenges configures the subgroup round and every
tail round together. Returns the final folded factors
(m, 1) beside the reduced claim, the transcript, and all 1+n round messages
(the round-0 coefficient message first). Each round reduces the claim as it
folds, so the caller gets the reduced claim without a second pass.
skip_rounds == 0 delegates to the plain StandardRound run — byte-identical to a
sumcheck without the skip.
Source code in zorch/sumcheck/univariate_skip.py
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | |
verify_univariate_skip ¶
verify_univariate_skip(
claim: Array,
msgs: list[Array],
skip_rounds: int,
total: int,
transcript: Transcript,
degree: int,
*,
challenges: ChallengePolicy
) -> tuple[Array, Transcript, Array, Array]
Replay the skip prover: the subgroup round-0 check then the coefficient tail,
threading the claim and ANDing every round's ok. Returns the reduced final claim
(which a consumer checks equals combine(factors)(point)), the transcript, the bound
point [r₀, …, r_n], and ok. skip_rounds == 0 replays the plain StandardRound run
(SumcheckRound), the exact dual of the prover's off-switch.
Source code in zorch/sumcheck/univariate_skip.py
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | |