1use crate::{
4 expressions::FoldingColumnTrait, instance_witness::Witness, FoldingConfig, FoldingEnv,
5 Instance, Side,
6};
7use core::{fmt::Debug, hash::Hash, marker::PhantomData, ops::Index};
8use derivative::Derivative;
9use kimchi::{circuits::gate::CurrOrNext, curve::KimchiCurve};
10use memoization::ColumnMemoizer;
11use poly_commitment::{self, commitment::CommitmentCurve};
12
13#[derive(Clone, Default)]
14pub struct EmptyStructure<G: KimchiCurve>(PhantomData<G::ScalarField>);
16
17impl<G: KimchiCurve, Col> Index<Col> for EmptyStructure<G> {
18 type Output = Vec<G::ScalarField>;
19
20 fn index(&self, _index: Col) -> &Self::Output {
21 panic!("shouldn't reach this point, as this type only works with witness-only constraint systems");
22 }
23}
24
25#[derive(Derivative)]
60#[derivative(Hash, PartialEq, Eq, Debug)]
61#[allow(clippy::type_complexity)]
62pub struct StandardConfig<G, Col, Chall, I, W, Srs, Sel = (), Str = EmptyStructure<G>>(
63 PhantomData<(G, Col, Chall, Sel, Str, I, W, Srs)>,
64);
65
66impl<G, Col, Chall, Sel, Str, I, W, Srs> FoldingConfig
68 for StandardConfig<G, Col, Chall, I, W, Srs, Sel, Str>
69where
70 Self: 'static,
71 G: CommitmentCurve,
72 I: Instance<G> + Index<Chall, Output = G::ScalarField> + Clone,
73 W: Witness<G> + Clone,
74 W: Index<Col, Output = [G::ScalarField]> + Index<Sel, Output = [G::ScalarField]>,
75 Srs: poly_commitment::SRS<G>,
76 Col: Hash + Eq + Debug + Clone + FoldingColumnTrait,
77 Sel: Ord + Copy + Hash + Debug,
78 Chall: Hash + Eq + Debug + Copy,
79 Str: Clone + Index<Col, Output = [G::ScalarField]>,
80{
81 type Column = Col;
82
83 type Selector = Sel;
84
85 type Challenge = Chall;
86
87 type Curve = G;
88
89 type Srs = Srs;
90
91 type Instance = I;
92
93 type Witness = W;
94
95 type Structure = Str;
96
97 type Env = Env<G, Col, Chall, Sel, Str, I, W>;
98}
99pub struct Env<G, Col, Chall, Sel, Str, I, W>
101where
102 G: CommitmentCurve,
103 I: Instance<G> + Index<Chall, Output = G::ScalarField> + Clone,
104 W: Witness<G> + Clone,
105 W: Index<Col, Output = [G::ScalarField]> + Index<Sel, Output = [G::ScalarField]>,
106 Col: Hash + Eq,
107{
108 instances: [I; 2],
109 witnesses: [W; 2],
110 next_evals: ColumnMemoizer<Col, G::ScalarField, 10>,
111 structure: Str,
112 _phantom: PhantomData<(G, Col, Chall, Sel, Str)>,
114}
115
116impl<G, Col, Chall, Sel, Str, I, W> FoldingEnv<G::ScalarField, I, W, Col, Chall, Sel>
118 for Env<G, Col, Chall, Sel, Str, I, W>
119where
120 G: CommitmentCurve,
121 I: Instance<G> + Index<Chall, Output = G::ScalarField> + Clone,
122 W: Witness<G> + Clone,
123 W: Index<Col, Output = [G::ScalarField]> + Index<Sel, Output = [G::ScalarField]>,
124 Col: FoldingColumnTrait + Eq + Hash,
125 Sel: Copy,
126 Str: Clone + Index<Col, Output = [G::ScalarField]>,
127{
128 type Structure = Str;
129
130 fn new(structure: &Self::Structure, instances: [&I; 2], witnesses: [&W; 2]) -> Self {
131 let instances = instances.map(Clone::clone);
134 let witnesses = witnesses.map(Clone::clone);
135 let structure = structure.clone();
136 Self {
137 instances,
138 witnesses,
139 structure,
140 next_evals: ColumnMemoizer::new(),
141 _phantom: PhantomData,
142 }
143 }
144
145 fn challenge(&self, challenge: Chall, side: Side) -> G::ScalarField {
146 let instance = match side {
147 Side::Left => &self.instances[0],
148 Side::Right => &self.instances[1],
149 };
150 instance[challenge]
152 }
153
154 fn col(&self, col: Col, curr_or_next: CurrOrNext, side: Side) -> &[G::ScalarField] {
155 let witness = match side {
156 Side::Left => &self.witnesses[0],
157 Side::Right => &self.witnesses[1],
158 };
159 if col.is_witness() {
163 match curr_or_next {
164 CurrOrNext::Curr => &witness[col],
165 CurrOrNext::Next => {
166 let f = || {
167 let evals = &witness[col];
175 let mut next = Vec::with_capacity(evals.len());
176 next.extend(evals[1..].iter());
177 next.push(evals[0]);
178 next
179 };
180 self.next_evals.get_or_insert(col, f)
181 }
182 }
183 } else {
184 &self.structure[col]
185 }
186 }
187
188 fn selector(&self, s: &Sel, side: Side) -> &[G::ScalarField] {
189 let witness = match side {
191 Side::Left => &self.witnesses[0],
192 Side::Right => &self.witnesses[1],
193 };
194 &witness[*s]
195 }
196}
197
198mod memoization {
201 use ark_ff::Field;
202 use core::hash::Hash;
203 use std::{
204 cell::{OnceCell, RefCell},
205 collections::HashMap,
206 sync::atomic::{AtomicUsize, Ordering},
207 };
208
209 pub struct ColumnMemoizerSegment<F: Field, const N: usize> {
212 cols: [OnceCell<Vec<F>>; N],
213 next: OnceCell<Box<Self>>,
214 }
215
216 impl<F: Field, const N: usize> ColumnMemoizerSegment<F, N> {
217 pub fn new() -> Self {
218 let cols = [(); N].map(|_| OnceCell::new());
219 let next = OnceCell::new();
220 Self { cols, next }
221 }
222 pub fn get_or_insert<I>(&self, i: usize, f: I) -> &Vec<F>
227 where
228 I: FnOnce() -> Vec<F>,
229 {
230 match i {
231 i if i < N => {
232 let col = &self.cols[i];
233 col.get_or_init(f)
234 }
235 i => {
236 let i = i - N;
237 let new = || Box::new(Self::new());
238 let next = self.next.get_or_init(new);
239 next.get_or_insert(i, f)
240 }
241 }
242 }
243 }
244 pub struct ColumnMemoizer<C: Hash + Eq, F: Field, const N: usize> {
248 first_segment: ColumnMemoizerSegment<F, N>,
249 next: AtomicUsize,
250 ids: RefCell<HashMap<C, usize>>,
251 }
252
253 impl<C: Hash + Eq, F: Field, const N: usize> ColumnMemoizer<C, F, N> {
254 pub fn new() -> Self {
255 let first_segment = ColumnMemoizerSegment::new();
256 let next = AtomicUsize::from(0);
257 let ids = RefCell::new(HashMap::new());
258 Self {
259 first_segment,
260 next,
261 ids,
262 }
263 }
264 pub fn get_or_insert<I>(&self, col: C, f: I) -> &Vec<F>
265 where
266 I: FnOnce() -> Vec<F>,
267 {
268 let mut ids = self.ids.borrow_mut();
271 let new_id = || self.next.fetch_add(1, Ordering::Relaxed);
272 let id = ids.entry(col).or_insert_with(new_id);
273 self.first_segment.get_or_insert(*id, f)
274 }
275 }
276}