Skip to main content

kimchi/
linearization.rs

1//! This module implements the linearization.
2use alloc::{boxed::Box, vec::Vec};
3
4use crate::{
5    alphas::Alphas,
6    circuits::{
7        argument::{Argument, ArgumentType},
8        berkeley_columns::BerkeleyChallengeTerm,
9        expr, lookup,
10        lookup::{
11            constraints::LookupConfiguration,
12            lookups::{LookupFeatures, LookupInfo, LookupPattern, LookupPatterns},
13        },
14        polynomials::{
15            complete_add::CompleteAdd,
16            endomul_scalar::EndomulScalar,
17            endosclmul::EndosclMul,
18            foreign_field_add::circuitgates::ForeignFieldAdd,
19            foreign_field_mul::circuitgates::ForeignFieldMul,
20            generic, permutation,
21            poseidon::Poseidon,
22            range_check::circuitgates::{RangeCheck0, RangeCheck1},
23            rot,
24            varbasemul::VarbaseMul,
25            xor,
26        },
27    },
28    collections::HashSet,
29};
30
31use crate::circuits::{
32    berkeley_columns::Column,
33    constraints::FeatureFlags,
34    expr::{ConstantExpr, Expr, FeatureFlag, Linearization, PolishToken},
35    gate::GateType,
36    wires::COLUMNS,
37};
38use ark_ff::{FftField, PrimeField, Zero};
39
40/// Get the expresion of constraints.
41///
42/// # Panics
43///
44/// Will panic if `generic_gate` is not associate with `alpha^0`.
45pub fn constraints_expr<F: PrimeField>(
46    feature_flags: Option<&FeatureFlags>,
47    generic: bool,
48) -> (
49    Expr<ConstantExpr<F, BerkeleyChallengeTerm>, Column>,
50    Alphas<F>,
51) {
52    // register powers of alpha so that we don't reuse them across mutually inclusive constraints
53    let mut powers_of_alpha = Alphas::<F>::default();
54
55    // Set up powers of alpha. Only the max number of constraints matters.
56    // The gate type argument can just be the zero gate.
57    powers_of_alpha.register(
58        ArgumentType::Gate(GateType::Zero),
59        VarbaseMul::<F>::CONSTRAINTS,
60    );
61
62    let mut cache = expr::Cache::default();
63
64    let mut expr = Poseidon::combined_constraints(&powers_of_alpha, &mut cache);
65    expr += VarbaseMul::combined_constraints(&powers_of_alpha, &mut cache);
66    expr += CompleteAdd::combined_constraints(&powers_of_alpha, &mut cache);
67    expr += EndosclMul::combined_constraints(&powers_of_alpha, &mut cache);
68    expr += EndomulScalar::combined_constraints(&powers_of_alpha, &mut cache);
69
70    {
71        let mut range_check0_expr =
72            || RangeCheck0::combined_constraints(&powers_of_alpha, &mut cache);
73
74        if let Some(feature_flags) = feature_flags {
75            if feature_flags.range_check0 {
76                expr += range_check0_expr();
77            }
78        } else {
79            expr += Expr::IfFeature(
80                FeatureFlag::RangeCheck0,
81                Box::new(range_check0_expr()),
82                Box::new(Expr::zero()),
83            );
84        }
85    }
86
87    {
88        let mut range_check1_expr =
89            || RangeCheck1::combined_constraints(&powers_of_alpha, &mut cache);
90
91        if let Some(feature_flags) = feature_flags {
92            if feature_flags.range_check1 {
93                expr += range_check1_expr();
94            }
95        } else {
96            expr += Expr::IfFeature(
97                FeatureFlag::RangeCheck1,
98                Box::new(range_check1_expr()),
99                Box::new(Expr::zero()),
100            );
101        }
102    }
103
104    {
105        let mut foreign_field_add_expr =
106            || ForeignFieldAdd::combined_constraints(&powers_of_alpha, &mut cache);
107        if let Some(feature_flags) = feature_flags {
108            if feature_flags.foreign_field_add {
109                expr += foreign_field_add_expr();
110            }
111        } else {
112            expr += Expr::IfFeature(
113                FeatureFlag::ForeignFieldAdd,
114                Box::new(foreign_field_add_expr()),
115                Box::new(Expr::zero()),
116            );
117        }
118    }
119
120    {
121        let mut foreign_field_mul_expr =
122            || ForeignFieldMul::combined_constraints(&powers_of_alpha, &mut cache);
123        if let Some(feature_flags) = feature_flags {
124            if feature_flags.foreign_field_mul {
125                expr += foreign_field_mul_expr();
126            }
127        } else {
128            expr += Expr::IfFeature(
129                FeatureFlag::ForeignFieldMul,
130                Box::new(foreign_field_mul_expr()),
131                Box::new(Expr::zero()),
132            );
133        }
134    }
135
136    {
137        let mut xor_expr = || xor::Xor16::combined_constraints(&powers_of_alpha, &mut cache);
138        if let Some(feature_flags) = feature_flags {
139            if feature_flags.xor {
140                expr += xor_expr();
141            }
142        } else {
143            expr += Expr::IfFeature(
144                FeatureFlag::Xor,
145                Box::new(xor_expr()),
146                Box::new(Expr::zero()),
147            );
148        }
149    }
150
151    {
152        let mut rot_expr = || rot::Rot64::combined_constraints(&powers_of_alpha, &mut cache);
153        if let Some(feature_flags) = feature_flags {
154            if feature_flags.rot {
155                expr += rot_expr();
156            }
157        } else {
158            expr += Expr::IfFeature(
159                FeatureFlag::Rot,
160                Box::new(rot_expr()),
161                Box::new(Expr::zero()),
162            );
163        }
164    }
165
166    if generic {
167        expr += generic::Generic::combined_constraints(&powers_of_alpha, &mut cache);
168    }
169
170    // permutation
171    powers_of_alpha.register(ArgumentType::Permutation, permutation::CONSTRAINTS);
172
173    // lookup
174    if let Some(feature_flags) = feature_flags {
175        if feature_flags.lookup_features.patterns != LookupPatterns::default() {
176            let lookup_configuration =
177                LookupConfiguration::new(LookupInfo::create(feature_flags.lookup_features));
178            let constraints = lookup::constraints::constraints(&lookup_configuration, false);
179
180            // note: the number of constraints depends on the lookup configuration,
181            // specifically the presence of runtime tables.
182            let constraints_len = u32::try_from(constraints.len())
183                .expect("we always expect a relatively low amount of constraints");
184
185            powers_of_alpha.register(ArgumentType::Lookup, constraints_len);
186
187            let alphas = powers_of_alpha.get_exponents(ArgumentType::Lookup, constraints_len);
188            let combined = Expr::combine_constraints(alphas, constraints);
189
190            expr += combined;
191        }
192    } else {
193        let all_features = LookupFeatures {
194            patterns: LookupPatterns {
195                xor: true,
196                lookup: true,
197                range_check: true,
198                foreign_field_mul: true,
199            },
200            uses_runtime_tables: true,
201            joint_lookup_used: true,
202        };
203        let lookup_configuration = LookupConfiguration::new(LookupInfo::create(all_features));
204        let constraints = lookup::constraints::constraints(&lookup_configuration, true);
205
206        // note: the number of constraints depends on the lookup configuration,
207        // specifically the presence of runtime tables.
208        let constraints_len = u32::try_from(constraints.len())
209            .expect("we always expect a relatively low amount of constraints");
210
211        powers_of_alpha.register(ArgumentType::Lookup, constraints_len);
212
213        let alphas = powers_of_alpha.get_exponents(ArgumentType::Lookup, constraints_len);
214        let combined = Expr::IfFeature(
215            FeatureFlag::LookupTables,
216            Box::new(Expr::combine_constraints(alphas, constraints)),
217            Box::new(Expr::zero()),
218        );
219
220        expr += combined;
221    }
222
223    // the generic gate must be associated with alpha^0
224    // to make the later addition with the public input work
225    if cfg!(debug_assertions) {
226        let mut generic_alphas =
227            powers_of_alpha.get_exponents(ArgumentType::Gate(GateType::Generic), 1);
228        assert_eq!(generic_alphas.next(), Some(0));
229    }
230
231    // Check that the feature flags correctly turn on or off the constraints generated by the given
232    // flags.
233    if cfg!(feature = "check_feature_flags") {
234        if let Some(feature_flags) = feature_flags {
235            let (feature_flagged_expr, _) = constraints_expr(None, generic);
236            let feature_flagged_expr = feature_flagged_expr.apply_feature_flags(feature_flags);
237            assert_eq!(expr, feature_flagged_expr);
238        }
239    }
240
241    // return the expression
242    (expr, powers_of_alpha)
243}
244
245/// Adds the polynomials that are evaluated as part of the proof
246/// for the linearization to work.
247pub fn linearization_columns<F: FftField>(feature_flags: Option<&FeatureFlags>) -> HashSet<Column> {
248    let mut h = HashSet::new();
249    use Column::*;
250
251    let feature_flags = match feature_flags {
252        Some(feature_flags) => *feature_flags,
253        None =>
254        // Generating using `IfFeature`, turn on all feature flags.
255        {
256            FeatureFlags {
257                range_check0: true,
258                range_check1: true,
259                foreign_field_add: true,
260                foreign_field_mul: true,
261                xor: true,
262                rot: true,
263                lookup_features: LookupFeatures {
264                    patterns: LookupPatterns {
265                        xor: true,
266                        lookup: true,
267                        range_check: true,
268                        foreign_field_mul: true,
269                    },
270                    joint_lookup_used: true,
271                    uses_runtime_tables: true,
272                },
273            }
274        }
275    };
276
277    // the witness polynomials
278    for i in 0..COLUMNS {
279        h.insert(Witness(i));
280    }
281
282    // the coefficient polynomials
283    for i in 0..COLUMNS {
284        h.insert(Coefficient(i));
285    }
286
287    let lookup_info = if feature_flags.lookup_features.patterns == LookupPatterns::default() {
288        None
289    } else {
290        Some(LookupInfo::create(feature_flags.lookup_features))
291    };
292
293    // the lookup polynomials
294    if let Some(lookup_info) = lookup_info {
295        for i in 0..=lookup_info.max_per_row {
296            h.insert(LookupSorted(i));
297        }
298        h.insert(LookupAggreg);
299        h.insert(LookupTable);
300
301        // the runtime lookup polynomials
302        if lookup_info.features.uses_runtime_tables {
303            h.insert(LookupRuntimeTable);
304        }
305    }
306
307    // the permutation polynomial
308    h.insert(Z);
309
310    // the poseidon selector polynomial
311    h.insert(Index(GateType::Poseidon));
312
313    // the generic selector polynomial
314    h.insert(Index(GateType::Generic));
315
316    h.insert(Index(GateType::CompleteAdd));
317    h.insert(Index(GateType::VarBaseMul));
318    h.insert(Index(GateType::EndoMul));
319    h.insert(Index(GateType::EndoMulScalar));
320
321    // optional columns
322    h.insert(Index(GateType::RangeCheck0));
323    h.insert(Index(GateType::RangeCheck1));
324    h.insert(Index(GateType::ForeignFieldAdd));
325    h.insert(Index(GateType::ForeignFieldMul));
326    h.insert(Index(GateType::Xor16));
327    h.insert(Index(GateType::Rot64));
328
329    // lookup selectors
330    h.insert(LookupRuntimeSelector);
331    h.insert(LookupKindIndex(LookupPattern::Xor));
332    h.insert(LookupKindIndex(LookupPattern::Lookup));
333    h.insert(LookupKindIndex(LookupPattern::RangeCheck));
334    h.insert(LookupKindIndex(LookupPattern::ForeignFieldMul));
335
336    h
337}
338
339/// Linearize the `expr`.
340///
341/// If the `feature_flags` argument is `None`, this will generate an expression
342/// using the `Expr::IfFeature` variant for each of the flags.
343///
344/// # Panics
345///
346/// Will panic if the `linearization` process fails.
347#[allow(clippy::type_complexity)]
348pub fn expr_linearization<F: PrimeField>(
349    feature_flags: Option<&FeatureFlags>,
350    generic: bool,
351) -> (
352    Linearization<Vec<PolishToken<F, Column, BerkeleyChallengeTerm>>, Column>,
353    Alphas<F>,
354) {
355    let evaluated_cols = linearization_columns::<F>(feature_flags);
356
357    let (expr, powers_of_alpha) = constraints_expr(feature_flags, generic);
358
359    let linearization = expr
360        .linearize(evaluated_cols)
361        .unwrap()
362        .map(|e| e.to_polish());
363
364    assert_eq!(linearization.index_terms.len(), 0);
365
366    (linearization, powers_of_alpha)
367}