kimchi/
linearization.rs

1//! This module implements the linearization.
2
3use crate::{
4    alphas::Alphas,
5    circuits::{
6        argument::{Argument, ArgumentType},
7        berkeley_columns::BerkeleyChallengeTerm,
8        expr, lookup,
9        lookup::{
10            constraints::LookupConfiguration,
11            lookups::{LookupFeatures, LookupInfo, LookupPattern, LookupPatterns},
12        },
13        polynomials::{
14            complete_add::CompleteAdd,
15            endomul_scalar::EndomulScalar,
16            endosclmul::EndosclMul,
17            foreign_field_add::circuitgates::ForeignFieldAdd,
18            foreign_field_mul::circuitgates::ForeignFieldMul,
19            generic, permutation,
20            poseidon::Poseidon,
21            range_check::circuitgates::{RangeCheck0, RangeCheck1},
22            rot,
23            varbasemul::VarbaseMul,
24            xor,
25        },
26    },
27};
28
29use crate::circuits::{
30    berkeley_columns::Column,
31    constraints::FeatureFlags,
32    expr::{ConstantExpr, Expr, FeatureFlag, Linearization, PolishToken},
33    gate::GateType,
34    wires::COLUMNS,
35};
36use ark_ff::{FftField, PrimeField, Zero};
37
38/// Get the expresion of constraints.
39///
40/// # Panics
41///
42/// Will panic if `generic_gate` is not associate with `alpha^0`.
43pub fn constraints_expr<F: PrimeField>(
44    feature_flags: Option<&FeatureFlags>,
45    generic: bool,
46) -> (
47    Expr<ConstantExpr<F, BerkeleyChallengeTerm>, Column>,
48    Alphas<F>,
49) {
50    // register powers of alpha so that we don't reuse them across mutually inclusive constraints
51    let mut powers_of_alpha = Alphas::<F>::default();
52
53    // Set up powers of alpha. Only the max number of constraints matters.
54    // The gate type argument can just be the zero gate.
55    powers_of_alpha.register(
56        ArgumentType::Gate(GateType::Zero),
57        VarbaseMul::<F>::CONSTRAINTS,
58    );
59
60    let mut cache = expr::Cache::default();
61
62    let mut expr = Poseidon::combined_constraints(&powers_of_alpha, &mut cache);
63    expr += VarbaseMul::combined_constraints(&powers_of_alpha, &mut cache);
64    expr += CompleteAdd::combined_constraints(&powers_of_alpha, &mut cache);
65    expr += EndosclMul::combined_constraints(&powers_of_alpha, &mut cache);
66    expr += EndomulScalar::combined_constraints(&powers_of_alpha, &mut cache);
67
68    {
69        let mut range_check0_expr =
70            || RangeCheck0::combined_constraints(&powers_of_alpha, &mut cache);
71
72        if let Some(feature_flags) = feature_flags {
73            if feature_flags.range_check0 {
74                expr += range_check0_expr();
75            }
76        } else {
77            expr += Expr::IfFeature(
78                FeatureFlag::RangeCheck0,
79                Box::new(range_check0_expr()),
80                Box::new(Expr::zero()),
81            );
82        }
83    }
84
85    {
86        let mut range_check1_expr =
87            || RangeCheck1::combined_constraints(&powers_of_alpha, &mut cache);
88
89        if let Some(feature_flags) = feature_flags {
90            if feature_flags.range_check1 {
91                expr += range_check1_expr();
92            }
93        } else {
94            expr += Expr::IfFeature(
95                FeatureFlag::RangeCheck1,
96                Box::new(range_check1_expr()),
97                Box::new(Expr::zero()),
98            );
99        }
100    }
101
102    {
103        let mut foreign_field_add_expr =
104            || ForeignFieldAdd::combined_constraints(&powers_of_alpha, &mut cache);
105        if let Some(feature_flags) = feature_flags {
106            if feature_flags.foreign_field_add {
107                expr += foreign_field_add_expr();
108            }
109        } else {
110            expr += Expr::IfFeature(
111                FeatureFlag::ForeignFieldAdd,
112                Box::new(foreign_field_add_expr()),
113                Box::new(Expr::zero()),
114            );
115        }
116    }
117
118    {
119        let mut foreign_field_mul_expr =
120            || ForeignFieldMul::combined_constraints(&powers_of_alpha, &mut cache);
121        if let Some(feature_flags) = feature_flags {
122            if feature_flags.foreign_field_mul {
123                expr += foreign_field_mul_expr();
124            }
125        } else {
126            expr += Expr::IfFeature(
127                FeatureFlag::ForeignFieldMul,
128                Box::new(foreign_field_mul_expr()),
129                Box::new(Expr::zero()),
130            );
131        }
132    }
133
134    {
135        let mut xor_expr = || xor::Xor16::combined_constraints(&powers_of_alpha, &mut cache);
136        if let Some(feature_flags) = feature_flags {
137            if feature_flags.xor {
138                expr += xor_expr();
139            }
140        } else {
141            expr += Expr::IfFeature(
142                FeatureFlag::Xor,
143                Box::new(xor_expr()),
144                Box::new(Expr::zero()),
145            );
146        }
147    }
148
149    {
150        let mut rot_expr = || rot::Rot64::combined_constraints(&powers_of_alpha, &mut cache);
151        if let Some(feature_flags) = feature_flags {
152            if feature_flags.rot {
153                expr += rot_expr();
154            }
155        } else {
156            expr += Expr::IfFeature(
157                FeatureFlag::Rot,
158                Box::new(rot_expr()),
159                Box::new(Expr::zero()),
160            );
161        }
162    }
163
164    if generic {
165        expr += generic::Generic::combined_constraints(&powers_of_alpha, &mut cache);
166    }
167
168    // permutation
169    powers_of_alpha.register(ArgumentType::Permutation, permutation::CONSTRAINTS);
170
171    // lookup
172    if let Some(feature_flags) = feature_flags {
173        if feature_flags.lookup_features.patterns != LookupPatterns::default() {
174            let lookup_configuration =
175                LookupConfiguration::new(LookupInfo::create(feature_flags.lookup_features));
176            let constraints = lookup::constraints::constraints(&lookup_configuration, false);
177
178            // note: the number of constraints depends on the lookup configuration,
179            // specifically the presence of runtime tables.
180            let constraints_len = u32::try_from(constraints.len())
181                .expect("we always expect a relatively low amount of constraints");
182
183            powers_of_alpha.register(ArgumentType::Lookup, constraints_len);
184
185            let alphas = powers_of_alpha.get_exponents(ArgumentType::Lookup, constraints_len);
186            let combined = Expr::combine_constraints(alphas, constraints);
187
188            expr += combined;
189        }
190    } else {
191        let all_features = LookupFeatures {
192            patterns: LookupPatterns {
193                xor: true,
194                lookup: true,
195                range_check: true,
196                foreign_field_mul: true,
197            },
198            uses_runtime_tables: true,
199            joint_lookup_used: true,
200        };
201        let lookup_configuration = LookupConfiguration::new(LookupInfo::create(all_features));
202        let constraints = lookup::constraints::constraints(&lookup_configuration, true);
203
204        // note: the number of constraints depends on the lookup configuration,
205        // specifically the presence of runtime tables.
206        let constraints_len = u32::try_from(constraints.len())
207            .expect("we always expect a relatively low amount of constraints");
208
209        powers_of_alpha.register(ArgumentType::Lookup, constraints_len);
210
211        let alphas = powers_of_alpha.get_exponents(ArgumentType::Lookup, constraints_len);
212        let combined = Expr::IfFeature(
213            FeatureFlag::LookupTables,
214            Box::new(Expr::combine_constraints(alphas, constraints)),
215            Box::new(Expr::zero()),
216        );
217
218        expr += combined;
219    }
220
221    // the generic gate must be associated with alpha^0
222    // to make the later addition with the public input work
223    if cfg!(debug_assertions) {
224        let mut generic_alphas =
225            powers_of_alpha.get_exponents(ArgumentType::Gate(GateType::Generic), 1);
226        assert_eq!(generic_alphas.next(), Some(0));
227    }
228
229    // Check that the feature flags correctly turn on or off the constraints generated by the given
230    // flags.
231    if cfg!(feature = "check_feature_flags") {
232        if let Some(feature_flags) = feature_flags {
233            let (feature_flagged_expr, _) = constraints_expr(None, generic);
234            let feature_flagged_expr = feature_flagged_expr.apply_feature_flags(feature_flags);
235            assert_eq!(expr, feature_flagged_expr);
236        }
237    }
238
239    // return the expression
240    (expr, powers_of_alpha)
241}
242
243/// Adds the polynomials that are evaluated as part of the proof
244/// for the linearization to work.
245pub fn linearization_columns<F: FftField>(
246    feature_flags: Option<&FeatureFlags>,
247) -> std::collections::HashSet<Column> {
248    let mut h = std::collections::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}