1use 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
38pub 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 let mut powers_of_alpha = Alphas::<F>::default();
52
53 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 powers_of_alpha.register(ArgumentType::Permutation, permutation::CONSTRAINTS);
170
171 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 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 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 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 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 (expr, powers_of_alpha)
241}
242
243pub 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 {
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 for i in 0..COLUMNS {
279 h.insert(Witness(i));
280 }
281
282 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 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 if lookup_info.features.uses_runtime_tables {
303 h.insert(LookupRuntimeTable);
304 }
305 }
306
307 h.insert(Z);
309
310 h.insert(Index(GateType::Poseidon));
312
313 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 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 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#[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}