1use 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
40pub 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 let mut powers_of_alpha = Alphas::<F>::default();
54
55 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 powers_of_alpha.register(ArgumentType::Permutation, permutation::CONSTRAINTS);
172
173 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 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 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 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 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 (expr, powers_of_alpha)
243}
244
245pub 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 {
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}