folding/
checker.rs

1//! A kind of pseudo-prover, will compute the expressions over the witness a check row by row
2//! for a zero result.
3
4use crate::{
5    expressions::{FoldingColumnTrait, FoldingCompatibleExpr, FoldingCompatibleExprInner},
6    instance_witness::Instance,
7    ExpExtension, FoldingConfig, Radix2EvaluationDomain, RelaxedInstance, RelaxedWitness,
8};
9use ark_ec::AffineRepr;
10use ark_ff::{Field, Zero};
11use ark_poly::Evaluations;
12use core::ops::Index;
13use kimchi::circuits::{expr::Variable, gate::CurrOrNext};
14
15#[cfg(not(test))]
16use log::debug;
17#[cfg(test)]
18use std::println as debug;
19
20// 1. We continue by defining a generic type of columns and selectors.
21// The selectors can be seen as additional (public) columns that are not part of
22// the witness.
23// The column must implement the trait [Hash] as it will be used by internal
24// structures of the library.
25#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
26pub enum Column {
27    X(usize),
28    Selector(usize),
29}
30
31// 2. We implement the trait [FoldingColumnTrait] that allows to distinguish
32// between the public and private inputs, often called the "instances" and the
33// "witnesses".
34// By default, we consider that the columns are all witness values and selectors
35// are public.
36impl FoldingColumnTrait for Column {
37    fn is_witness(&self) -> bool {
38        match self {
39            Column::X(_) => true,
40            Column::Selector(_) => false,
41        }
42    }
43}
44
45// 3. We define different traits that can be used generically by the folding
46// examples.
47// It can be used by "pseudo-provers".
48
49pub struct Provider<C: FoldingConfig> {
50    pub instance: C::Instance,
51    pub witness: C::Witness,
52}
53
54impl<C: FoldingConfig> Provider<C> {
55    pub fn new(instance: C::Instance, witness: C::Witness) -> Self {
56        Self { instance, witness }
57    }
58}
59
60pub struct ExtendedProvider<C: FoldingConfig> {
61    pub inner_provider: Provider<C>,
62    pub instance: RelaxedInstance<<C as FoldingConfig>::Curve, <C as FoldingConfig>::Instance>,
63    pub witness: RelaxedWitness<<C as FoldingConfig>::Curve, <C as FoldingConfig>::Witness>,
64}
65
66impl<C: FoldingConfig> ExtendedProvider<C> {
67    pub fn new(
68        instance: RelaxedInstance<C::Curve, C::Instance>,
69        witness: RelaxedWitness<C::Curve, C::Witness>,
70    ) -> Self {
71        let inner_provider = {
72            let instance = instance.extended_instance.instance.clone();
73            let witness = witness.extended_witness.witness.clone();
74            Provider::new(instance, witness)
75        };
76        Self {
77            inner_provider,
78            instance,
79            witness,
80        }
81    }
82}
83
84pub trait Provide<C: FoldingConfig> {
85    fn resolve(
86        &self,
87        inner: FoldingCompatibleExprInner<C>,
88        domain: Radix2EvaluationDomain<<C::Curve as AffineRepr>::ScalarField>,
89    ) -> Vec<<C::Curve as AffineRepr>::ScalarField>;
90}
91
92impl<C: FoldingConfig> Provide<C> for Provider<C>
93where
94    C::Witness: Index<
95        C::Column,
96        Output = Evaluations<
97            <C::Curve as AffineRepr>::ScalarField,
98            Radix2EvaluationDomain<<C::Curve as AffineRepr>::ScalarField>,
99        >,
100    >,
101    C::Witness: Index<
102        C::Selector,
103        Output = Evaluations<
104            <C::Curve as AffineRepr>::ScalarField,
105            Radix2EvaluationDomain<<C::Curve as AffineRepr>::ScalarField>,
106        >,
107    >,
108    C::Instance: Index<C::Challenge, Output = <C::Curve as AffineRepr>::ScalarField>,
109{
110    fn resolve(
111        &self,
112        inner: FoldingCompatibleExprInner<C>,
113        domain: Radix2EvaluationDomain<<C::Curve as AffineRepr>::ScalarField>,
114    ) -> Vec<<C::Curve as AffineRepr>::ScalarField> {
115        let domain_size = domain.size as usize;
116        match inner {
117            FoldingCompatibleExprInner::Constant(c) => {
118                vec![c; domain_size]
119            }
120            FoldingCompatibleExprInner::Challenge(chal) => {
121                let v = self.instance[chal];
122                vec![v; domain_size]
123            }
124            FoldingCompatibleExprInner::Cell(var) => {
125                let Variable { col, row } = var;
126
127                let col = &self.witness[col].evals;
128
129                let mut col = col.clone();
130                //check this, while not relevant in this case I think it should be right rotation
131                if let CurrOrNext::Next = row {
132                    col.rotate_left(1);
133                }
134                col
135            }
136            FoldingCompatibleExprInner::Extensions(_) => {
137                panic!("not handled here");
138            }
139        }
140    }
141}
142
143impl<C: FoldingConfig> Provide<C> for ExtendedProvider<C>
144where
145    C::Witness: Index<
146        C::Column,
147        Output = Evaluations<
148            <C::Curve as AffineRepr>::ScalarField,
149            Radix2EvaluationDomain<<C::Curve as AffineRepr>::ScalarField>,
150        >,
151    >,
152    C::Witness: Index<
153        C::Selector,
154        Output = Evaluations<
155            <C::Curve as AffineRepr>::ScalarField,
156            Radix2EvaluationDomain<<C::Curve as AffineRepr>::ScalarField>,
157        >,
158    >,
159    C::Instance: Index<C::Challenge, Output = <C::Curve as AffineRepr>::ScalarField>,
160{
161    fn resolve(
162        &self,
163        inner: FoldingCompatibleExprInner<C>,
164        domain: Radix2EvaluationDomain<<C::Curve as AffineRepr>::ScalarField>,
165    ) -> Vec<<C::Curve as AffineRepr>::ScalarField> {
166        match inner {
167            FoldingCompatibleExprInner::Extensions(ext) => match ext {
168                ExpExtension::U => {
169                    let u = self.instance.u;
170                    let domain_size = domain.size as usize;
171                    vec![u; domain_size]
172                }
173                ExpExtension::Error => self.witness.error_vec.evals.clone(),
174                ExpExtension::ExtendedWitness(i) => self
175                    .witness
176                    .extended_witness
177                    .extended
178                    .get(&i)
179                    .unwrap()
180                    .evals
181                    .clone(),
182                ExpExtension::Alpha(i) => {
183                    let alpha = self
184                        .instance
185                        .extended_instance
186                        .instance
187                        .get_alphas()
188                        .get(i)
189                        .unwrap();
190                    let domain_size = domain.size as usize;
191                    vec![alpha; domain_size]
192                }
193                ExpExtension::Selector(s) => {
194                    let col = &self.inner_provider.witness[s].evals;
195                    col.clone()
196                }
197            },
198            e => self.inner_provider.resolve(e, domain),
199        }
200    }
201}
202
203pub trait Checker<C: FoldingConfig>: Provide<C> {
204    fn check_rec(
205        &self,
206        exp: FoldingCompatibleExpr<C>,
207        domain: Radix2EvaluationDomain<<C::Curve as AffineRepr>::ScalarField>,
208    ) -> Vec<<C::Curve as AffineRepr>::ScalarField> {
209        let e2 = exp.clone();
210        let res = match exp {
211            FoldingCompatibleExpr::Atom(inner) => self.resolve(inner, domain),
212            FoldingCompatibleExpr::Double(e) => {
213                let v = self.check_rec(*e, domain);
214                v.into_iter().map(|x| x.double()).collect()
215            }
216            FoldingCompatibleExpr::Square(e) => {
217                let v = self.check_rec(*e, domain);
218                v.into_iter().map(|x| x.square()).collect()
219            }
220            FoldingCompatibleExpr::Add(e1, e2) => {
221                let v1 = self.check_rec(*e1, domain);
222                let v2 = self.check_rec(*e2, domain);
223                v1.into_iter().zip(v2).map(|(a, b)| a + b).collect()
224            }
225            FoldingCompatibleExpr::Sub(e1, e2) => {
226                let v1 = self.check_rec(*e1, domain);
227                let v2 = self.check_rec(*e2, domain);
228                v1.into_iter().zip(v2).map(|(a, b)| a - b).collect()
229            }
230            FoldingCompatibleExpr::Mul(e1, e2) => {
231                let v1 = self.check_rec(*e1, domain);
232                let v2 = self.check_rec(*e2, domain);
233                v1.into_iter().zip(v2).map(|(a, b)| a * b).collect()
234            }
235            FoldingCompatibleExpr::Pow(e, exp) => {
236                let v = self.check_rec(*e, domain);
237                v.into_iter().map(|x| x.pow([exp])).collect()
238            }
239        };
240        debug!("exp: {:?}", e2);
241        debug!("res: [\n");
242        for e in res.iter() {
243            debug!("{e}\n");
244        }
245        debug!("]");
246        res
247    }
248
249    fn check(
250        &self,
251        exp: &FoldingCompatibleExpr<C>,
252        domain: Radix2EvaluationDomain<<C::Curve as AffineRepr>::ScalarField>,
253    ) {
254        let res = self.check_rec(exp.clone(), domain);
255        for (i, row) in res.iter().enumerate() {
256            if !row.is_zero() {
257                panic!("check in row {i} failed, {row} != 0");
258            }
259        }
260    }
261}