Simple Example
This document explains the fundamentals of Polynomial Identity Language with the help of a simple multiplier program. PIL was designed with the aim to simplify
This document explains the fundamentals of Polynomial Identity Language with the help of a simple multiplier program.
PIL was designed with the aim to simplify the proving and verification of execution correctness. Given that PIL is geared towards modularity, each PIL code has to stipulate a unique identifier for each program. It therefore has an effective syntax that is easy to learn.
In the nutshell, a typical PIL code states the program identifier, the parameters used in the program's computations as well as constraints these parameters must satisfy.
Key features
A PIL code starts with the program's which is a reserved keyword used to identify the program being executed and to frame the scope of the program definition.
The has to be instantiated with a unique name together with an argument representing the of the program, which is the maximum number of rows in any execution trace of the program.
In a PIL code, one should define the used by its program and the among the defined polynomials. Polynomials are identified by the keyword .
As opposed to polynomials which are preprocessed for a given program, polynomials are allowed to change from one execution to the next. Constant polynomials are considered public as they are known by all parties, while committed polynomials are in most cases, only known by one party (usually the proving party).
The keyword allows the compiler to identify the corresponding polynomial as committed.
Multiplier program in PIL
Let us create a simple PIL program that models the computation of the product of two integers. Consider a program that, at each step, takes two input numbers and multiplies them.
Such a program is commonly referred to as the program, and it can be modelled by using 3 polynomials;
where and are "free" inputs and is the output. The term “free" refers to the fact the values are arbitrarily chosen and they do not strictly depend on any previously computed values.
The corresponding execution trace would be correct if the values in the output column satisfy the following identity:
See the execution trace of the Multiplier program in the table below:
Since the above identity, labelled , is satisfied in each of the rows of the execution trace, it means that the output column is filled with correct values.
In the language of proof/verification systems concerned with proving and verifying the correctness of the execution trace, the two inputs and together with the output are referred to as .
So, the values in each column of the execution trace actually represents or describes a particular polynomial. Such polynomials can be computed by .
The PIL code for the above Multiplier program is as follows.
namespace Multiplier(2**10);
// Polynomials
pol commit freeIn1;
pol commit freeIn2;
pol commit out;
// Constraints
out = freeIn1*freeIn2;In the above figure, the namespace given to the Multiplier program is , and its specified length is .
In the zkEVM context, these polynomials would be committed by the Main state machine for verification, they appear in the PIL code as pol commit.
Optimized Multiplier program
The above design of the Multiplier program, as represented by its execution trace, does not scale easily to more complex operations. The number of polynomials (or number of columns) grows linearly with the number of operations that needs to be performed.
For example, if we were to design a Multiplier program that computes operation, the above design would require committed polynomials, which is far from being practical.
Here's a more practical design, which reduces the committed polynomials to only 3 polynomials;
-
The polynomial which records, as a column in the execution trace, each input one at a time.
-
The polynomial (column) which flags the starting row of each operation by evaluating to in each odd row and otherwise.
-
The polynomial which holds the result of the operation as before.
See the below table for the corresponding execution trace:
Observe how each column of the execution trace records the "state" in each row.
-
: The column records the first input of the operation, hence reflects a , while records as its default initial value.
-
: The column records the second input of the operation, reflects a because the operation has started in the previous row, and now records the value which is the first input to the operation.
-
: The column now records the first input of the second operation (for the sake of simplicity, the same multiplication operation is used again), reflects a because a fresh operation has begun in this row, and then records the output value of the operation that started in .
-
: Similarly, the column records the second input of the operation, reflects a to indicate the current operation has started in the previous row, and then records the value which is the first input to the current operation.
The same pattern is followed in the subsequent rows of the execution trace, for the three columns; , and .
Constraints
In order to express the values of the polynomial in terms of the values of and per row, we observe the following;
-
Whenever equals (i.e., in every ), the next value of the polynomial, denoted by , equals the value of current value. That is,
-
Whenever equals (i.e., in every ), the next value is the product of the current and the previous values of . That is,
which is the value of the polynomial in .
But, whenever equals (for every ), we have . Hence can be rewritten as,
Or equivalently, for every , the next output value can be expressed as:
Putting and together yields the following constraint:
Therefore, the PIL code for an optimized Multiplier SM can be written as follows,
namespace Multiplier(2**10); // Constant Polynomials pol constant RESET; // Committed Polynomials pol commit freeIn; pol commit out; // Constraints out' = RESET*freeIn + (1-RESET)*(out*freeIn);Observe that the polynomial is because it does not change from one execution to the next. In an actual implementation of the Multiplier program, would be among the preprocessed polynomials.
Last updated on
Public Values
Public Values refers to values of committed polynomials that are known to both the prover and the verifier, as part of the arithmetization process. Public Value
Index
Ethereum is a state machine that transitions from an old state to a new state by reading a series of transactions. It is a natural choice, in order to interpret