1use ark_ec::AffineRepr;
21use ark_ff::{Field, One, Zero};
22use ark_poly::{EvaluationDomain, Evaluations, Radix2EvaluationDomain};
23use core::{fmt::Debug, hash::Hash, iter::successors};
24use error_term::{compute_error, ExtendedEnv};
25use expressions::{folding_expression, FoldingColumnTrait, IntegratedFoldingExpr};
26use instance_witness::{Foldable, RelaxableInstance, RelaxablePair};
27use kimchi::circuits::gate::CurrOrNext;
28use mina_poseidon::FqSponge;
29use poly_commitment::{commitment::CommitmentCurve, PolyComm, SRS};
30use quadraticization::ExtendedWitnessGenerator;
31use std::{
32 rc::Rc,
33 sync::atomic::{AtomicUsize, Ordering},
34};
35
36pub use error_term::Side;
38pub use expressions::{ExpExtension, FoldingCompatibleExpr};
39pub use instance_witness::{Instance, RelaxedInstance, RelaxedWitness, Witness};
40
41pub mod columns;
42pub mod decomposable_folding;
43
44mod error_term;
45
46pub mod eval_leaf;
47pub mod expressions;
48pub mod instance_witness;
49pub mod quadraticization;
50pub mod standard_config;
51
52pub mod checker;
55
56type ScalarField<C> = <<C as FoldingConfig>::Curve as AffineRepr>::ScalarField;
61type BaseField<C> = <<C as FoldingConfig>::Curve as AffineRepr>::BaseField;
62
63pub trait FoldingConfig: Debug + 'static {
65 type Column: FoldingColumnTrait + Debug + Eq + Hash;
66
67 type Selector: Clone + Debug + Eq + Hash + Copy + Ord + PartialOrd;
69
70 type Challenge: Clone + Copy + Debug + Eq + Hash;
73
74 type Curve: CommitmentCurve;
76
77 type Srs: SRS<Self::Curve>;
80
81 type Instance: Instance<Self::Curve> + Clone;
83
84 type Witness: Witness<Self::Curve> + Clone;
88
89 type Structure: Clone;
90
91 type Env: FoldingEnv<
92 <Self::Curve as AffineRepr>::ScalarField,
93 Self::Instance,
94 Self::Witness,
95 Self::Column,
96 Self::Challenge,
97 Self::Selector,
98 Structure = Self::Structure,
99 >;
100}
101
102pub trait FoldingEnv<F: Zero + Clone, I, W, Col, Chal, Selector> {
111 type Structure;
113
114 fn new(structure: &Self::Structure, instances: [&I; 2], witnesses: [&W; 2]) -> Self;
117
118 fn challenge(&self, challenge: Chal, side: Side) -> F;
121
122 fn col(&self, col: Col, curr_or_next: CurrOrNext, side: Side) -> &[F];
124
125 fn selector(&self, s: &Selector, side: Side) -> &[F];
128}
129
130type Evals<F> = Evaluations<F, Radix2EvaluationDomain<F>>;
131
132pub struct FoldingScheme<'a, CF: FoldingConfig> {
133 pub expression: IntegratedFoldingExpr<CF>,
134 pub srs: &'a CF::Srs,
135 pub domain: Radix2EvaluationDomain<ScalarField<CF>>,
136 pub zero_vec: Evals<ScalarField<CF>>,
137 pub structure: CF::Structure,
138 pub extended_witness_generator: ExtendedWitnessGenerator<CF>,
139 quadraticization_columns: usize,
140}
141
142impl<'a, CF: FoldingConfig> FoldingScheme<'a, CF> {
143 pub fn new(
144 constraints: Vec<FoldingCompatibleExpr<CF>>,
145 srs: &'a CF::Srs,
146 domain: Radix2EvaluationDomain<ScalarField<CF>>,
147 structure: &CF::Structure,
148 ) -> (Self, FoldingCompatibleExpr<CF>) {
149 let (expression, extended_witness_generator, quadraticization_columns) =
150 folding_expression(constraints);
151 let zero = <ScalarField<CF>>::zero();
152 let evals = core::iter::repeat(zero).take(domain.size()).collect();
153 let zero_vec = Evaluations::from_vec_and_domain(evals, domain);
154 let final_expression = expression.clone().final_expression();
155 let scheme = Self {
156 expression,
157 srs,
158 domain,
159 zero_vec,
160 structure: structure.clone(),
161 extended_witness_generator,
162 quadraticization_columns,
163 };
164 (scheme, final_expression)
165 }
166
167 pub fn get_number_of_additional_columns(&self) -> usize {
169 self.quadraticization_columns
170 }
171
172 #[allow(clippy::type_complexity)]
183 pub fn fold_instance_witness_pair<A, B, Sponge>(
184 &self,
185 a: A,
186 b: B,
187 fq_sponge: &mut Sponge,
188 ) -> FoldingOutput<CF>
189 where
190 A: RelaxablePair<CF::Curve, CF::Instance, CF::Witness>,
191 B: RelaxablePair<CF::Curve, CF::Instance, CF::Witness>,
192 Sponge: FqSponge<BaseField<CF>, CF::Curve, ScalarField<CF>>,
193 {
194 let a = a.relax(&self.zero_vec);
195 let b = b.relax(&self.zero_vec);
196
197 let u = (a.0.u, b.0.u);
198
199 let (left_instance, left_witness) = a;
200 let (right_instance, right_witness) = b;
201 let env = ExtendedEnv::new(
202 &self.structure,
203 [left_instance, right_instance],
204 [left_witness, right_witness],
205 self.domain,
206 None,
207 );
208 let env: ExtendedEnv<CF> =
212 env.compute_extension(&self.extended_witness_generator, self.srs);
213
214 let error: [Vec<ScalarField<CF>>; 2] = compute_error(&self.expression, &env, u);
216 let error_evals = error.map(|e| Evaluations::from_vec_and_domain(e, self.domain));
217
218 let blinders = PolyComm::new(vec![ScalarField::<CF>::one()]);
221 let error_commitments = error_evals
222 .iter()
223 .map(|e| {
224 self.srs
225 .commit_evaluations_custom(self.domain, e, &blinders)
226 .unwrap()
227 .commitment
228 })
229 .collect::<Vec<_>>();
230 let error_commitments: [PolyComm<CF::Curve>; 2] = error_commitments.try_into().unwrap();
231
232 let error: [Vec<_>; 2] = error_evals.map(|e| e.evals);
233
234 assert_eq!(error_commitments[0].len(), 1);
237 assert_eq!(error_commitments[1].len(), 1);
238
239 let t_0 = &error_commitments[0].get_first_chunk();
240 let t_1 = &error_commitments[1].get_first_chunk();
241
242 let to_absorb = env.to_absorb(t_0, t_1);
244
245 fq_sponge.absorb_fr(&to_absorb.0);
246 fq_sponge.absorb_g(&to_absorb.1);
247
248 let challenge = fq_sponge.challenge();
249
250 let (
251 [relaxed_extended_left_instance, relaxed_extended_right_instance],
252 [relaxed_extended_left_witness, relaxed_extended_right_witness],
253 ) = env.unwrap();
254
255 let folded_instance = RelaxedInstance::combine_and_sub_cross_terms(
256 relaxed_extended_left_instance.clone(),
258 relaxed_extended_right_instance.clone(),
259 challenge,
260 &error_commitments,
261 );
262
263 let folded_witness = RelaxedWitness::combine_and_sub_cross_terms(
264 relaxed_extended_left_witness,
265 relaxed_extended_right_witness,
266 challenge,
267 error,
268 );
269 FoldingOutput {
270 folded_instance,
271 folded_witness,
272 t_0: error_commitments[0].clone(),
273 t_1: error_commitments[1].clone(),
274 relaxed_extended_left_instance,
275 relaxed_extended_right_instance,
276 to_absorb,
277 }
278 }
279
280 pub fn fold_instance_pair<A, B, Sponge>(
285 &self,
286 a: A,
287 b: B,
288 error_commitments: [PolyComm<CF::Curve>; 2],
289 fq_sponge: &mut Sponge,
290 ) -> RelaxedInstance<CF::Curve, CF::Instance>
291 where
292 A: RelaxableInstance<CF::Curve, CF::Instance>,
293 B: RelaxableInstance<CF::Curve, CF::Instance>,
294 Sponge: FqSponge<BaseField<CF>, CF::Curve, ScalarField<CF>>,
295 {
296 let a: RelaxedInstance<CF::Curve, CF::Instance> = a.relax();
297 let b: RelaxedInstance<CF::Curve, CF::Instance> = b.relax();
298
299 assert_eq!(error_commitments[0].len(), 1);
302 assert_eq!(error_commitments[1].len(), 1);
303
304 let to_absorb = {
305 let mut left = a.to_absorb();
306 let right = b.to_absorb();
307 left.0.extend(right.0);
308 left.1.extend(right.1);
309 left.1.extend([
310 error_commitments[0].get_first_chunk(),
311 error_commitments[1].get_first_chunk(),
312 ]);
313 left
314 };
315
316 fq_sponge.absorb_fr(&to_absorb.0);
317 fq_sponge.absorb_g(&to_absorb.1);
318
319 let challenge = fq_sponge.challenge();
320
321 RelaxedInstance::combine_and_sub_cross_terms(a, b, challenge, &error_commitments)
322 }
323
324 #[allow(clippy::type_complexity)]
325 pub fn verify_fold<Sponge>(
329 &self,
330 left_instance: RelaxedInstance<CF::Curve, CF::Instance>,
331 right_instance: RelaxedInstance<CF::Curve, CF::Instance>,
332 t_0: PolyComm<CF::Curve>,
333 t_1: PolyComm<CF::Curve>,
334 fq_sponge: &mut Sponge,
335 ) -> RelaxedInstance<CF::Curve, CF::Instance>
336 where
337 Sponge: FqSponge<BaseField<CF>, CF::Curve, ScalarField<CF>>,
338 {
339 let to_absorb = {
340 let mut left = left_instance.to_absorb();
341 let right = right_instance.to_absorb();
342 left.0.extend(right.0);
343 left.1.extend(right.1);
344 left.1
345 .extend([t_0.get_first_chunk(), t_1.get_first_chunk()]);
346 left
347 };
348
349 fq_sponge.absorb_fr(&to_absorb.0);
350 fq_sponge.absorb_g(&to_absorb.1);
351
352 let challenge = fq_sponge.challenge();
353
354 RelaxedInstance::combine_and_sub_cross_terms(
355 left_instance.clone(),
357 right_instance.clone(),
358 challenge,
359 &[t_0, t_1],
360 )
361 }
362}
363
364pub struct FoldingOutput<C: FoldingConfig> {
366 pub folded_instance: RelaxedInstance<C::Curve, C::Instance>,
368 pub folded_witness: RelaxedWitness<C::Curve, C::Witness>,
371 pub t_0: PolyComm<C::Curve>,
374 pub t_1: PolyComm<C::Curve>,
377 pub relaxed_extended_left_instance: RelaxedInstance<C::Curve, C::Instance>,
380 pub relaxed_extended_right_instance: RelaxedInstance<C::Curve, C::Instance>,
383 pub to_absorb: (Vec<ScalarField<C>>, Vec<C::Curve>),
385}
386
387impl<C: FoldingConfig> FoldingOutput<C> {
388 #[allow(clippy::type_complexity)]
389 pub fn pair(
390 self,
391 ) -> (
392 RelaxedInstance<C::Curve, C::Instance>,
393 RelaxedWitness<C::Curve, C::Witness>,
394 ) {
395 (self.folded_instance, self.folded_witness)
396 }
397}
398
399#[derive(Debug, Clone)]
408pub enum Alphas<F: Field> {
409 Powers(F, Rc<AtomicUsize>),
410 Combinations(Vec<F>),
411}
412
413impl<F: Field> PartialEq for Alphas<F> {
414 fn eq(&self, other: &Self) -> bool {
415 self.clone().powers() == other.clone().powers()
417 }
418}
419
420impl<F: Field> Eq for Alphas<F> {}
421
422impl<F: Field> Foldable<F> for Alphas<F> {
423 fn combine(a: Self, b: Self, challenge: F) -> Self {
424 let a = a.powers();
425 let b = b.powers();
426 assert_eq!(a.len(), b.len());
427 let comb = a
428 .into_iter()
429 .zip(b)
430 .map(|(a, b)| a + b * challenge)
431 .collect();
432 Self::Combinations(comb)
433 }
434}
435
436impl<F: Field> Alphas<F> {
437 pub fn new(alpha: F) -> Self {
438 Self::Powers(alpha, Rc::new(AtomicUsize::from(0)))
439 }
440
441 pub fn new_sized(alpha: F, count: usize) -> Self {
442 Self::Powers(alpha, Rc::new(AtomicUsize::from(count)))
443 }
444
445 pub fn get(&self, i: usize) -> Option<F> {
446 match self {
447 Alphas::Powers(alpha, count) => {
448 let _ = count.fetch_max(i + 1, Ordering::Relaxed);
449 let i = [i as u64];
450 Some(alpha.pow(i))
451 }
452 Alphas::Combinations(alphas) => alphas.get(i).cloned(),
453 }
454 }
455
456 pub fn powers(self) -> Vec<F> {
457 match self {
458 Alphas::Powers(alpha, count) => {
459 let n = count.load(Ordering::Relaxed);
460 let alphas = successors(Some(F::one()), |last| Some(*last * alpha));
461 alphas.take(n).collect()
462 }
463 Alphas::Combinations(c) => c,
464 }
465 }
466}