Skip to content

Working with field elements #813

@parazyd

Description

@parazyd

Hi. I'm trying to get the following code to compile, but I'm getting into issues working with the Value API.

I'm not able to figure out what exactly has to be done in fn is_eq_with_output() when doing the delta invert.
It is likely that I'm using the API in a wrong way, different from what it should actually be. Could you please help me out?

use std::marker::PhantomData;

use halo2_proofs::{
    circuit::{AssignedCell, Chip, Layouter, Region},
    pasta::group::ff::WithSmallOrderMulGroup,
    plonk::{self, Advice, Column, ConstraintSystem, Expression, Selector},
    poly::Rotation,
};

const NUM_OF_UTILITY_ADVICE_COLUMNS: usize = 4;

#[derive(Clone, Debug)]
pub struct IsEqualConfig<F: WithSmallOrderMulGroup<3> + Ord> {
    s_is_eq: Selector,
    advices: [Column<Advice>; NUM_OF_UTILITY_ADVICE_COLUMNS],
    _marker: PhantomData<F>,
}

pub struct IsEqualChip<F: WithSmallOrderMulGroup<3> + Ord> {
    config: IsEqualConfig<F>,
    _marker: PhantomData<F>,
}

impl<F: WithSmallOrderMulGroup<3> + Ord> Chip<F> for IsEqualChip<F> {
    type Config = IsEqualConfig<F>;
    type Loaded = ();

    fn config(&self) -> &Self::Config {
        &self.config
    }

    fn loaded(&self) -> &Self::Loaded {
        &()
    }
}

impl<F: WithSmallOrderMulGroup<3> + Ord> IsEqualChip<F> {
    pub fn construct(
        config: <Self as Chip<F>>::Config,
        _loaded: <Self as Chip<F>>::Loaded,
    ) -> Self {
        Self { config, _marker: PhantomData }
    }

    pub fn configure(
        meta: &mut ConstraintSystem<F>,
        advices: [Column<Advice>; NUM_OF_UTILITY_ADVICE_COLUMNS],
    ) -> <Self as Chip<F>>::Config {
        let s_is_eq = meta.selector();

        meta.create_gate("is_eq", |meta| {
            let lhs = meta.query_advice(advices[0], Rotation::cur());
            let rhs = meta.query_advice(advices[1], Rotation::cur());
            let out = meta.query_advice(advices[2], Rotation::cur());
            let delta_invert = meta.query_advice(advices[3], Rotation::cur());
            let s_is_eq = meta.query_selector(s_is_eq);
            let one = Expression::Constant(F::ONE);

            vec![
                // out is 0 or 1
                s_is_eq.clone() * (out.clone() * (one.clone() - out.clone())),
                // if a != b then (a - b) * inverse(a - b) == 1 - out
                // if a == b then (a - b) * 1 == 1 - out
                s_is_eq.clone() *
                    ((lhs.clone() - rhs.clone()) * delta_invert.clone() + (out - one.clone())),
                // constrain delta_invert: (a - b) * inverse(a - b) must be 1 or 0
                s_is_eq * (lhs.clone() - rhs.clone()) * ((lhs - rhs) * delta_invert - one),
            ]
        });

        IsEqualConfig { s_is_eq, advices, _marker: PhantomData }
    }

    pub fn is_eq_with_output(
        &self,
        layouter: &mut impl Layouter<F>,
        a: AssignedCell<F, F>,
        b: AssignedCell<F, F>,
    ) -> Result<AssignedCell<F, F>, plonk::Error> {
        let config = self.config();

        let out = layouter.assign_region(
            || "is_eq",
            |mut region: Region<'_, F>| {
                config.s_is_eq.enable(&mut region, 0)?;

                a.copy_advice(|| "copy a", &mut region, config.advices[0], 0)?;
                b.copy_advice(|| "copy b", &mut region, config.advices[1], 0)?;

                region.assign_advice(
                    || "delta invert",
                    config.advices[3],
                    0,
                    || {
                        a.value().zip(b.value()).map(|(a_v, b_v)| {
                            if a_v == b_v {
                                F::ONE
                            } else {
                                let delta = a.value_field() - b.value_field();
                                delta.invert().evaluate()
                            }
                        })
                    },
                )?;

                let is_eq = a.value().zip(b.value()).map(
                    |(a_v, b_v)| {
                        if a_v == b_v {
                            F::ONE
                        } else {
                            F::ZERO
                        }
                    },
                );

                let cell = region.assign_advice(|| "is_eq", config.advices[2], 0, || is_eq)?;
                Ok(cell)
            },
        )?;

        Ok(out)
    }
}

#[derive(Clone, Debug)]
pub struct AssertEqualConfig<F: WithSmallOrderMulGroup<3> + Ord> {
    s_eq: Selector,
    advices: [Column<Advice>; 2],
    _marker: PhantomData<F>,
}

pub struct AssertEqualChip<F: WithSmallOrderMulGroup<3> + Ord> {
    config: AssertEqualConfig<F>,
    _marker: PhantomData<F>,
}

impl<F: WithSmallOrderMulGroup<3> + Ord> Chip<F> for AssertEqualChip<F> {
    type Config = AssertEqualConfig<F>;
    type Loaded = ();

    fn config(&self) -> &Self::Config {
        &self.config
    }

    fn loaded(&self) -> &Self::Loaded {
        &()
    }
}

impl<F: WithSmallOrderMulGroup<3> + Ord> AssertEqualChip<F> {
    pub fn construct(
        config: <Self as Chip<F>>::Config,
        _loaded: <Self as Chip<F>>::Loaded,
    ) -> Self {
        Self { config, _marker: PhantomData }
    }

    pub fn configure(
        meta: &mut ConstraintSystem<F>,
        advices: [Column<Advice>; 2],
    ) -> <Self as Chip<F>>::Config {
        let s_eq = meta.selector();
        meta.create_gate("assert_eq", |meta| {
            let lhs = meta.query_advice(advices[0], Rotation::cur());
            let rhs = meta.query_advice(advices[1], Rotation::cur());
            let s_eq = meta.query_selector(s_eq);

            vec![s_eq * (lhs - rhs)]
        });

        AssertEqualConfig { s_eq, advices, _marker: PhantomData }
    }

    pub fn assert_equal(
        &self,
        layouter: &mut impl Layouter<F>,
        a: AssignedCell<F, F>,
        b: AssignedCell<F, F>,
    ) -> Result<(), plonk::Error> {
        let config = self.config();

        layouter.assign_region(
            || "assert_eq",
            |mut region: Region<'_, F>| {
                config.s_eq.enable(&mut region, 0)?;

                a.copy_advice(|| "copy a", &mut region, config.advices[0], 0)?;
                b.copy_advice(|| "copy b", &mut region, config.advices[1], 0)?;

                Ok(())
            },
        )?;

        Ok(())
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions