Shifting
As we've already seen, we can think of multilinears as lists. Often, it's convenient to specify a virtual polynomial on the list side of things, and then to later recapture that operation on the algebraic side.
Describing how shifting operates on lists is not hard. Roughly, if we start with a list , we want to circularly rotate that thing downwards by steps, let's say. So the resulting list will be .
Actually, it will be convenient to refine this a bit. We are going to introduce a further block size parameter . For such and for some offset parameter , the task will be to
- chunk the list into size- blocks;
- circularly rotate each invdividual block (!) downward by steps.
If our initial multilinear is , we will write for the above thing.
Shifting Algebraically
Now, this is a perfectly valid description of something to do with a list. The much-harder question is: will the verifier have a way of evaluating efficiently? That is, let's assume that the verifier has the ability to efficiently evaluate for any given point (for example, is simply committed). Can we devise a means by which the verifier might bootstrap -evaluation into -evaluation? That is, how might the verifier efficiently ascertain , given the ability to evaluate the underlying polynomial ?
Note here that we are already using the duality between lists and multilinears. At first, when we defined , we did so as a list. Just above though, we're already understanding it as a multilinear polynomial. Indeed, how else would we make sense of evaluating it at a random (i.e., which isn't necessarily in the cube)?
So the first thing we need to do is write down an algebraic expression for . In principle, this isn't hard, since we already know its values on the cube (we can just take its MLE). The problem though, will be to do this in such a way that the verifier may evaluate it efficiently.
The Shift Indicator
We'll begin with a slight detour (which is really not one at all!). As above, we fix a height parameter , a block size parameter , and an offset . As usual, we are going to identify with by sending .
We're going to define the shift indicator function , beginning as a function on . For each pair , we are going to declare that equals 1 if and only if holds, and equals otherwise. That is, this thing returns 1 on those pairs of cube-points which differ by steps (with wraparound) in the lexicographic ordering of the cube . This was just a function, but as usual, we can implicitly associate it with its MLE, a -variate multilinear (whose restriction to the cube recovers ).
Now the key claim is that is transparent: the verifier can locally evaluate it on any pair of points and in just work. This claim is not obvious at all, and in [DP23, § 4.3] we expend a good amount of work proving it.
The Shift Polynomial
Assuming this fact for now, we get a new way of writing . Indeed, equals:
What is the intuition of this? First of all, this thing is evidently multilinear, provided that we believe that is multilinear. Thus we need to argue that its restriction to the cube equals exactly . To see this, we pick a cube element and study the value of the above expression, with specialized to . As the outer sum index varies, the multiplier will be nonzero precisely when holds (here, we implicitly truncate and by only considering their lowest bits). Thus, the sum above will pick out "exactly one" value of . Which? The value of will be , where the bitstring is chosen to be the -step lexicographic predecessor of . This is exactly what we wanted.
Efficient Evaluation
What about the problem we started with—namely, for the verifier to learn efficiently? The key is that if we take the above expression—but specialize to —then we wend up with the expression:
This is exactly the thing we can sumcheck: indeed, it's enough to sumcheck the -variate polynomial defined by:
This polynomial's sum over the cube will be nothing other than our desired evaluation . At the very end of the sumcheck, the verifier will be reduced to evaluating two things: , on the one hand, and , on the other (here, the point will be sampled during the sumcheck). The first thing the verifier can just query. As for the second, we already agreed above that is transparent! Thus the verifier can just efficiently evaluate it itself.
This completes the description of as a virtual polynomial—i.e., as a thing that the verifier can efficiently evaluate at any point .
Then What?
What do we get out of this construction? In short, shifting lets us link distinct rows of our tables. Occasionally, it happens that we want to "spread" a computation across multiple rows. When we do this, we often want to "link" the end of one row with the beginning of the next. In fact, this sort of looks like how classical AIR works.
Of course, we will not always want to do this, and our use of M3 vastly reduces the frequency that we want to use this kind of thing. On the other hand, it can be useful to have.
Copy Constraints
We note that shifting achieves a goal analogous to that achieved by copy constraints, which appear in PLONKish schemes. In those schemes, copy constraints allow the verifier to require that various arbitrary pairs of elements of the prover's trace table be equal to each other. In particular, these pairs can control cells in completely arbitrary locations (in a completely unstructured way—the linked cells don't need to be near each other, for example). In fact, just this sort of copy constraint is achieved by the protocol [DP23, Prot. 4.22].
Because copy constraints are unstructured, they are very powerful; on the other hand, this power comes at an efficiency cost. Copy-constraint protocols like [DP23, Prot. 4.22] require instance-dependent transparent preprocessing; moreover, at runtime, they reduce to expensive multiset-checks. On the other hand, in many cases, this power is not needed: we only end up needing to link cells in very structured and predictable ways (e.g., linking the end of each row to the beginning of the following row).
Our shift construction, then, achieves the effective outcome of copy constraints, but at a far-lower efficiency cost. Indeed, the cost is just a single -round sumcheck (or even fewer rounds, if the shifting is blockwise!).