Twist and Shout
🚧 These docs are under construction 🚧
👷If you are urgently interested in this specific page, open a Github issue and we'll try to expedite it.👷
One-hot polynomials
A one-hot vector of length is a unit vector in , i.e., a vector of length with exactly one entry equal to and all other entries equal to .
In Jolt, we refer to the multilinear extension (MLE) of a one-hot vector as a one-hot polynomial.
We also use the same terminology to refer to the concatenation of different one-hot vectors. For example, if where each is one-hot, then we'll call one-hot, and say the same about its MLE.
Shout
Shout is a batch-evaluation argument. Let be any function, and suppose the prover has already committed to inputs to . A batch-evaluation argument gives the verifier query access to the MLE of the output vector .
From the verifier's perspective, a batch-evaluation argument is an efficient reduction from the difficult task of evaluating the MLE of the output vector at a random point, to the easier task of evaluating the MLE of the input vector at a random point.
In Shout, a wrinkle is that each input to is committed in one-hot form (i.e., as a unit vector in ) rather than as a binary vector in . For example, if , , and , then in Shout will be committed as the length-four unit vector while will be committed as .
Let denote the evaluation table of , i.e., is the vector of length whose 'th entry stores . Let denote the matrix whose 'th row is the one-hot representation of vector .
Then is simply the inner product of row of with . This can be seen to imply that for any ,
All that Shout does is apply the sum-check protocol to compute the above sum. From the verifier's perspective, this reduces the task of computing to that of evaluation at a random point and evaluation at a random point. Since the vector was committed by the prover, the prover can provide the requested evaluation of along with an evaluation proof.
In the case where committing to, or providing an evaluation proof for, is too expensive, Shout instead virtualizes . This means itself is not committed. Rather, several smaller vectors are committed, and when the verifier needs to evaluate at a random point, the sum-check protocol is applied to reduce evaluating (the MLE of) at a point to evaluating (the MLEs of) the simpler vectors at a different point.
In Shout, this virtualization uses the fact that, since (each of the rows of) is one-hot and of length , (each row of) can be expressed as the tensor product of smaller one-hot vectors, each of length . Rather than committing to directly, the prover commits to the smaller vectors, and then we use an additional application of the sum-check protocol to reduce the task of evaluating at a point to the task of evaluation each of the smaller vectors at a point.
Prefix-suffix Shout
For the Shout verifier to be fast, all that is needed is that be quickly evaluable. This is the case for all of the primitive RISC-V instructions (and many other functions). However, for the Shout prover to be fast, needs additional structure. We call the kind of structure exploited to make the Jolt prover fast prefix-suffix structure. Prefix-suffix structure is spiritually similar to the notion of decomposable tables in Lasso: roughly, it captures the situation where a single evaluation of at input can be obtained by splitting into chunks each of size , evaluating a simple function on each chunk, and putting the evaluations together in a simple way. Whereas the Lasso prover had to explicitly commit to the chunks, the Shout prover only uses the decomposition ``in its own head'' to compute its sum-check messages quickly. This is one reason the Shout protocol is much more efficient than the Lasso protocol: table decompositions are not baked into the protocol itself but rather used only by the prover to quickly compute sum-check messages. The protocol itself is agnostic to the decomposition used (and in particular does not force the prover to use a particular decomposition).
For details, see Appendix A of [NTZ25].
Twist
Shout is a batch-evaluation argument, which can equivalently be viewed as a lookup argument, i.e., for performing reads into a read-only memory. The relevant read-only memory for Shout is the memory that stores all $2^n$ evaluations of the function $f$ being evaluated.
Twist is an extension from read-only memory to read-write memory.
Imagine the prover has already committed to $T$ read addresses and $T$ write addresses, each address indexing into a memory of size $K$. The prover has also committed to a \emph{value} associated with each write operation. We think of reads and writes as proceeding in ``cycles'' (analogous to CPU cycle), where in each cycle , the 'th read operation happens, followed by the 'th write operation.
The goal of Twist is to give the verifier query access to (the MLE of) the vector of read-values, where denotes the value returned by the 'th read operation (i.e., the value that, as of time , was most recently written to the 'th read address).
In Twist, as in Shout, each address is specified in one-hot form. So we think of the read addresses as a matrix whose 'th row is the one-hot representation of the address read at cycle , and similarly for write address .
Whereas Shout involves a single invocation of the sum-check protocol (plus an additional invocation if is virtualized in terms of smaller vectors), Twist involves several invocations of the sum-check protocol. We call these the read-checking sum-check, the write-checking sum-check, and the Val-evaluation sum-check.
Prior to the start of these three invocations of the sum-check protocol, the Twist prover commits to a vector called the Increments vector, or for short. If is the cell written at cycle , then returns the difference between the value written in cycle and the stored at cell at the start of the cycle. The next section explains that once is committed, there is actually no need to commit to the write values : the 'th write value is in fact implied by the associated increment, so there is no need to commit to both.
wv virtualization
In the Twist and Shout paper (Figure 9), the read and write checking sumchecks of Twist are presented as follows:
Observe that the write checking sumcheck is presented as a way to confirm that an evaluation of is correct.
Under this formulation, both and the polynomial are committed.
With a slight tweak, we can avoid committing to . First, we will modify to be a polynomial over just the cycle variables, i.e. . Intuitively, the th coefficient of is the delta for the memory cell accessed at cycle (agnostic to which cell was accessed).
Second, we will reformulate the write checking sumcheck to more closely match the read checking sumcheck:
Intuitively, the write value at cycle is equal to whatever was already in that register (), plus the increment ().
We also need to tweak the -evaluation sumcheck to accommodate the modification . As presented in the paper:
After modifying to be a polynomial over just the cycle variables: