kimchi/
alphas.rs

1//! This module implements an abstraction to keep track of the powers of alphas.
2//! As a recap, alpha is a challenge sent by the verifier in PLONK,
3//! and is used to aggregate multiple constraints into a single polynomial.
4//! It is important that different constraints use different powers of alpha,
5//! as otherwise they can interact and potentially cancel one another.
6//! (The proof is in the use of the Schwartz-Zippel lemma.)
7//! As such, we want two properties from this:
8//!
9//! - we should keep track of a mapping between type of constraint and range of powers
10//! - when powers of alphas are used, we should ensure that no more no less are used
11//!
12//! We use powers of alpha in two different places in the codebase:
13//!
14//! - when creating the index, we do not know alpha at this point so we
15//!   simply keep track of what constraints will use what powers
16//! - when creating a proof or verifying a proof, at this point we know alpha
17//!   so we can use the mapping we created during the creation of the index.
18//!
19//! For this to work, we use the type [Alphas] to register ranges of powers of alpha,
20//! for the various [ArgumentType]s.
21//!
22
23use 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// ------------------------------------------
36
37/// This type can be used to create a mapping between powers of alpha and constraint types.
38/// See [Self::default] to create one,
39/// and [Self::register] to register a new mapping.
40/// Once you know the alpha value, you can convert this type to a [Alphas].
41#[derive(Debug, Default, Serialize, Deserialize, Clone)]
42pub struct Alphas<F> {
43    /// The next power of alpha to use
44    /// the end result will be [1, alpha^{next_power - 1}]
45    next_power: u32,
46    /// The mapping between constraint types and powers of alpha
47    mapping: HashMap<ArgumentType, (u32, u32)>,
48    /// The powers of alpha: 1, alpha, alpha^2, etc.
49    /// If set to [Some], you can't register new constraints.
50    alphas: Option<Vec<F>>,
51}
52
53impl<F: Field> Alphas<F> {
54    /// Registers a new [ArgumentType],
55    /// associating it with a number `powers` of powers of alpha.
56    /// This function will panic if you register the same type twice.
57    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        // gates are a special case, as we reuse the same power of alpha
63        // across all of them (they're mutually exclusive)
64        let ty = if matches!(ty, ArgumentType::Gate(_)) {
65            // the zero gate is not used, so we default to it
66            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    /// Returns a range of exponents, for a given [ArgumentType], upperbounded by `num`.
82    /// Note that this function will panic if you did not register enough powers of alpha.
83    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    /// Instantiates the ranges with an actual field element `alpha`.
116    /// Once you call this function, you cannot register new constraints via [Self::register].
117    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    /// This function allows us to retrieve the powers of alpha, upperbounded by `num`
129    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            //            ArgumentType::Lookup,
175        ] {
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// ------------------------------------------
197
198/// Wrapper around an iterator that warns you if not consumed entirely.
199#[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// ------------------------------------------
239
240#[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    // testing [Builder]
249
250    #[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    // useful for the spec
315
316    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        // make sure this is present in the specification
329        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}