1use crate::{
8 columns::ExtendedFoldingColumn,
9 error_term::{compute_error, ExtendedEnv},
10 expressions::{ExpExtension, FoldingCompatibleExpr, FoldingCompatibleExprInner, FoldingExp},
11 instance_witness::{RelaxableInstance, RelaxablePair, RelaxedInstance, RelaxedWitness},
12 BaseField, FoldingConfig, FoldingOutput, FoldingScheme, ScalarField,
13};
14use ark_poly::{Evaluations, Radix2EvaluationDomain};
15use mina_poseidon::FqSponge;
16use poly_commitment::{PolyComm, SRS};
17use std::collections::BTreeMap;
18
19pub struct DecomposableFoldingScheme<'a, CF: FoldingConfig> {
20 inner: FoldingScheme<'a, CF>,
21}
22
23impl<'a, CF: FoldingConfig> DecomposableFoldingScheme<'a, CF> {
24 pub fn new(
40 constraints: BTreeMap<CF::Selector, Vec<FoldingCompatibleExpr<CF>>>,
42 common_constraints: Vec<FoldingCompatibleExpr<CF>>,
44 srs: &'a CF::Srs,
45 domain: Radix2EvaluationDomain<ScalarField<CF>>,
46 structure: &CF::Structure,
47 ) -> (Self, FoldingCompatibleExpr<CF>) {
48 let constraints = constraints
49 .into_iter()
50 .flat_map(|(s, exps)| {
51 exps.into_iter().map(move |exp| {
52 let s = FoldingCompatibleExprInner::Extensions(ExpExtension::Selector(s));
53 let s = Box::new(FoldingCompatibleExpr::Atom(s));
54 FoldingCompatibleExpr::Mul(s, Box::new(exp))
55 })
56 })
57 .chain(common_constraints)
58 .collect();
59 let (inner, exp) = FoldingScheme::new(constraints, srs, domain, structure);
60 (DecomposableFoldingScheme { inner }, exp)
61 }
62
63 pub fn get_number_of_additional_columns(&self) -> usize {
65 self.inner.get_number_of_additional_columns()
66 }
67
68 #[allow(clippy::type_complexity)]
69 pub fn fold_instance_witness_pair<A, B, Sponge>(
74 &self,
75 a: A,
76 b: B,
77 selector: Option<CF::Selector>,
78 fq_sponge: &mut Sponge,
79 ) -> FoldingOutput<CF>
80 where
81 A: RelaxablePair<CF::Curve, CF::Instance, CF::Witness>,
82 B: RelaxablePair<CF::Curve, CF::Instance, CF::Witness>,
83 Sponge: FqSponge<BaseField<CF>, CF::Curve, ScalarField<CF>>,
84 {
85 let scheme = &self.inner;
86 let a = a.relax(&scheme.zero_vec);
87 let b = b.relax(&scheme.zero_vec);
88
89 let u = (a.0.u, b.0.u);
90
91 let (left_instance, left_witness) = a;
92 let (right_instance, right_witness) = b;
93 let env = ExtendedEnv::new(
94 &scheme.structure,
95 [left_instance, right_instance],
96 [left_witness, right_witness],
97 scheme.domain,
98 selector,
99 );
100
101 let env = env.compute_extension(&scheme.extended_witness_generator, scheme.srs);
102 let error = compute_error(&scheme.expression, &env, u);
103 let error_evals = error.map(|e| Evaluations::from_vec_and_domain(e, scheme.domain));
104
105 let error_commitments = error_evals
106 .iter()
107 .map(|e| scheme.srs.commit_evaluations_non_hiding(scheme.domain, e))
108 .collect::<Vec<_>>();
109 let error_commitments: [PolyComm<CF::Curve>; 2] = error_commitments.try_into().unwrap();
110
111 let error = error_evals.into_iter().map(|e| e.evals).collect::<Vec<_>>();
112 let error: [Vec<_>; 2] = error.try_into().unwrap();
113
114 assert_eq!(error_commitments[0].len(), 1);
117 assert_eq!(error_commitments[1].len(), 1);
118
119 let t0 = &error_commitments[0].get_first_chunk();
120 let t1 = &error_commitments[1].get_first_chunk();
121
122 let to_absorb = env.to_absorb(t0, t1);
123 fq_sponge.absorb_fr(&to_absorb.0);
124 fq_sponge.absorb_g(&to_absorb.1);
125
126 let challenge = fq_sponge.challenge();
127
128 let (
129 [relaxed_extended_left_instance, relaxed_extended_right_instance],
130 [relaxed_extended_left_witness, relaxed_extended_right_witness],
131 ) = env.unwrap();
132
133 let folded_instance = RelaxedInstance::combine_and_sub_cross_terms(
134 relaxed_extended_left_instance.clone(),
136 relaxed_extended_right_instance.clone(),
137 challenge,
138 &error_commitments,
139 );
140
141 let folded_witness = RelaxedWitness::combine_and_sub_cross_terms(
142 relaxed_extended_left_witness,
143 relaxed_extended_right_witness,
144 challenge,
145 error,
146 );
147 FoldingOutput {
148 folded_instance,
149 folded_witness,
150 t_0: error_commitments[0].clone(),
151 t_1: error_commitments[1].clone(),
152 relaxed_extended_left_instance,
153 relaxed_extended_right_instance,
154 to_absorb,
155 }
156 }
157
158 pub fn fold_instance_pair<A, B, Sponge>(
163 &self,
164 a: A,
165 b: B,
166 error_commitments: [PolyComm<CF::Curve>; 2],
167 fq_sponge: &mut Sponge,
168 ) -> RelaxedInstance<CF::Curve, CF::Instance>
169 where
170 A: RelaxableInstance<CF::Curve, CF::Instance>,
171 B: RelaxableInstance<CF::Curve, CF::Instance>,
172 Sponge: FqSponge<BaseField<CF>, CF::Curve, ScalarField<CF>>,
173 {
174 let a: RelaxedInstance<CF::Curve, CF::Instance> = a.relax();
175 let b: RelaxedInstance<CF::Curve, CF::Instance> = b.relax();
176
177 assert_eq!(error_commitments[0].len(), 1);
180 assert_eq!(error_commitments[1].len(), 1);
181
182 let to_absorb = {
183 let mut left = a.to_absorb();
184 let right = b.to_absorb();
185 left.0.extend(right.0);
186 left.1.extend(right.1);
187 left.1.extend([
188 error_commitments[0].get_first_chunk(),
189 error_commitments[1].get_first_chunk(),
190 ]);
191 left
192 };
193
194 fq_sponge.absorb_fr(&to_absorb.0);
195 fq_sponge.absorb_g(&to_absorb.1);
196
197 let challenge = fq_sponge.challenge();
198
199 RelaxedInstance::combine_and_sub_cross_terms(a, b, challenge, &error_commitments)
200 }
201}
202
203pub(crate) fn check_selector<C: FoldingConfig>(exp: &FoldingExp<C>) -> Option<&C::Selector> {
204 match exp {
205 FoldingExp::Atom(ExtendedFoldingColumn::Selector(s)) => Some(s),
206 _ => None,
207 }
208}