winter_air/air/transition/mod.rs
1// Copyright (c) Facebook, Inc. and its affiliates.
2//
3// This source code is licensed under the MIT license found in the
4// LICENSE file in the root directory of this source tree.
5
6use alloc::vec::Vec;
7
8use super::{AirContext, ConstraintDivisor, ExtensionOf, FieldElement};
9
10mod frame;
11pub use frame::EvaluationFrame;
12
13mod degree;
14pub use degree::TransitionConstraintDegree;
15
16// CONSTANTS
17// ================================================================================================
18
19const MIN_CYCLE_LENGTH: usize = 2;
20
21// TRANSITION CONSTRAINTS INFO
22// ================================================================================================
23
24/// Metadata for transition constraints of a computation.
25///
26/// This metadata includes:
27/// - List of transition constraint degrees for the main trace segment, as well as for auxiliary
28/// trace segments (if any).
29/// - Groupings of random composition constraint coefficients separately for the main trace segment
30/// and for auxiliary tace segment.
31/// - Divisor of transition constraints for a computation.
32pub struct TransitionConstraints<E: FieldElement> {
33 main_constraint_coef: Vec<E>,
34 main_constraint_degrees: Vec<TransitionConstraintDegree>,
35 aux_constraint_coef: Vec<E>,
36 aux_constraint_degrees: Vec<TransitionConstraintDegree>,
37 divisor: ConstraintDivisor<E::BaseField>,
38}
39
40impl<E: FieldElement> TransitionConstraints<E> {
41 // CONSTRUCTOR
42 // --------------------------------------------------------------------------------------------
43 /// Returns a new instance of [TransitionConstraints] for a computation described by the
44 /// specified AIR context.
45 ///
46 /// # Panics
47 /// Panics if the number of transition constraints in the context does not match the number of
48 /// provided composition coefficients.
49 pub fn new(context: &AirContext<E::BaseField>, composition_coefficients: &[E]) -> Self {
50 assert_eq!(
51 context.num_transition_constraints(),
52 composition_coefficients.len(),
53 "number of transition constraints must match the number of composition coefficient tuples"
54 );
55
56 // build constraint divisor; the same divisor applies to all transition constraints
57 let divisor = ConstraintDivisor::from_transition(
58 context.trace_len(),
59 context.num_transition_exemptions(),
60 );
61
62 let main_constraint_degrees = context.main_transition_constraint_degrees.clone();
63 let aux_constraint_degrees = context.aux_transition_constraint_degrees.clone();
64
65 let (main_constraint_coef, aux_constraint_coef) =
66 composition_coefficients.split_at(context.main_transition_constraint_degrees.len());
67 Self {
68 main_constraint_coef: main_constraint_coef.to_vec(),
69 main_constraint_degrees,
70 aux_constraint_coef: aux_constraint_coef.to_vec(),
71 aux_constraint_degrees,
72 divisor,
73 }
74 }
75
76 // PUBLIC ACCESSORS
77 // --------------------------------------------------------------------------------------------
78
79 /// Returns a list of transition constraint degree descriptors for the main trace segment of
80 /// a computation.
81 ///
82 /// This list will be identical to the list passed into the [AirContext::new()] method as
83 /// the `transition_constraint_degrees` parameter, or into [AirContext::new_multi_segment()]
84 /// as the `main_transition_constraint_degrees` parameter.
85 pub fn main_constraint_degrees(&self) -> &[TransitionConstraintDegree] {
86 &self.main_constraint_degrees
87 }
88
89 /// Returns the number of constraints applied against the main trace segment of a computation.
90 pub fn num_main_constraints(&self) -> usize {
91 self.main_constraint_degrees.len()
92 }
93
94 /// Returns the random coefficients for constraints applied against main trace segment of a
95 /// computation.
96 pub fn main_constraint_coef(&self) -> Vec<E> {
97 self.main_constraint_coef.clone()
98 }
99
100 /// Returns a list of transition constraint degree descriptors for the auxiliary trace segment
101 /// of a computation.
102 ///
103 /// This list will be identical to the list passed into [AirContext::new_multi_segment()]
104 /// as the `aux_transition_constraint_degrees` parameter.
105 pub fn aux_constraint_degrees(&self) -> &[TransitionConstraintDegree] {
106 &self.aux_constraint_degrees
107 }
108
109 /// Returns the number of constraints applied against the auxiliary trace segment of a
110 /// computation.
111 pub fn num_aux_constraints(&self) -> usize {
112 self.aux_constraint_degrees.len()
113 }
114
115 /// Returns the random coefficients for constraints applied against the auxiliary trace segment
116 /// of a computation.
117 pub fn aux_constraint_coef(&self) -> Vec<E> {
118 self.aux_constraint_coef.clone()
119 }
120
121 /// Returns a divisor for transition constraints.
122 ///
123 /// All transition constraints have the same divisor which has the form:
124 /// $$
125 /// z(x) = \frac{x^n - 1}{x - g^{n - 1}}
126 /// $$
127 /// where: $n$ is the length of the execution trace and $g$ is the generator of the trace
128 /// domain.
129 ///
130 /// This divisor specifies that transition constraints must hold on all steps of the
131 /// execution trace except for the last one.
132 pub fn divisor(&self) -> &ConstraintDivisor<E::BaseField> {
133 &self.divisor
134 }
135
136 // CONSTRAINT COMPOSITION
137 // --------------------------------------------------------------------------------------------
138
139 /// Computes a linear combination of all transition constraint evaluations and divides the
140 /// result by transition constraint divisor.
141 ///
142 /// A transition constraint is described by a rational function of the form $\frac{C(x)}{z(x)}$,
143 /// where:
144 /// * $C(x)$ is the constraint polynomial.
145 /// * $z(x)$ is the constraint divisor polynomial.
146 ///
147 /// Thus, this function computes a linear combination of $C(x)$ evaluations.
148 ///
149 /// Since, the divisor polynomial is the same for all transition constraints (see
150 /// [ConstraintDivisor::from_transition]), we can divide the linear combination by the
151 /// divisor rather than dividing each individual $C(x)$ evaluation. This requires executing only
152 /// one division at the end.
153 pub fn combine_evaluations<F>(&self, main_evaluations: &[F], aux_evaluations: &[E], x: F) -> E
154 where
155 F: FieldElement<BaseField = E::BaseField>,
156 E: ExtensionOf<F>,
157 {
158 // merge constraint evaluations for the main trace segment
159 let mut result = main_evaluations
160 .iter()
161 .zip(self.main_constraint_coef.iter())
162 .fold(E::ZERO, |acc, (&const_eval, &coef)| acc + coef.mul_base(const_eval));
163
164 if !self.aux_constraint_coef.is_empty() {
165 result += aux_evaluations
166 .iter()
167 .zip(self.aux_constraint_coef.iter())
168 .fold(E::ZERO, |acc, (&const_eval, &coef)| acc + coef * const_eval);
169 };
170 // divide out the evaluation of divisor at x and return the result
171 let z = E::from(self.divisor.evaluate_at(x));
172
173 result / z
174 }
175}