Skip to content

Commit ed0c692

Browse files
committed
update constrain eval opt
1 parent 64bad0e commit ed0c692

37 files changed

Lines changed: 625 additions & 856 deletions

crypto/stark/src/constraints/evaluator.rs

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ where
5151
logup_table_offset: &FieldElement<FieldExtension>,
5252
) -> Vec<FieldElement<FieldExtension>> {
5353
let is_uniform = zerofier_data.is_uniform();
54+
let num_base = air.num_base_transition_constraints();
5455

5556
// Pre-compute LogUp alpha powers once for all LDE domain points.
5657
let logup_alpha_powers: Vec<FieldElement<FieldExtension>> =
@@ -86,6 +87,7 @@ where
8687
|| {
8788
(
8889
vec![FieldElement::<FieldExtension>::zero(); num_transition],
90+
vec![FieldElement::<Field>::zero(); num_base],
8991
vec![FieldElement::<Field>::zero(); num_periodic],
9092
Frame::preallocate(
9193
num_offsets,
@@ -95,7 +97,7 @@ where
9597
),
9698
)
9799
},
98-
|(transition_buf, periodic_buf, frame), (i, boundary)| {
100+
|(transition_buf, base_buf, periodic_buf, frame), (i, boundary)| {
99101
frame.fill_from_lde(lde_trace, i, offsets);
100102

101103
for (j, col) in lde_periodic_columns.iter().enumerate() {
@@ -110,24 +112,38 @@ where
110112
logup_table_offset,
111113
&packing_shifts,
112114
);
113-
air.compute_transition_into(&ctx, transition_buf);
115+
air.compute_transition_prover(&ctx, base_buf, transition_buf);
114116

115117
let acc_transition = if is_uniform {
116118
// All constraints share one zerofier: factor it out of the sum.
117119
let z = zerofier_data.get_uniform(i);
118-
let sum = transition_buf
120+
// F×E inner product for base constraints (3 muls per term)
121+
let mut sum = base_buf
119122
.iter()
120-
.zip(transition_coefficients)
123+
.zip(&transition_coefficients[..num_base])
121124
.fold(FieldElement::zero(), |acc, (eval, beta)| acc + eval * beta);
125+
// E×E for extension constraints (9 muls per term)
126+
sum = transition_buf[num_base..]
127+
.iter()
128+
.zip(&transition_coefficients[num_base..])
129+
.fold(sum, |acc, (eval, beta)| acc + eval * beta);
122130
z * &sum
123131
} else {
124-
transition_buf
132+
let mut sum = base_buf
125133
.iter()
126134
.enumerate()
127-
.zip(transition_coefficients)
135+
.zip(&transition_coefficients[..num_base])
128136
.fold(FieldElement::zero(), |acc, ((c_idx, eval), beta)| {
129137
acc + zerofier_data.get(c_idx, i) * eval * beta
130-
})
138+
});
139+
sum = transition_buf[num_base..]
140+
.iter()
141+
.enumerate()
142+
.zip(&transition_coefficients[num_base..])
143+
.fold(sum, |acc, ((j, eval), beta)| {
144+
acc + zerofier_data.get(num_base + j, i) * eval * beta
145+
});
146+
sum
131147
};
132148

133149
acc_transition + boundary
@@ -140,6 +156,7 @@ where
140156
#[cfg(not(feature = "parallel"))]
141157
{
142158
let mut transition_buf = vec![FieldElement::<FieldExtension>::zero(); num_transition];
159+
let mut base_buf = vec![FieldElement::<Field>::zero(); num_base];
143160
let mut periodic_buf = vec![FieldElement::<Field>::zero(); num_periodic];
144161
let mut frame =
145162
Frame::preallocate(num_offsets, rows_per_step, num_main_cols, num_aux_cols);
@@ -162,23 +179,37 @@ where
162179
logup_table_offset,
163180
&packing_shifts,
164181
);
165-
air.compute_transition_into(&ctx, &mut transition_buf);
182+
air.compute_transition_prover(&ctx, &mut base_buf, &mut transition_buf);
166183

167184
let acc_transition = if is_uniform {
168185
let z = zerofier_data.get_uniform(i);
169-
let sum = transition_buf
186+
// F×E inner product for base constraints (3 muls per term)
187+
let mut sum = base_buf
170188
.iter()
171-
.zip(transition_coefficients)
189+
.zip(&transition_coefficients[..num_base])
172190
.fold(FieldElement::zero(), |acc, (eval, beta)| acc + eval * beta);
191+
// E×E for extension constraints (9 muls per term)
192+
sum = transition_buf[num_base..]
193+
.iter()
194+
.zip(&transition_coefficients[num_base..])
195+
.fold(sum, |acc, (eval, beta)| acc + eval * beta);
173196
z * &sum
174197
} else {
175-
transition_buf
198+
let mut sum = base_buf
176199
.iter()
177200
.enumerate()
178-
.zip(transition_coefficients)
201+
.zip(&transition_coefficients[..num_base])
179202
.fold(FieldElement::zero(), |acc, ((c_idx, eval), beta)| {
180203
acc + zerofier_data.get(c_idx, i) * eval * beta
181-
})
204+
});
205+
sum = transition_buf[num_base..]
206+
.iter()
207+
.enumerate()
208+
.zip(&transition_coefficients[num_base..])
209+
.fold(sum, |acc, ((j, eval), beta)| {
210+
acc + zerofier_data.get(num_base + j, i) * eval * beta
211+
});
212+
sum
182213
};
183214

184215
acc_transition + boundary

crypto/stark/src/constraints/transition.rs

Lines changed: 166 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ use math::field::element::FieldElement;
77
use math::field::traits::{IsFFTField, IsField, IsSubFieldOf};
88
use math::polynomial::Polynomial;
99

10-
/// TransitionConstraint represents the behaviour that a transition constraint
10+
/// TransitionConstraintEvaluator represents the behaviour that a transition constraint
1111
/// over the computation that wants to be proven must comply with.
12-
pub trait TransitionConstraint<F, E>: Send + Sync
12+
pub trait TransitionConstraintEvaluator<F, E>: Send + Sync
1313
where
1414
F: IsSubFieldOf<E> + IsFFTField + Send + Sync,
1515
E: IsField + Send + Sync,
@@ -30,7 +30,7 @@ where
3030
/// the evaluation.
3131
/// Once computed, the evaluation should be inserted in the `transition_evaluations`
3232
/// vector, in the index corresponding to the constraint as given by `constraint_idx()`.
33-
fn evaluate(
33+
fn evaluate_verifier(
3434
&self,
3535
evaluation_context: &TransitionEvaluationContext<F, E>,
3636
transition_evaluations: &mut [FieldElement<E>],
@@ -84,6 +84,27 @@ where
8484
0
8585
}
8686

87+
/// Prover-optimized evaluation that writes base-field constraints to `base_evals`
88+
/// and extension-field constraints to `ext_evals`.
89+
///
90+
/// Constraints with `constraint_idx() < base_evals.len()` are "base" constraints
91+
/// and MUST override this to write `FieldElement<F>` into `base_evals[constraint_idx()]`.
92+
/// Extension constraints (LogUp etc.) use the default, which asserts the index is
93+
/// in the extension range and delegates to `evaluate()`.
94+
fn evaluate_prover(
95+
&self,
96+
evaluation_context: &TransitionEvaluationContext<F, E>,
97+
base_evals: &mut [FieldElement<F>],
98+
ext_evals: &mut [FieldElement<E>],
99+
) {
100+
debug_assert!(
101+
self.constraint_idx() >= base_evals.len(),
102+
"Base constraint idx {} must override evaluate_prover()",
103+
self.constraint_idx(),
104+
);
105+
self.evaluate_verifier(evaluation_context, ext_evals);
106+
}
107+
87108
/// Method for calculating the end exemptions polynomial.
88109
///
89110
/// This polynomial is used to compute zerofiers of the constraint, and the default
@@ -267,3 +288,145 @@ where
267288
* end_exemptions_poly.evaluate(z)
268289
}
269290
}
291+
292+
// =============================================================================
293+
// User-facing TransitionConstraint trait + adapter
294+
// =============================================================================
295+
296+
use crate::table::TableView;
297+
298+
/// User-facing trait for defining transition constraints.
299+
///
300+
/// Implement `evaluate()` to define the polynomial identity; the verifier and
301+
/// prover evaluation paths are auto-generated via `.boxed()`.
302+
///
303+
/// The `evaluate` method is generic over its field types so the same polynomial
304+
/// works for both the prover (`TableView<F, E>`) and verifier (`TableView<E, E>`).
305+
pub trait TransitionConstraint<F, E>: Send + Sync
306+
where
307+
F: IsSubFieldOf<E> + IsFFTField + Send + Sync,
308+
E: IsField + Send + Sync,
309+
{
310+
/// The degree of the constraint as a multivariate polynomial.
311+
fn degree(&self) -> usize;
312+
313+
/// Unique index in `[0, N)` where N is the total number of transition constraints.
314+
fn constraint_idx(&self) -> usize;
315+
316+
/// Number of exempted rows at the end of the trace.
317+
fn end_exemptions(&self) -> usize {
318+
0
319+
}
320+
321+
/// Evaluate the constraint polynomial on a trace step.
322+
///
323+
/// Generic over the field so the same polynomial works for both
324+
/// prover (FF=F, returns FieldElement<F>) and verifier (FF=E, returns FieldElement<E>).
325+
fn evaluate<FF, EE>(&self, step: &TableView<FF, EE>) -> FieldElement<FF>
326+
where
327+
FF: IsSubFieldOf<EE>,
328+
EE: IsField;
329+
330+
/// Periodicity (default 1 = every row).
331+
fn period(&self) -> usize {
332+
1
333+
}
334+
335+
/// Offset for periodic application (default 0).
336+
fn offset(&self) -> usize {
337+
0
338+
}
339+
340+
/// Exemptions period (default None).
341+
fn exemptions_period(&self) -> Option<usize> {
342+
None
343+
}
344+
345+
/// Offset for periodic exemptions (default None).
346+
fn periodic_exemptions_offset(&self) -> Option<usize> {
347+
None
348+
}
349+
350+
/// Wrap into a boxed `TransitionConstraintEvaluator` for the evaluator.
351+
///
352+
/// The adapter auto-generates `evaluate_verifier()` and `evaluate_prover()`
353+
/// from the generic `evaluate()`.
354+
fn boxed(self) -> Box<dyn TransitionConstraintEvaluator<F, E>>
355+
where
356+
Self: Sized + 'static,
357+
{
358+
Box::new(TransitionConstraintAdapter(self))
359+
}
360+
}
361+
362+
/// Adapter: implements `TransitionConstraintEvaluator` for any `TransitionConstraint`.
363+
///
364+
/// Auto-generates `evaluate_verifier()` (E×E path) and `evaluate_prover()` (F path)
365+
/// from the user's generic `evaluate()`.
366+
pub struct TransitionConstraintAdapter<T>(pub T);
367+
368+
impl<T, F, E> TransitionConstraintEvaluator<F, E> for TransitionConstraintAdapter<T>
369+
where
370+
T: TransitionConstraint<F, E> + 'static,
371+
F: IsSubFieldOf<E> + IsFFTField + Send + Sync,
372+
E: IsField + Send + Sync,
373+
{
374+
fn degree(&self) -> usize {
375+
self.0.degree()
376+
}
377+
fn constraint_idx(&self) -> usize {
378+
self.0.constraint_idx()
379+
}
380+
fn end_exemptions(&self) -> usize {
381+
self.0.end_exemptions()
382+
}
383+
fn period(&self) -> usize {
384+
self.0.period()
385+
}
386+
fn offset(&self) -> usize {
387+
self.0.offset()
388+
}
389+
fn exemptions_period(&self) -> Option<usize> {
390+
self.0.exemptions_period()
391+
}
392+
fn periodic_exemptions_offset(&self) -> Option<usize> {
393+
self.0.periodic_exemptions_offset()
394+
}
395+
396+
fn evaluate_verifier(
397+
&self,
398+
ctx: &TransitionEvaluationContext<F, E>,
399+
evals: &mut [FieldElement<E>],
400+
) {
401+
let idx = self.0.constraint_idx();
402+
match ctx {
403+
TransitionEvaluationContext::Prover { frame, .. } => {
404+
evals[idx] = self.0.evaluate(frame.get_evaluation_step(0)).to_extension();
405+
}
406+
TransitionEvaluationContext::Verifier { frame, .. } => {
407+
evals[idx] = self.0.evaluate(frame.get_evaluation_step(0));
408+
}
409+
}
410+
}
411+
412+
fn evaluate_prover(
413+
&self,
414+
ctx: &TransitionEvaluationContext<F, E>,
415+
base_evals: &mut [FieldElement<F>],
416+
ext_evals: &mut [FieldElement<E>],
417+
) {
418+
let idx = self.0.constraint_idx();
419+
if idx < base_evals.len() {
420+
// Base-field fast path: write FieldElement<F> directly
421+
if let TransitionEvaluationContext::Prover { frame, .. } = ctx {
422+
base_evals[idx] = self.0.evaluate(frame.get_evaluation_step(0));
423+
} else {
424+
unreachable!("evaluate_prover called with non-Prover context");
425+
}
426+
} else {
427+
// Fallback: AIR did not opt into base-field splitting,
428+
// delegate to the verifier path which writes E evals.
429+
self.evaluate_verifier(ctx, ext_evals);
430+
}
431+
}
432+
}

crypto/stark/src/examples/bit_flags.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{
2-
constraints::{boundary::BoundaryConstraints, transition::TransitionConstraint},
2+
constraints::{boundary::BoundaryConstraints, transition::TransitionConstraintEvaluator},
33
context::AirContext,
44
proof::options::ProofOptions,
55
trace::TraceTable,
@@ -18,7 +18,7 @@ impl BitConstraint {
1818
}
1919
}
2020

21-
impl TransitionConstraint<StarkField, StarkField> for BitConstraint {
21+
impl TransitionConstraintEvaluator<StarkField, StarkField> for BitConstraint {
2222
fn degree(&self) -> usize {
2323
2
2424
}
@@ -35,7 +35,7 @@ impl TransitionConstraint<StarkField, StarkField> for BitConstraint {
3535
Some(15)
3636
}
3737

38-
fn evaluate(
38+
fn evaluate_verifier(
3939
&self,
4040
evaluation_context: &TransitionEvaluationContext<StarkField, StarkField>,
4141
transition_evaluations: &mut [FieldElement<StarkField>],
@@ -78,7 +78,7 @@ impl ZeroFlagConstraint {
7878
}
7979
}
8080

81-
impl TransitionConstraint<StarkField, StarkField> for ZeroFlagConstraint {
81+
impl TransitionConstraintEvaluator<StarkField, StarkField> for ZeroFlagConstraint {
8282
fn degree(&self) -> usize {
8383
1
8484
}
@@ -91,7 +91,7 @@ impl TransitionConstraint<StarkField, StarkField> for ZeroFlagConstraint {
9191
16
9292
}
9393

94-
fn evaluate(
94+
fn evaluate_verifier(
9595
&self,
9696
evaluation_context: &TransitionEvaluationContext<StarkField, StarkField>,
9797
transition_evaluations: &mut [FieldElement<StarkField>],
@@ -120,7 +120,7 @@ impl TransitionConstraint<StarkField, StarkField> for ZeroFlagConstraint {
120120

121121
pub struct BitFlagsAIR {
122122
context: AirContext,
123-
constraints: Vec<Box<dyn TransitionConstraint<StarkField, StarkField>>>,
123+
constraints: Vec<Box<dyn TransitionConstraintEvaluator<StarkField, StarkField>>>,
124124
}
125125

126126
impl AIR for BitFlagsAIR {
@@ -135,7 +135,7 @@ impl AIR for BitFlagsAIR {
135135
fn new(proof_options: &ProofOptions) -> Self {
136136
let bit_constraint = Box::new(BitConstraint::new());
137137
let flag_constraint = Box::new(ZeroFlagConstraint::new());
138-
let constraints: Vec<Box<dyn TransitionConstraint<Self::Field, Self::FieldExtension>>> =
138+
let constraints: Vec<Box<dyn TransitionConstraintEvaluator<Self::Field, Self::FieldExtension>>> =
139139
vec![bit_constraint, flag_constraint];
140140

141141
let num_transition_constraints = constraints.len();
@@ -155,7 +155,7 @@ impl AIR for BitFlagsAIR {
155155

156156
fn transition_constraints(
157157
&self,
158-
) -> &Vec<Box<dyn TransitionConstraint<Self::Field, Self::FieldExtension>>> {
158+
) -> &Vec<Box<dyn TransitionConstraintEvaluator<Self::Field, Self::FieldExtension>>> {
159159
&self.constraints
160160
}
161161

0 commit comments

Comments
 (0)