Layouter -> Gates
Last updated
Last updated
Buckle up, things start to get a bit more complex now. In this section we will get to see some engineering tools that were built to let us implement theoretical computer science in practice.
The Layouter
trait works together with the Region
struct and RegionLayouter
trait to allow us to define the gate functionality that we want.
Layouter
's function assign_region(Layouter, name, assignment)
essentially allows us to define how each gate or property we want will work. The entry assignment
is a mutable function that does most of the work here, its output is used to help create the output of assign_region
itself.
Generally speaking, the mutable function that is assignment
will call a few other functions that we want to briefly introduce. Recall that we chose a main equation to define the functionality that we wanted to see, so the layouter is going to help us assign values correctly within this equation (i.e. by assigning values to the columns correctly) based on what gate we want to see.
For example, to assign a witness value (a value to one of the wires, not a selector), we generally use the assign_advice(Region, annotation, column, offset, to)
function implemented on the Region
struct. And this function in turn calls the assign_advice(RegionLayouter, annotation, column, offset, to)
function defined in the RegionLayouter
trait. What these function calls achieve is the insertion of a value in a column of type advice. Which column? Well that is determined by the column
entry in the assign_advice
function.
To assign a selector value, we will instead use the function assign_fixed()
, and the reasoning/structure here is essentially the same.
Those last few paragraphs were extremely dense. You may have to go over them again, but perhaps do that after reading the rest of this section. They are in a sense a summary of what is explained next.
Let's recap what we want to achieve in terms of functionality the multiplication gate that we want to build. What was our equation again?
We have figured out what values to insert in the columns representing the selector values, which leaves us with the advice columns (l,r,o
) and a public input column (of type instance). We'll probably need to insert the correct values for l,r
based on the computation we want to do, and then work out the correct value for o
, which in this case should be the product of the previous 2 values. s_c
should just be set to 0 in this gate because it is not relevant right now; it is to allow the addition of a constant (and would therefore be the same in all uses of a circuit). PI
should also be 0, it represents a public input which can be different for each proof made on the same circuit (for example, perhaps it could represent a public key).
We have now covered how we need to assign values to columns in order to represent our gates. But recall that the assign_region
doesn't actually take in values, and so where do we get the values to put into the columns?
Hold on here, I know things are getting quite complex but we are almost done.
So, in the composer trait TutorialComposer
, let's define the function raw_multiplication
and give it the inputs (I know we already saw this, but le't actually go over it a bit more now)
Note that since this mutable function takes no entries we will have to define how to combine our values through the mutable function f
in raw_multiply
. For example, we can do that as in the image below (i.e. when we call the raw_multiply
function we give it the wire values)
Okay, I know that was a lot. Let's try to put that understanding together with some code. And be happy in the knowledge that we have covered a lot of the complexity already - you'll be able to make your own circuits in no time! And don't be afraid to reread this stuff, it is dense and takes a bit of time.
How should we set our selectors in order to achieve multiplication of and ? Well, the multiplication value is selected by , so let's set that to one. What else? Well, we need to ensure the output comes out to be the multiplication value, so we should set to 1. That seems to be it for things we want on, so all other selector values should be set to 0 (this should happen by default).
Question: what happens if instead of setting to 1 we set it to 2 (or any other value)?
Wherever we are using layouter.assign_region
, in that code block we should have the values we want to put into the columns, say values . One way we can achieve this, as you will see in the examples, is to call this function inside a function that we are defining in the composer trait introduced in the previous section.
When we implement the above function, we will call layouter.assign_region
, and the function f
shown above as an entry to raw_multiplication
will be what we use to get the values that we want to put in the columns - notice that this function is of type FM
which returns three assigned cells, each with a field value.