ark_r1cs_std/poly/domain/
mod.rs

1use crate::{
2    boolean::Boolean,
3    eq::EqGadget,
4    fields::{fp::FpVar, FieldVar},
5};
6use ark_ff::PrimeField;
7use ark_relations::r1cs::SynthesisError;
8use ark_std::vec::Vec;
9
10pub mod vanishing_poly;
11
12#[derive(Clone, Debug)]
13/// Defines an evaluation domain over a prime field. The domain is a coset of
14/// size `1<<dim`.
15///
16/// Native code corresponds to `ark-poly::univariate::domain::radix2`, but
17/// `ark-poly` only supports subgroup for now.
18// TODO: support cosets in `ark-poly`.
19pub struct Radix2DomainVar<F: PrimeField> {
20    /// generator of subgroup g
21    pub gen: F,
22    /// index of the quotient group (i.e. the `offset`)
23    offset: FpVar<F>,
24    /// dimension of evaluation domain, which is log2(size of coset)
25    pub dim: u64,
26}
27impl<F: PrimeField> Radix2DomainVar<F> {
28    /// Construct an evaluation domain with the given offset.
29    pub fn new(gen: F, dimension: u64, offset: FpVar<F>) -> Result<Self, SynthesisError> {
30        offset.enforce_not_equal(&FpVar::zero())?;
31        Ok(Self {
32            gen,
33            offset,
34            dim: dimension,
35        })
36    }
37
38    /// What is the offset of `self`?
39    pub fn offset(&self) -> &FpVar<F> {
40        &self.offset
41    }
42}
43
44impl<F: PrimeField> EqGadget<F> for Radix2DomainVar<F> {
45    fn is_eq(&self, other: &Self) -> Result<Boolean<F>, SynthesisError> {
46        if self.gen != other.gen || self.dim != other.dim {
47            Ok(Boolean::FALSE)
48        } else {
49            self.offset.is_eq(&other.offset)
50        }
51    }
52}
53
54impl<F: PrimeField> Radix2DomainVar<F> {
55    /// order of the domain
56    pub fn order(&self) -> usize {
57        1 << self.dim
58    }
59
60    /// Returns offset, offset*g, offset*g^2, ..., offset*g^{coset_size}
61    pub fn elements(&self) -> Vec<FpVar<F>> {
62        let mut result = Vec::new();
63        result.push(self.offset.clone());
64        for _ in 1..(1 << self.dim) {
65            let new_element = result.last().unwrap() * self.gen;
66            result.push(new_element);
67        }
68        result
69    }
70
71    /// Size of the domain
72    pub fn size(&self) -> u64 {
73        1 << self.dim
74    }
75
76    /// For domain `h<g>` with dimension `n`, `position` represented by
77    /// `query_pos` in big endian form, returns all points of
78    /// `h*g^{position}<g^{2^{n-coset_dim}}>`. The result is the query coset at
79    /// index `query_pos` for the FRI protocol.
80    ///
81    /// # Panics
82    /// This function panics when `query_pos.len() != coset_dim` or
83    /// `query_pos.len() != self.dim`.
84    pub fn query_position_to_coset_elements(
85        &self,
86        query_pos: &[Boolean<F>],
87        coset_dim: u64,
88    ) -> Result<Vec<FpVar<F>>, SynthesisError> {
89        Ok(self
90            .query_position_to_coset(query_pos, coset_dim)?
91            .elements())
92    }
93
94    /// For domain `h<g>` with dimension `n`, `position` represented by
95    /// `query_pos` in big endian form, returns all points of
96    /// `h*g^{position}<g^{n-query_pos.len()}>`
97    ///
98    /// # Panics
99    /// This function panics when `query_pos.len() < log2_num_cosets`.
100    pub fn query_position_to_coset(
101        &self,
102        query_pos: &[Boolean<F>],
103        coset_dim: u64,
104    ) -> Result<Self, SynthesisError> {
105        let coset_index = truncate_to_coset_index(query_pos, self.dim, coset_dim);
106        let offset_var = &self.offset * FpVar::Constant(self.gen).pow_le(&coset_index)?;
107        Ok(Self {
108            gen: self.gen.pow(&[1 << (self.dim - coset_dim)]), // distance between coset
109            offset: offset_var,
110            dim: coset_dim,
111        })
112    }
113}
114
115fn truncate_to_coset_index<F: PrimeField>(
116    query_pos: &[Boolean<F>],
117    codeword_dim: u64,
118    coset_dim: u64,
119) -> Vec<Boolean<F>> {
120    let log2_num_cosets = (codeword_dim - coset_dim) as usize;
121    assert!(query_pos.len() >= log2_num_cosets);
122    query_pos[0..log2_num_cosets].to_vec()
123}
124
125#[cfg(test)]
126mod tests {
127    use crate::prelude::*;
128    use ark_ff::PrimeField;
129    use ark_relations::r1cs::ConstraintSystem;
130    use ark_std::{rand::Rng, test_rng};
131
132    use crate::{
133        alloc::AllocVar, convert::ToBitsGadget, fields::fp::FpVar, poly::domain::Radix2DomainVar,
134        R1CSVar,
135    };
136
137    fn test_query_coset_template<F: PrimeField>() {
138        const COSET_DIM: u64 = 7;
139        const COSET_SIZE: u64 = 1 << COSET_DIM;
140        const LOCALIZATION: u64 = 3;
141        let cs = ConstraintSystem::new_ref();
142        let mut rng = test_rng();
143        let gen = F::get_root_of_unity(COSET_SIZE).unwrap();
144        let offset = F::rand(&mut rng);
145        let offset_var = FpVar::new_witness(cs.clone(), || Ok(offset)).unwrap();
146        let domain = Radix2DomainVar::new(gen, COSET_DIM, offset_var).unwrap();
147
148        let num_cosets = 1 << (COSET_DIM - LOCALIZATION);
149
150        let coset_index = rng.gen_range(0..num_cosets);
151        println!("{:0b}", coset_index);
152        let coset_index_var = UInt32::new_witness(cs.clone(), || Ok(coset_index))
153            .unwrap()
154            .to_bits_le()
155            .unwrap()
156            .into_iter()
157            .take(COSET_DIM as usize)
158            .collect::<Vec<_>>();
159
160        let elements_actual = domain
161            .query_position_to_coset(&coset_index_var, LOCALIZATION)
162            .unwrap()
163            .elements();
164
165        let elements_expected = domain
166            .elements()
167            .into_iter()
168            .skip(coset_index as usize)
169            .step_by(1 << (COSET_DIM - LOCALIZATION))
170            .collect::<Vec<_>>();
171
172        assert_eq!(elements_expected.len(), elements_actual.len());
173
174        elements_expected
175            .into_iter()
176            .zip(elements_actual.into_iter())
177            .for_each(|(left, right)| {
178                assert_eq!(left.value().unwrap(), right.value().unwrap());
179            });
180    }
181
182    #[test]
183    fn test_on_bls12_381() {
184        test_query_coset_template::<ark_bls12_381::Fr>();
185    }
186
187    #[test]
188    fn test_on_bls12_377() {
189        test_query_coset_template::<ark_bls12_377::Fr>();
190    }
191}