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}