1use crate::circuits::{argument::ArgumentType, gate::GateType};
24use ark_ff::Field;
25use serde::{Deserialize, Serialize};
26use std::{
27 collections::HashMap,
28 fmt::Display,
29 iter::{Cloned, Skip, Take},
30 ops::Range,
31 slice::Iter,
32 thread,
33};
34
35#[derive(Debug, Default, Serialize, Deserialize, Clone)]
42pub struct Alphas<F> {
43 next_power: u32,
46 mapping: HashMap<ArgumentType, (u32, u32)>,
48 alphas: Option<Vec<F>>,
51}
52
53impl<F: Field> Alphas<F> {
54 pub fn register(&mut self, ty: ArgumentType, powers: u32) {
58 if self.alphas.is_some() {
59 panic!("you cannot register new constraints once initialized with a field element");
60 }
61
62 let ty = if matches!(ty, ArgumentType::Gate(_)) {
65 ArgumentType::Gate(GateType::Zero)
67 } else {
68 ty
69 };
70
71 if self.mapping.insert(ty, (self.next_power, powers)).is_some() {
72 panic!("cannot re-register {ty:?}");
73 }
74
75 self.next_power = self
76 .next_power
77 .checked_add(powers)
78 .expect("too many powers of alphas were registered");
79 }
80
81 pub fn get_exponents(
84 &self,
85 ty: ArgumentType,
86 num: u32,
87 ) -> MustConsumeIterator<Range<u32>, u32> {
88 let ty = if matches!(ty, ArgumentType::Gate(_)) {
89 ArgumentType::Gate(GateType::Zero)
90 } else {
91 ty
92 };
93
94 let range = self
95 .mapping
96 .get(&ty)
97 .unwrap_or_else(|| panic!("constraint {ty:?} was not registered"));
98
99 if num > range.1 {
100 panic!(
101 "you asked for {num} exponents, but only registered {} for {:?}",
102 range.1, ty
103 );
104 }
105
106 let start = range.0;
107 let end = start + num;
108
109 MustConsumeIterator {
110 inner: start..end,
111 debug_info: ty,
112 }
113 }
114
115 pub fn instantiate(&mut self, alpha: F) {
118 let mut last_power = F::one();
119 let mut alphas = Vec::with_capacity(self.next_power as usize);
120 alphas.push(F::one());
121 for _ in 1..self.next_power {
122 last_power *= alpha;
123 alphas.push(last_power);
124 }
125 self.alphas = Some(alphas);
126 }
127
128 pub fn get_alphas(
130 &self,
131 ty: ArgumentType,
132 num: u32,
133 ) -> MustConsumeIterator<Cloned<Take<Skip<Iter<F>>>>, F> {
134 let ty = if matches!(ty, ArgumentType::Gate(_)) {
135 ArgumentType::Gate(GateType::Zero)
136 } else {
137 ty
138 };
139
140 let range = self
141 .mapping
142 .get(&ty)
143 .unwrap_or_else(|| panic!("constraint {ty:?} was not registered"));
144
145 if num > range.1 {
146 panic!(
147 "you asked for {num} alphas, but only {} are available for {:?}",
148 range.1, ty
149 );
150 }
151
152 match &self.alphas {
153 None => panic!("you must call instantiate with an actual field element first"),
154 Some(alphas) => {
155 let alphas_range = alphas
156 .iter()
157 .skip(range.0 as usize)
158 .take(num as usize)
159 .cloned();
160 MustConsumeIterator {
161 inner: alphas_range,
162 debug_info: ty,
163 }
164 }
165 }
166 }
167}
168
169impl<T> Display for Alphas<T> {
170 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
171 for arg in [
172 ArgumentType::Gate(GateType::Zero),
173 ArgumentType::Permutation,
174 ] {
176 let name = if matches!(arg, ArgumentType::Gate(_)) {
177 "gates".to_string()
178 } else {
179 format!("{arg:?}")
180 };
181 let range = self
182 .mapping
183 .get(&arg)
184 .expect("you need to register all arguments before calling display");
185 writeln!(
186 f,
187 "* **{}**. Offset starts at {} and {} powers of $\\alpha$ are used",
188 name, range.0, range.1
189 )?;
190 }
191
192 Ok(())
193 }
194}
195
196#[derive(Debug)]
200pub struct MustConsumeIterator<I, T>
201where
202 I: Iterator<Item = T>,
203 T: core::fmt::Display,
204{
205 inner: I,
206 debug_info: ArgumentType,
207}
208
209impl<I, T> Iterator for MustConsumeIterator<I, T>
210where
211 I: Iterator<Item = T>,
212 T: core::fmt::Display,
213{
214 type Item = I::Item;
215 fn next(&mut self) -> Option<Self::Item> {
216 self.inner.next()
217 }
218}
219
220impl<I, T> Drop for MustConsumeIterator<I, T>
221where
222 I: Iterator<Item = T>,
223 T: core::fmt::Display,
224{
225 fn drop(&mut self) {
226 if let Some(v) = self.inner.next() {
227 if thread::panicking() {
228 eprintln!("the registered number of powers of alpha for {:?} is too large, you haven't used alpha^{} (absolute power of alpha)", self.debug_info,
229 v);
230 } else {
231 panic!("the registered number of powers of alpha for {:?} is too large, you haven't used alpha^{} (absolute power of alpha)", self.debug_info,
232 v);
233 }
234 }
235 }
236}
237
238#[cfg(test)]
241mod tests {
242 use std::{fs, path::Path};
243
244 use super::*;
245 use crate::circuits::gate::GateType;
246 use mina_curves::pasta::{Fp, Vesta};
247
248 #[test]
251 fn incorrect_alpha_powers() {
252 let mut alphas = Alphas::<Fp>::default();
253 alphas.register(ArgumentType::Gate(GateType::Poseidon), 3);
254
255 let mut powers = alphas.get_exponents(ArgumentType::Gate(GateType::Poseidon), 3);
256 assert_eq!(powers.next(), Some(0));
257 assert_eq!(powers.next(), Some(1));
258 assert_eq!(powers.next(), Some(2));
259
260 alphas.register(ArgumentType::Permutation, 3);
261 let mut powers = alphas.get_exponents(ArgumentType::Permutation, 3);
262
263 assert_eq!(powers.next(), Some(3));
264 assert_eq!(powers.next(), Some(4));
265 assert_eq!(powers.next(), Some(5));
266 }
267
268 #[test]
269 #[should_panic]
270 fn register_after_instantiating() {
271 let mut alphas = Alphas::<Fp>::default();
272 alphas.instantiate(Fp::from(1));
273 alphas.register(ArgumentType::Gate(GateType::Poseidon), 3);
274 }
275
276 #[test]
277 #[should_panic]
278 fn didnt_use_all_alpha_powers() {
279 let mut alphas = Alphas::<Fp>::default();
280 alphas.register(ArgumentType::Permutation, 7);
281 let mut powers = alphas.get_exponents(ArgumentType::Permutation, 3);
282 powers.next();
283 }
284
285 #[test]
286 #[should_panic]
287 fn registered_alpha_powers_for_some_constraint_twice() {
288 let mut alphas = Alphas::<Fp>::default();
289 alphas.register(ArgumentType::Gate(GateType::Poseidon), 2);
290 alphas.register(ArgumentType::Gate(GateType::ForeignFieldMul), 3);
291 }
292
293 #[test]
294 fn powers_of_alpha() {
295 let mut alphas = Alphas::default();
296 alphas.register(ArgumentType::Gate(GateType::Poseidon), 4);
297 let mut powers = alphas.get_exponents(ArgumentType::Gate(GateType::Poseidon), 4);
298
299 assert_eq!(powers.next(), Some(0));
300 assert_eq!(powers.next(), Some(1));
301 assert_eq!(powers.next(), Some(2));
302 assert_eq!(powers.next(), Some(3));
303
304 let alpha = Fp::from(2);
305 alphas.instantiate(alpha);
306
307 let mut alphas = alphas.get_alphas(ArgumentType::Gate(GateType::Poseidon), 4);
308 assert_eq!(alphas.next(), Some(1.into()));
309 assert_eq!(alphas.next(), Some(2.into()));
310 assert_eq!(alphas.next(), Some(4.into()));
311 assert_eq!(alphas.next(), Some(8.into()));
312 }
313
314 use crate::{
317 circuits::{gate::CircuitGate, wires::Wire},
318 linearization::expr_linearization,
319 prover_index::testing::new_index_for_test,
320 };
321
322 #[test]
323 fn get_alphas_for_spec() {
324 let gates = vec![CircuitGate::<Fp>::zero(Wire::for_row(0)); 2];
325 let index = new_index_for_test::<Vesta>(gates, 0);
326 let (_linearization, powers_of_alpha) =
327 expr_linearization::<Fp>(Some(&index.cs.feature_flags), true);
328 let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
330 let spec_path = Path::new(&manifest_dir)
331 .join("..")
332 .join("book")
333 .join("specifications")
334 .join("kimchi")
335 .join("template.md");
336
337 let spec = fs::read_to_string(spec_path).unwrap();
338 if !spec.contains(&powers_of_alpha.to_string()) {
339 panic!(
340 "the specification of kimchi must contain the following paragraph:\n\n{powers_of_alpha}\n\n"
341 );
342 }
343 }
344}