Skip to main content

stwo_gpu_constraint_framework/
lib.rs

1#![cfg_attr(feature = "prover", feature(portable_simd))]
2#![cfg_attr(not(feature = "std"), no_std)]
3
4/// ! This module contains helpers to express and use constraints for components.
5mod component;
6
7#[cfg(feature = "prover")]
8pub mod expr;
9mod info;
10pub mod logup;
11mod point;
12pub mod preprocessed_columns;
13#[cfg(all(feature = "prover", feature = "std"))]
14mod prover;
15
16use core::array;
17use core::fmt::Debug;
18use core::ops::{Add, AddAssign, Mul, Neg, Sub};
19
20pub use component::{FrameworkComponent, FrameworkEval, TraceLocationAllocator};
21pub use info::InfoEvaluator;
22use num_traits::{One, Zero};
23pub use point::PointEvaluator;
24use preprocessed_columns::PreProcessedColumnId;
25#[cfg(all(feature = "prover", feature = "std"))]
26pub use prover::{
27    assert_constraints_on_polys, assert_constraints_on_trace, relation_tracker, AssertEvaluator,
28    CpuDomainEvaluator, FractionWriter, LogupColGenerator, LogupTraceGenerator,
29    SimdDomainEvaluator,
30};
31
32// GPU constraint kernel configuration - allows users to enable/disable direct GPU kernels
33#[cfg(all(feature = "prover", feature = "std", feature = "gpu"))]
34pub use prover::{
35    is_gpu_constraint_kernels_enabled, set_gpu_constraint_kernels_enabled, will_use_gpu_kernels,
36    GpuDomainEvaluator,
37};
38use std_shims::Vec;
39use stwo::core::fields::m31::BaseField;
40use stwo::core::fields::qm31::{SecureField, SECURE_EXTENSION_DEGREE};
41use stwo::core::fields::FieldExpOps;
42use stwo::core::Fraction;
43
44#[rustfmt::skip]
45pub use stwo::core::verifier::PREPROCESSED_TRACE_IDX;
46pub const ORIGINAL_TRACE_IDX: usize = 1;
47pub const INTERACTION_TRACE_IDX: usize = 2;
48
49/// A vector that describes the batching of logup entries.
50/// Each vector member corresponds to a logup entry, and contains the batch number to which the
51/// entry should be added.
52/// Note that the batch numbers should be consecutive and start from 0, and that the vector's
53/// length should be equal to the number of logup entries.
54type Batching = Vec<usize>;
55
56/// A trait for evaluating expressions at some point or row.
57pub trait EvalAtRow {
58    // TODO(Ohad): Use a better trait for these, like 'Algebra' or something.
59    /// The field type holding values of columns for the component. These are the inputs to the
60    /// constraints. It might be [BaseField] packed types, or even [SecureField], when evaluating
61    /// the columns out of domain.
62    type F: FieldExpOps
63        + Clone
64        + Debug
65        + Zero
66        + Neg<Output = Self::F>
67        + AddAssign
68        + AddAssign<BaseField>
69        + Add<Self::F, Output = Self::F>
70        + Sub<Self::F, Output = Self::F>
71        + Mul<BaseField, Output = Self::F>
72        + Add<SecureField, Output = Self::EF>
73        + Mul<SecureField, Output = Self::EF>
74        + Neg<Output = Self::F>
75        + From<BaseField>;
76
77    /// A field type representing the closure of `F` with multiplying by [SecureField]. Constraints
78    /// usually get multiplied by [SecureField] values for security.
79    type EF: One
80        + Clone
81        + Debug
82        + Zero
83        + Neg<Output = Self::EF>
84        + AddAssign
85        + Add<BaseField, Output = Self::EF>
86        + Mul<BaseField, Output = Self::EF>
87        + Add<SecureField, Output = Self::EF>
88        + Sub<SecureField, Output = Self::EF>
89        + Mul<SecureField, Output = Self::EF>
90        + Add<Self::F, Output = Self::EF>
91        + Mul<Self::F, Output = Self::EF>
92        + Sub<Self::EF, Output = Self::EF>
93        + Mul<Self::EF, Output = Self::EF>
94        + From<SecureField>
95        + From<Self::F>;
96
97    /// Returns the next mask value for the first interaction at offset 0.
98    fn next_trace_mask(&mut self) -> Self::F {
99        let [mask_item] = self.next_interaction_mask(ORIGINAL_TRACE_IDX, [0]);
100        mask_item
101    }
102
103    fn get_preprocessed_column(&mut self, _column: PreProcessedColumnId) -> Self::F {
104        let [mask_item] = self.next_interaction_mask(PREPROCESSED_TRACE_IDX, [0]);
105        mask_item
106    }
107
108    /// Returns the mask values of the given offsets for the next column in the interaction.
109    fn next_interaction_mask<const N: usize>(
110        &mut self,
111        interaction: usize,
112        offsets: [isize; N],
113    ) -> [Self::F; N];
114
115    /// Returns the extension mask values of the given offsets for the next extension degree many
116    /// columns in the interaction.
117    fn next_extension_interaction_mask<const N: usize>(
118        &mut self,
119        interaction: usize,
120        offsets: [isize; N],
121    ) -> [Self::EF; N] {
122        let mut res_col_major =
123            array::from_fn(|_| self.next_interaction_mask(interaction, offsets).into_iter());
124        array::from_fn(|_| {
125            Self::combine_ef(res_col_major.each_mut().map(|iter| iter.next().unwrap()))
126        })
127    }
128
129    /// Adds a constraint to the component.
130    fn add_constraint<G>(&mut self, constraint: G)
131    where
132        Self::EF: Mul<G, Output = Self::EF> + From<G>;
133
134    /// Adds an intermediate value in the base field to the component and returns its value.
135    /// Does nothing by default.
136    fn add_intermediate(&mut self, val: Self::F) -> Self::F {
137        val
138    }
139
140    /// Adds an intermediate value in the extension field to the component and returns its value.
141    /// Does nothing by default.
142    fn add_extension_intermediate(&mut self, val: Self::EF) -> Self::EF {
143        val
144    }
145
146    /// Combines 4 base field values into a single extension field value.
147    fn combine_ef(values: [Self::F; SECURE_EXTENSION_DEGREE]) -> Self::EF;
148
149    /// Adds `entry.values` to `entry.relation` with `entry.multiplicity` for all 'entry' in
150    /// 'entries', batched together.
151    /// Constraint degree increases with number of batched constraints as the denominators are
152    /// multiplied.
153    fn add_to_relation<R: Relation<Self::F, Self::EF>>(
154        &mut self,
155        entry: RelationEntry<'_, Self::F, Self::EF, R>,
156    ) {
157        let frac = Fraction::new(
158            entry.multiplicity.clone(),
159            entry.relation.combine(entry.values),
160        );
161        self.write_logup_frac(frac);
162    }
163
164    // TODO(alont): Remove these once LogupAtRow is no longer used.
165    fn write_logup_frac(&mut self, _fraction: Fraction<Self::EF, Self::EF>) {
166        unimplemented!()
167    }
168    fn finalize_logup_batched(&mut self, _batching: &Batching) {
169        unimplemented!()
170    }
171
172    fn finalize_logup(&mut self) {
173        unimplemented!();
174    }
175
176    fn finalize_logup_in_pairs(&mut self) {
177        unimplemented!();
178    }
179}
180
181/// Default implementation for evaluators that have an element called "logup" that works like a
182/// LogupAtRow, where the logup functionality can be proxied.
183/// TODO(alont): Remove once LogupAtRow is no longer used.
184macro_rules! logup_proxy {
185    () => {
186        fn write_logup_frac(&mut self, fraction: Fraction<Self::EF, Self::EF>) {
187            if self.logup.fracs.is_empty() {
188                self.logup.is_finalized = false;
189            }
190            self.logup.fracs.push(fraction.clone());
191        }
192
193        /// Finalize the logup by adding the constraints for the fractions, batched by
194        /// the given `batching`.
195        /// `batching` should contain the batch into which every logup entry should be inserted.
196        fn finalize_logup_batched(&mut self, batching: &crate::Batching) {
197            assert!(!self.logup.is_finalized, "LogupAtRow was already finalized");
198            assert_eq!(
199                batching.len(),
200                self.logup.fracs.len(),
201                "Batching must be of the same length as the number of entries"
202            );
203
204            let last_batch = *batching.iter().max().unwrap();
205
206            let mut fracs_by_batch =
207                hashbrown::HashMap::<usize, std_shims::Vec<Fraction<Self::EF, Self::EF>>>::new();
208
209            for (batch, frac) in batching.iter().zip(self.logup.fracs.iter()) {
210                fracs_by_batch
211                    .entry(*batch)
212                    .or_insert_with(std_shims::Vec::new)
213                    .push(frac.clone());
214            }
215
216            let keys_set: hashbrown::HashSet<_> = fracs_by_batch.keys().cloned().collect();
217            let all_batches_set: hashbrown::HashSet<_> = (0..last_batch + 1).collect();
218
219            assert_eq!(
220                keys_set, all_batches_set,
221                "Batching must contain all consecutive batches"
222            );
223
224            let mut prev_col_cumsum = <Self::EF as num_traits::Zero>::zero();
225
226            // All batches except the last are cumulatively summed in new interaction columns.
227            for batch_id in (0..last_batch) {
228                let cur_frac: Fraction<_, _> = fracs_by_batch[&batch_id].iter().cloned().sum();
229                let [cur_cumsum] =
230                    self.next_extension_interaction_mask(self.logup.interaction, [0]);
231                let diff = cur_cumsum.clone() - prev_col_cumsum.clone();
232                prev_col_cumsum = cur_cumsum;
233                self.add_constraint(diff * cur_frac.denominator - cur_frac.numerator);
234            }
235
236            let frac: Fraction<_, _> = fracs_by_batch[&last_batch].clone().into_iter().sum();
237            let [prev_row_cumsum, cur_cumsum] =
238                self.next_extension_interaction_mask(self.logup.interaction, [-1, 0]);
239
240            let diff = cur_cumsum - prev_row_cumsum - prev_col_cumsum.clone();
241            // Instead of checking diff = num / denom, check diff = num / denom - cumsum_shift.
242            // This makes (num / denom - cumsum_shift) have sum zero, which makes the constraint
243            // uniform - apply on all rows.
244            let shifted_diff = diff + self.logup.cumsum_shift.clone();
245
246            self.add_constraint(shifted_diff * frac.denominator - frac.numerator);
247
248            self.logup.is_finalized = true;
249        }
250
251        /// Finalizes the row's logup in the default way. Currently, this means no batching.
252        fn finalize_logup(&mut self) {
253            let batches = (0..self.logup.fracs.len()).collect();
254            self.finalize_logup_batched(&batches)
255        }
256
257        /// Finalizes the row's logup, batched in pairs.
258        /// TODO(alont) Remove this once a better batching mechanism is implemented.
259        fn finalize_logup_in_pairs(&mut self) {
260            let batches = (0..self.logup.fracs.len()).map(|n| n / 2).collect();
261            self.finalize_logup_batched(&batches)
262        }
263    };
264}
265pub(crate) use logup_proxy;
266
267pub trait RelationEFTraitBound<F: Clone>:
268    Clone + Zero + From<F> + From<SecureField> + Mul<F, Output = Self> + Sub<Self, Output = Self>
269{
270}
271
272impl<F, EF> RelationEFTraitBound<F> for EF
273where
274    F: Clone,
275    EF: Clone + Zero + From<F> + From<SecureField> + Mul<F, Output = EF> + Sub<EF, Output = EF>,
276{
277}
278
279/// A trait for defining a logup relation type.
280pub trait Relation<F: Clone, EF: RelationEFTraitBound<F>>: Sized {
281    fn combine(&self, values: &[F]) -> EF;
282
283    fn get_name(&self) -> &str;
284    fn get_size(&self) -> usize;
285}
286
287/// A struct representing a relation entry.
288/// `relation` is the relation into which elements are entered.
289/// `multiplicity` is the multiplicity of the elements.
290///     A positive multiplicity is used to signify a "use", while a negative multiplicity
291///     signifies a "yield".
292/// `values` are elements in the base field that are entered into the relation.
293pub struct RelationEntry<'a, F: Clone, EF: RelationEFTraitBound<F>, R: Relation<F, EF>> {
294    relation: &'a R,
295    multiplicity: EF,
296    values: &'a [F],
297}
298impl<'a, F: Clone, EF: RelationEFTraitBound<F>, R: Relation<F, EF>> RelationEntry<'a, F, EF, R> {
299    pub const fn new(relation: &'a R, multiplicity: EF, values: &'a [F]) -> Self {
300        Self {
301            relation,
302            multiplicity,
303            values,
304        }
305    }
306}
307
308#[macro_export]
309macro_rules! relation {
310    ($name:tt, $size:tt) => {
311        #[derive(Clone, Debug, PartialEq)]
312        pub struct $name($crate::logup::LookupElements<$size>);
313
314        #[allow(dead_code)]
315        impl $name {
316            pub fn dummy() -> Self {
317                Self($crate::logup::LookupElements::dummy())
318            }
319            pub fn draw(channel: &mut impl stwo::core::channel::Channel) -> Self {
320                Self($crate::logup::LookupElements::draw(channel))
321            }
322        }
323
324        impl<F: Clone, EF: $crate::RelationEFTraitBound<F>> $crate::Relation<F, EF> for $name {
325            fn combine(&self, values: &[F]) -> EF {
326                self.0.combine(values)
327            }
328
329            fn get_name(&self) -> &str {
330                stringify!($name)
331            }
332
333            fn get_size(&self) -> usize {
334                $size
335            }
336        }
337    };
338}
339
340#[cfg(test)]
341#[macro_export]
342macro_rules! m31 {
343    ($m:expr) => {
344        stwo::core::fields::m31::M31::from_u32_unchecked($m)
345    };
346}
347
348#[cfg(test)]
349#[macro_export]
350macro_rules! qm31 {
351    ($m0:expr, $m1:expr, $m2:expr, $m3:expr) => {{
352        stwo::core::fields::qm31::QM31::from_u32_unchecked($m0, $m1, $m2, $m3)
353    }};
354}