Skip to main content

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