zorch.pcs.ring_switch¶
Ring-switching reduction (DP24 §1.3) for binary-field PCS packing.
A prover over GF(2) stores its witness as raw bits but commits the packed
form: W bits per big-field element, so the PCS sees a multilinear with
log2(W) fewer variables. An IOP claim ẑ(point) = v about the bit-coefficient
multilinear ẑ therefore cannot be opened directly — packing lays bits out as
big-field coordinates, not evaluations. Ring switching bridges the gap with
one wire vector and one challenge:
s_hat_v[r] = Σ_i bit_r(witness[i]) · tensor[i]— theWbit-slice partial evaluations ofẑat the claim point's suffix (tensoris its eq tensor). The only ring-switch data on the wire; the verifier checks the incoming claim against it with its own prefix weights (a consumer concern — see "what stays outside" below).-
After observing
s_hat_v, the verifier samplesr'' ∈ F^{log2(W)}. Collapsing the bit axis witheq(r'')turns the claim intoΣ_i packed_witness[i] · rs_eq_ind[i] = ⟨transpose(s_hat_v), eq(r'')⟩whose left side is a transparent-weighted sum over the packed multilinear — exactly the shape a Basefold-style PCS opens — and whose right side the verifier computes from the wire alone.
Both readings are the two axis-orders of one tensor-algebra element
Σ_i tensor[i] ⊗ witness[i] ∈ F ⊗_{GF(2)} F (a W×W bit matrix); converting
between them is [tensor_algebra_transpose], a pure bit transpose. Soundness
rides on r'' being sampled after s_hat_v is observed — see the contract
below.
The GF(2)-basis is the dtype's storage-bit basis¶
bit_r and the packing must decompose over the same GF(2)-basis of the field.
This module uses the dtype's little-endian storage bits (lane 0's bit 0 is
r = 0), which is the packing convention of a bitcast — so a consumer that
packs its bit witness by reinterpreting the bit buffer as field elements agrees
with these kernels by construction, in any representation (tower or GHASH
alike; the reduction is basis-blind, only claim-check weights are not).
Transcript-free: the caller owns Fiat-Shamir order¶
These are pure functions; nothing here observes or samples. The caller must
enforce, per claim: observe s_hat_v → then sample r'' (and, when batching
claims, sample the combination scalars only after every claim's s_hat_v is
observed — the reduction is linear, so batching is the caller scaling each
rs_eq_ind by its scalar). This keeps the block agnostic to the host's
transcript (a byte-duplex, a device sponge, or a native Transcript).
What stays outside¶
- Prefix claim-check weights: the incoming claim's prefix may live in a univariate-skip domain whose Lagrange weights are scheme-specific.
- Serialization of
s_hat_vand the transcript labels. - The PCS that opens the reduced claim (
pcs/basefold,pcs/ligerito, …).
The verifier never materializes rs_eq_ind (length 2^ℓ): [eval_rs_eq]
evaluates its MLE at the PCS's final point in O(ℓ·W) field multiplies plus
O(ℓ) bit transposes, polylog in the witness (DP24 §1.3, Figure 3).
RingSwitch
dataclass
¶
One reduced claim: observe s_hat_v, then let the PCS prove
Σ_i packed_witness[i] · rs_eq_ind[i] = claim. A registered pytree so a
consumer's jitted open path can return it across the jit boundary.
Source code in zorch/pcs/ring_switch.py
124 125 126 127 128 129 130 131 132 133 134 135 136 137 | |
bit_slice_evals ¶
bit_slice_evals(
packed_witness: Array, tensor: Array
) -> Array
s_hat_v[r] = Σ_i bit_r(packed_witness[i]) · tensor[i] for r ∈ [0, W).
(n,) × (n,) -> (W,). The memory-bounded bit-select reduction accumulates
directly into the output without materializing its (n, W, L) broadcast.
Batches over claims that share the witness: a selectors-major tensor of
shape (n, N) gives (N, W), row k the slice-evals against tensor[:, k]
— the packed witness is read once for all N. A batched ring-switch open
stacks its N suffix tensors this way to fold N witness passes into one.
Source code in zorch/pcs/ring_switch.py
75 76 77 78 79 80 81 82 83 84 85 86 87 | |
rs_eq_ind ¶
rs_eq_ind(tensor: Array, eq_r_dprime: Array) -> Array
rs_eq_ind[i] = Σ_b bit_b(tensor[i]) · eq_r_dprime[b] — the transparent
weight vector of the reduced claim (the same select-XOR kernel as
[bit_slice_evals] with the bit-decomposed side swapped).
(n,) × (W,) -> (n,).
Source code in zorch/pcs/ring_switch.py
90 91 92 93 94 95 96 97 98 | |
tensor_algebra_transpose ¶
tensor_algebra_transpose(v: Array) -> Array
The W×W bit transpose between the two readings of a tensor-algebra
element: bit_b(out[h]) = bit_h(v[b]). (W,) -> (W,); an involution.
Source code in zorch/pcs/ring_switch.py
101 102 103 104 105 106 107 108 109 110 111 112 113 114 | |
inner_product ¶
inner_product(a: Array, b: Array) -> Array
Σ_i a[i]·b[i] — field multiplies then a native field-additive reduce.
(n,) × (n,) -> ().
Source code in zorch/pcs/ring_switch.py
117 118 119 120 121 | |
reduce_bit_claim ¶
reduce_bit_claim(
s_hat_v: Array, suffix_tensor: Array, eq_r_dprime: Array
) -> RingSwitch
The prover-side reduction for one claim.
s_hat_v is the wire message from [bit_slice_evals] — taken as input, not
recomputed from the witness, so a caller cannot assemble the reduction
without first holding the message it must observe (and the dominant kernel
runs once). suffix_tensor is the eq tensor of the claim point's suffix
(the coordinates that index packed elements), eq_r_dprime the eq tensor of
the post-observe challenge r'' (length W). Sampling order stays the
caller's — see the module docstring.
Source code in zorch/pcs/ring_switch.py
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | |
eval_rs_eq ¶
eval_rs_eq(
z_vals: Array, query: Array, eq_r_dprime: Array
) -> Array
MLE(rs_eq_ind)(query) in O(ℓ·W) multiplies — the succinct verifier's
replacement for materializing rs_eq_ind ([DP24] §1.3, Figure 3).
z_vals are the claim-point suffix coordinates that built suffix_tensor,
query the PCS's final challenge point (same length and variable order).
Walks one tensor-algebra element E: in characteristic 2,
eq(z, q) = 1 + z + q, so each step is E += z·E (vertical) + q·E
(horizontal); the final vertical fold against eq(r'') is the same
transpose + inner product the prover-side claim uses.
Source code in zorch/pcs/ring_switch.py
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | |