zorch.sumcheck.prover¶
Sumcheck prover rounds: the product StandardRound, its compressed-wire
sibling CompressedProductRound, and the summand seam they read.
A sumcheck round splits each MLE on the current variable, sends the round
polynomial sampled over an EvalDomain, then folds every MLE at the verifier's
challenge (P0 + r*(P1 - P0)). Split and fold are summand-independent, and the
round poly is built by zorch.sumcheck.domain.summand_evals over the stacked
(m, N) state -- generic over BOTH the summand (SumcheckSummand._combine:
product, LogUp, ...) and the sampling domain. StandardRound (here) is the plain
materialized round: it holds the full factor table and does split -> sample ->
combine -> sum, the linear-time reference the memory-optimized siblings
(sqrt_space.SqrtSpaceRound, eq.SmallValueRound) specialize. Its summand
defaults to the product (ProductSummand) and its domain to the natural
{0..degree} evals.
The dense round binds MSB-first (domain.fold); the jagged engines bind
LSB-first (domain.fold(..., msb=False)). The split/fold primitives and the
round-poly builder (summand_evals) live in zorch.sumcheck.domain; the
verifier dual in zorch.sumcheck.verifier.
Rounds run under the scheme-agnostic zorch.prove.fold_rounds host loop (any
Round, any message shape) -- one round per variable, folding the state down
each step. SumcheckSummand (degree + _combine) is the summand seam the
round-poly builder reads, so the product ProductSummand and the LogUp
logup_gkr.prover.LogupSummand drive it interchangeably.
FoldingClaim
dataclass
¶
What a sumcheck round threads: the engine's folding state and the claim.
state is whatever the engine folds — stacked factor tables, or the √-space
engine's deferred (factors, eq) pair. The claim rides beside it because a
round's challenge is derived rather than received, and the point and running
value it builds live nowhere else.
Source code in zorch/sumcheck/prover.py
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | |
advance ¶
advance(
state: Any, reduced: Array, challenge: Array
) -> FoldingClaim
The folded state beside the claim this round reduced it to.
Source code in zorch/sumcheck/prover.py
71 72 73 | |
ProductSummand
dataclass
¶
The product sumcheck summand s = Σ_x Πₖ Pₖ(x): the combine math alone, no
round machinery. The default summand of StandardRound; the eq / sqrt_space
engines hold one to weight their eq-product. Pairs with the LogUp
logup_gkr.prover.LogupSummand under the SumcheckSummand seam.
Source code in zorch/sumcheck/prover.py
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | |
combine_scalars ¶
combine_scalars() -> tuple[Array, ...]
No loop-invariant scalars: the product reads only its factors.
Source code in zorch/sumcheck/prover.py
110 111 112 | |
combine ¶
combine(scalars: Sequence[Array], *factors: Array) -> Array
Product Πₖ fₖ (the scalar-explicit seam; product takes no scalars).
Single source of the combine math: _combine and any marked path route
here, so they cannot drift.
Source code in zorch/sumcheck/prover.py
114 115 116 117 118 119 | |
StandardRound ¶
Bases: ProverRound[Any, Array, TranscriptT], Generic[TranscriptT]
The plain materialized sumcheck round: send the summand's round poly over
domain, sample the challenge, fold the stacked (m, N) state. Bound to a
SumcheckSummand (product by default) and an EvalDomain (the natural
{0..degree} evals when None). This is the linear-time reference the √-space /
eq engines specialize; driven by fold_rounds.
challenges names the field challenges are drawn in. Naming the
transcript's own field is the one-squeeze schedule; an extension policy lets
a tail whose earlier round bound in that extension continue folding in the
same field.
Source code in zorch/sumcheck/prover.py
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 173 174 | |
CompressedProductRound ¶
Bases: ProverRound[Any, Array, TranscriptT], Generic[TranscriptT]
Two-factor product round with the compressed coefficient wire: the message
is [c_0, c_2] — the degree-2 round polynomial's constant and leading
coefficients — and the linear coefficient stays off the wire (the verifier
dual, verifier.CompressedCoeffsSumcheckRound, reconstructs it from the
running claim via s(0) + s(1) = claim). Split and fold match
StandardRound(ProductSummand(2)) exactly (the MSB variable binds); only the
message form differs, so a scheme whose wire fixes this form swaps rounds
without touching the fold. c_2 = Σ (P1_f - P0_f)·(P1_b - P0_b) is the honest
leading coefficient in any characteristic; over char 2 it coincides with the
(P0 + P1) products some wire specs write it as.
Source code in zorch/sumcheck/prover.py
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 | |
RoundMsg
dataclass
¶
One per-variable sumcheck round's message: the round polynomial sent plus
the Fiat-Shamir challenge it induced — the round_polys form the proof and the
challenges the evaluation point. The challenge is re-derivable from round_poly,
so it never goes on the wire; it rides here only to spare the prover a transcript
replay. LogupSumcheckRound.__call__ emits one per round for the fold_rounds
driver. Registered as a pytree because a downstream lax.scan stacks it as its
output — a scan output leaf must be a valid JAX type.
Source code in zorch/sumcheck/prover.py
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | |
SumcheckSummand ¶
Bases: Protocol
The summand seam a per-variable round exposes: the round-poly degree, and
_combine — the summand over the lifted factors. The round-poly builder reads
only this, so one builder serves every sumcheck; ProductSummand (product) and
logup_gkr.prover.LogupSummand (LogUp) both satisfy it, and the host-loop
engines (sqrt_space / eq) pass a summand to summand_evals.
degree is a read-only property here so a frozen-dataclass field (product)
and a @property (LogUp) both match — a plain degree: int would demand a
settable attribute that neither provides.
combine is the scalar-explicit form of the summand and combine_scalars the
loop-invariant scalars it reads (LogUp's λ; empty for product), so they bind once
rather than per round.
Source code in zorch/sumcheck/prover.py
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 | |
initial_claim ¶
initial_claim(
state: Any, value: Array, rounds: int
) -> FoldingClaim
Start a fold from state with nothing yet bound into value.
Source code in zorch/sumcheck/prover.py
86 87 88 89 90 | |