zorch.logup_gkr.jagged_prover¶
Jagged LogUp-GKR prover: the materialized per-layer sumcheck.
The jagged sibling of prover.GkrLayerRound. A JaggedGkrLayer materializes
only sum(row_counts) of its virtual 2^(niv+nrv) positions; every
non-materialized position holds the fold-neutral fraction (n=0, d=1), whose
LogUp summand eq * (lam*(n0*d1 + n1*d0) + d0*d1) collapses to just its eq
weight. The sumcheck therefore runs over the materialized arrays and adds the
virtual mass back in closed form: the eq weights of a full hypercube sum to
the product of the bound variables' eq factors (pad_adj), so the
correction per round is pad_adj - eq_sum_materialized.
Round polynomials travel in coefficient form, interpolated through
{0, 1, 1/2, b}: the summand carries the current variable's eq factor, whose
root b = (1-z)/(1-2z) is known to both sides, so a degree-3 round needs
only the materialized evaluations at {0, 1/2} plus s(1) = claim - s(0)
(Gruen, https://eprint.iacr.org/2024/108). Value-form on the natural domain
would need a third materialized evaluation per round.
Variables bind LSB-first (consecutive-pair fold): a jagged layer is
batch-major, so the row LSB is the in-segment pair dimension and the
stride-2 fold never crosses a segment boundary once odd segments are
re-padded (the same in-trace gather derivation as the circuit transition).
Row variables fold first while their eq factor rides as the materialized
eq_row lookup; once rows are exhausted the accumulated row-eq residual
becomes the scalar eq_adj and the batch variables fold densely. The
bound point is challenges reversed -- LSB-first binding makes the last
challenge the MSB -- so the carry convention (MSB-first point, child selector
appended last) matches the dense chain's.
Per-round shapes shrink and the gather layout changes round to round, so the
driver is a host-orchestrated Python loop over plain numeric bodies, not the
homogeneous zorch.sumcheck scan (see docs/reference/conventions.md).
JaggedLayerProof
dataclass
¶
One jagged GKR layer's sumcheck transcript: the batching challenge and opening claim the layer entered with (the per-layer anchors a consumer diffs first when a byte-match diverges mid-pyramid), the coefficient-form round polynomials, the bound point, and the final pair openings.
A pytree (every field is an Array, like the dense sumcheck.RoundMsg) so
it can be returned across a frx.jit boundary -- the per-layer jit the
chained prover wraps each round in.
point is retained for wire serialization despite being replay-derivable
— LayerProof.point carries the rationale and the
verifier-must-never-read rule.
Source code in zorch/logup_gkr/jagged_prover.py
86 87 88 89 90 91 92 93 94 95 96 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 | |
JaggedGkrLayerRound ¶
Bases: ProverRound
Prove one jagged GKR layer; the chain of these (floor outward) is the
jagged GKR prover, threading the same (num_eval, den_eval, eval_point)
carry as the dense chain. One ChallengePolicy configures lam, every
per-variable fold, and the child selector; prover.bind_output consumes
that same policy at the chain head.
The per-layer prove dispatches through the module-level _jagged_round_zone
(one executable per layer): the schedule rides as a traced operand and the
planes arrive already cap-width, so the whole-layer trace keys on the cap
shape -- one compile per nrv class, reused across every layer, pass, AND
shard. The round holds only its layer (no per-instance jit, no self-closure),
so the chain frees each round -- and its layer -- the moment it builds the
next. The pyramid stays a host-orchestrated Python loop of these (one trace
per layer shape, never one jit over the whole pyramid -- it does not fit at
scale; see prover.LogupSumcheckRound).
Source code in zorch/logup_gkr/jagged_prover.py
656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 | |
prove_jagged_layer ¶
prove_jagged_layer(
layer: JaggedGkrLayer,
lam: Array,
claim: Array,
eval_point: Array,
transcript: Transcript,
*,
challenges: ChallengePolicy,
caps: RoundWidthCaps | None = None
) -> tuple[Array, Transcript, JaggedLayerProof]
Prove one jagged GKR layer's materialized sumcheck from an explicit
lam / claim (no inter-layer carry). The standalone single-layer seam
the layer tests drive; the pyramid runs JaggedGkrLayerRound, which
brackets this same core (_prove_jagged_layer_from_ops) with the carry.
eval_point is MSB-first over (batch || row) variables; its length
fixes the virtual row depth nrv = len(eval_point) - niv, which may
exceed what the materialized row counts need -- the extra rounds fold
saturated all-ones segments against re-padded neutral rows, exactly the
virtual positions' values. Returns the bound point (MSB-first, i.e. the
challenges reversed), the advanced transcript, and the proof.
caps is the fixed-width, size-invariant round layout, and is
mandatory: every round runs at one static operand shape per phase, live
prefix tracked by the rounds' live operand, so one compiled round
kernel serves every round -- and every layer and input proved under the
same caps. Row counts are traced, so there is no exact-layout fallback
with static per-round output widths; the zero-slack layout is the capacity
layout whose caps happen to be tight.
The device-derived schedule: the per-round re-pad schedule is a
pure function of row_counts + the round index and derives inside the
claimed kernels, so the loop carries only the tiny i32[nseg] row_counts
operand plus per-round i32[3] live triples -- both ride as traced operands,
so row_counts never enters the jit key. The virtual-row-space fit
(max(row_counts) <= 2^nrv) is the consumer's capacity-class obligation
-- a host check cannot read the traced counts.
Source code in zorch/logup_gkr/jagged_prover.py
125 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 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | |