Lab: Layouter -> Gates

Let's keep building up our tutorial chip. We defined the number of wires we wanted in TutorialConfig and then we wrapped this in a struct with a marker in order to get TutorialChip. In a sense what we want to define is an API that contains some gates on the wire values.

You are given the tutorial composer below

Task 1

Define a new gate and add it to the composer trait. This gate should conform to the chip's config that we created, and it thus should only work with the columns present in the config. In other words, your new gate should take wire values based on variables in the equation we are working under. (this change can be as dumb as you like, it's just to spend a bit of time doing something that reinforces understanding of where things are being created, myelinating those neural pathways)

Task 2

Of course we need to implement the composer trait on our chip. This gets very complicated, so we will give you an example of how it can be done and ask you to do it for the others functions.

fn raw_multiply<FM>(
        &self,
        layouter: &mut impl Layouter<F>,
        mut f: FM,
    ) -> Result<(Cell, Cell, Cell), Error>
    where
        FM: FnMut() -> Value<(Assigned<F>, Assigned<F>, Assigned<F>)>,
    {
        layouter.assign_region(
            || "mul",
            |mut region| {
                let mut values = None;
                let lhs = region.assign_advice(
                    || "lhs",
                    self.config.l,
                    0,
                    || {
                        values = Some(f());
                        values.unwrap().map(|v| v.0)
                    },
                )?;
                let rhs = region.assign_advice(
                    || "rhs",
                    self.config.r,
                    0,
                    || values.unwrap().map(|v| v.1),
                )?;

                let out = region.assign_advice(
                    || "out",
                    self.config.o,
                    0,
                    || values.unwrap().map(|v| v.2),
                )?;

                region.assign_fixed(|| "m", self.config.sm, 0, || Value::known(F::one()))?;
                region.assign_fixed(|| "o", self.config.so, 0, || Value::known(F::one()))?;

                Ok((lhs.cell(), rhs.cell(), out.cell()))
            },
        )
    }rus

Notice that everything is done in the assign_region function. The mutable function that goes into that function is also where most of the work happens. Also we know this image can look a bit scary at first, but there are certain 'types' of things happening that should be very digestable, and soon should become straightforward. In fact in the above image there are sort of only 2 types of things happening.

As you study this code block you may notice that the first time we call assign_advice, the value we assign to column a is derived from the function f, which we haven't looked at yet. This will happen later. Unfortunately because of the complexity of this system there isn't always a simple way to explain things, so sometimes we have to hope you will forgive us for asking you to assume the existence of something we haven't explained. Though we did show you an example of such a function on the previous page, we present it again here for your convenience.

What you always know about the function f is that it outputs three field values, and you can sort of assume those values to represent (l,r,o) for now.

Aside from assigning the advice wire values, we also assign the fixed wire values based on the functionality we want in the gate, i.e. which parts of our equations we want to turn on or off on this row of the table created by the columns.

The actual task

Can you implement the other functions? raw_add should be straightforward, copy will look very different to the addition or multiplication gates and can in fact be done in a couple lines using constrain_equal implemented on Region.

expose_public can be done in one line similar to copy, using constrain_instance implemented on layouter without neding to do things within the assign region of layouter (because we are only adding a value to the PI column).

Your own gate will depend on how you defined it.

Last updated