arnaucube
arnaucube/miden-cryptoPublic Self-hosted repository
miden-crypto/src/gkr/multivariate/mod.rs
src/gkr/multivariate/mod.rsRaw
  1use core::ops::Index;
  2
  3use alloc::sync::Arc;
  4use winter_math::{fields::f64::BaseElement, log2, FieldElement, StarkField};
  5
  6mod eq_poly;
  7pub use eq_poly::EqPolynomial;
  8
  9#[derive(Clone, Debug)]
 10pub struct MultiLinear<E: FieldElement> {
 11    pub num_variables: usize,
 12    pub evaluations: Vec<E>,
 13}
 14
 15impl<E: FieldElement> MultiLinear<E> {
 16    pub fn new(values: Vec<E>) -> Self {
 17        Self {
 18            num_variables: log2(values.len()) as usize,
 19            evaluations: values,
 20        }
 21    }
 22
 23    pub fn from_values(values: &[E]) -> Self {
 24        Self {
 25            num_variables: log2(values.len()) as usize,
 26            evaluations: values.to_owned(),
 27        }
 28    }
 29
 30    pub fn num_variables(&self) -> usize {
 31        self.num_variables
 32    }
 33
 34    pub fn evaluations(&self) -> &[E] {
 35        &self.evaluations
 36    }
 37
 38    pub fn len(&self) -> usize {
 39        self.evaluations.len()
 40    }
 41
 42    pub fn evaluate(&self, query: &[E]) -> E {
 43        let tensored_query = tensorize(query);
 44        inner_product(&self.evaluations, &tensored_query)
 45    }
 46
 47    pub fn bind(&self, round_challenge: E) -> Self {
 48        let mut result = vec![E::ZERO; 1 << (self.num_variables() - 1)];
 49        for i in 0..(1 << (self.num_variables() - 1)) {
 50            result[i] = self.evaluations[i << 1]
 51                + round_challenge * (self.evaluations[(i << 1) + 1] - self.evaluations[i << 1]);
 52        }
 53        Self::from_values(&result)
 54    }
 55
 56    pub fn bind_assign(&mut self, round_challenge: E) {
 57        let mut result = vec![E::ZERO; 1 << (self.num_variables() - 1)];
 58        for i in 0..(1 << (self.num_variables() - 1)) {
 59            result[i] = self.evaluations[i << 1]
 60                + round_challenge * (self.evaluations[(i << 1) + 1] - self.evaluations[i << 1]);
 61        }
 62        *self = Self::from_values(&result);
 63    }
 64
 65    pub fn split(&self, at: usize) -> (Self, Self) {
 66        assert!(at < self.len());
 67        (
 68            Self::new(self.evaluations[..at].to_vec()),
 69            Self::new(self.evaluations[at..2 * at].to_vec()),
 70        )
 71    }
 72
 73    pub fn extend(&mut self, other: &MultiLinear<E>) {
 74        let other_vec = other.evaluations.to_vec();
 75        assert_eq!(other_vec.len(), self.len());
 76        self.evaluations.extend(other_vec);
 77        self.num_variables += 1;
 78    }
 79}
 80
 81impl<E: FieldElement> Index<usize> for MultiLinear<E> {
 82    type Output = E;
 83
 84    fn index(&self, index: usize) -> &E {
 85        &(self.evaluations[index])
 86    }
 87}
 88
 89/// A multi-variate polynomial for composing individual multi-linear polynomials
 90pub trait CompositionPolynomial<E: FieldElement>: Sync + Send {
 91    /// The number of variables when interpreted as a multi-variate polynomial.
 92    fn num_variables(&self) -> usize;
 93
 94    /// Maximum degree in all variables.
 95    fn max_degree(&self) -> usize;
 96
 97    /// Given a query, of length equal the number of variables, evaluate [Self] at this query.
 98    fn evaluate(&self, query: &[E]) -> E;
 99}
100
101pub struct ComposedMultiLinears<E: FieldElement> {
102    pub composer: Arc<dyn CompositionPolynomial<E>>,
103    pub multi_linears: Vec<MultiLinear<E>>,
104}
105
106impl<E: FieldElement> ComposedMultiLinears<E> {
107    pub fn new(
108        composer: Arc<dyn CompositionPolynomial<E>>,
109        multi_linears: Vec<MultiLinear<E>>,
110    ) -> Self {
111        Self { composer, multi_linears }
112    }
113
114    pub fn num_ml(&self) -> usize {
115        self.multi_linears.len()
116    }
117
118    pub fn num_variables(&self) -> usize {
119        self.composer.num_variables()
120    }
121
122    pub fn num_variables_ml(&self) -> usize {
123        self.multi_linears[0].num_variables
124    }
125
126    pub fn degree(&self) -> usize {
127        self.composer.max_degree()
128    }
129
130    pub fn bind(&self, round_challenge: E) -> ComposedMultiLinears<E> {
131        let result: Vec<MultiLinear<E>> =
132            self.multi_linears.iter().map(|f| f.bind(round_challenge)).collect();
133
134        Self {
135            composer: self.composer.clone(),
136            multi_linears: result,
137        }
138    }
139}
140
141#[derive(Clone)]
142pub struct ComposedMultiLinearsOracle<E: FieldElement> {
143    pub composer: Arc<dyn CompositionPolynomial<E>>,
144    pub multi_linears: Vec<MultiLinearOracle>,
145}
146
147#[derive(Debug, Clone)]
148pub struct MultiLinearOracle {
149    pub id: usize,
150}
151
152// Composition polynomials
153
154pub struct IdentityComposition {
155    num_variables: usize,
156}
157
158impl IdentityComposition {
159    pub fn new() -> Self {
160        Self { num_variables: 1 }
161    }
162}
163
164impl<E> CompositionPolynomial<E> for IdentityComposition
165where
166    E: FieldElement,
167{
168    fn num_variables(&self) -> usize {
169        self.num_variables
170    }
171
172    fn max_degree(&self) -> usize {
173        self.num_variables
174    }
175
176    fn evaluate(&self, query: &[E]) -> E {
177        assert_eq!(query.len(), 1);
178        query[0]
179    }
180}
181
182pub struct ProjectionComposition {
183    coordinate: usize,
184}
185
186impl ProjectionComposition {
187    pub fn new(coordinate: usize) -> Self {
188        Self { coordinate }
189    }
190}
191
192impl<E> CompositionPolynomial<E> for ProjectionComposition
193where
194    E: FieldElement,
195{
196    fn num_variables(&self) -> usize {
197        1
198    }
199
200    fn max_degree(&self) -> usize {
201        1
202    }
203
204    fn evaluate(&self, query: &[E]) -> E {
205        query[self.coordinate]
206    }
207}
208
209pub struct LogUpDenominatorTableComposition<E>
210where
211    E: FieldElement,
212{
213    projection_coordinate: usize,
214    alpha: E,
215}
216
217impl<E> LogUpDenominatorTableComposition<E>
218where
219    E: FieldElement,
220{
221    pub fn new(projection_coordinate: usize, alpha: E) -> Self {
222        Self { projection_coordinate, alpha }
223    }
224}
225
226impl<E> CompositionPolynomial<E> for LogUpDenominatorTableComposition<E>
227where
228    E: FieldElement,
229{
230    fn num_variables(&self) -> usize {
231        1
232    }
233
234    fn max_degree(&self) -> usize {
235        1
236    }
237
238    fn evaluate(&self, query: &[E]) -> E {
239        query[self.projection_coordinate] + self.alpha
240    }
241}
242
243pub struct LogUpDenominatorWitnessComposition<E>
244where
245    E: FieldElement,
246{
247    projection_coordinate: usize,
248    alpha: E,
249}
250
251impl<E> LogUpDenominatorWitnessComposition<E>
252where
253    E: FieldElement,
254{
255    pub fn new(projection_coordinate: usize, alpha: E) -> Self {
256        Self { projection_coordinate, alpha }
257    }
258}
259
260impl<E> CompositionPolynomial<E> for LogUpDenominatorWitnessComposition<E>
261where
262    E: FieldElement,
263{
264    fn num_variables(&self) -> usize {
265        1
266    }
267
268    fn max_degree(&self) -> usize {
269        1
270    }
271
272    fn evaluate(&self, query: &[E]) -> E {
273        -(query[self.projection_coordinate] + self.alpha)
274    }
275}
276
277pub struct ProductComposition {
278    num_variables: usize,
279}
280
281impl ProductComposition {
282    pub fn new(num_variables: usize) -> Self {
283        Self { num_variables }
284    }
285}
286
287impl<E> CompositionPolynomial<E> for ProductComposition
288where
289    E: FieldElement,
290{
291    fn num_variables(&self) -> usize {
292        self.num_variables
293    }
294
295    fn max_degree(&self) -> usize {
296        self.num_variables
297    }
298
299    fn evaluate(&self, query: &[E]) -> E {
300        query.iter().fold(E::ONE, |acc, x| acc * *x)
301    }
302}
303
304pub struct SumComposition {
305    num_variables: usize,
306}
307
308impl SumComposition {
309    pub fn new(num_variables: usize) -> Self {
310        Self { num_variables }
311    }
312}
313
314impl<E> CompositionPolynomial<E> for SumComposition
315where
316    E: FieldElement,
317{
318    fn num_variables(&self) -> usize {
319        self.num_variables
320    }
321
322    fn max_degree(&self) -> usize {
323        self.num_variables
324    }
325
326    fn evaluate(&self, query: &[E]) -> E {
327        query.iter().fold(E::ZERO, |acc, x| acc + *x)
328    }
329}
330
331pub struct GkrCompositionVanilla<E: 'static>
332where
333    E: FieldElement,
334{
335    num_variables_ml: usize,
336    num_variables_merge: usize,
337    combining_randomness: E,
338    gkr_randomness: Vec<E>,
339}
340
341impl<E> GkrCompositionVanilla<E>
342where
343    E: FieldElement,
344{
345    pub fn new(
346        num_variables_ml: usize,
347        num_variables_merge: usize,
348        combining_randomness: E,
349        gkr_randomness: Vec<E>,
350    ) -> Self {
351        Self {
352            num_variables_ml,
353            num_variables_merge,
354            combining_randomness,
355            gkr_randomness,
356        }
357    }
358}
359
360impl<E> CompositionPolynomial<E> for GkrCompositionVanilla<E>
361where
362    E: FieldElement,
363{
364    fn num_variables(&self) -> usize {
365        self.num_variables_ml // + TODO
366    }
367
368    fn max_degree(&self) -> usize {
369        self.num_variables_ml //TODO
370    }
371
372    fn evaluate(&self, query: &[E]) -> E {
373        let eval_left_numerator = query[0];
374        let eval_right_numerator = query[1];
375        let eval_left_denominator = query[2];
376        let eval_right_denominator = query[3];
377        let eq_eval = query[4];
378
379        eq_eval
380            * ((eval_left_numerator * eval_right_denominator
381                + eval_right_numerator * eval_left_denominator)
382                + eval_left_denominator * eval_right_denominator * self.combining_randomness)
383    }
384}
385
386#[derive(Clone)]
387pub struct GkrComposition<E>
388where
389    E: FieldElement<BaseField = BaseElement>,
390{
391    pub num_variables_ml: usize,
392    pub combining_randomness: E,
393
394    eq_composer: Arc<dyn CompositionPolynomial<E>>,
395    right_numerator_composer: Vec<Arc<dyn CompositionPolynomial<E>>>,
396    left_numerator_composer: Vec<Arc<dyn CompositionPolynomial<E>>>,
397    right_denominator_composer: Vec<Arc<dyn CompositionPolynomial<E>>>,
398    left_denominator_composer: Vec<Arc<dyn CompositionPolynomial<E>>>,
399}
400
401impl<E> GkrComposition<E>
402where
403    E: FieldElement<BaseField = BaseElement>,
404{
405    pub fn new(
406        num_variables_ml: usize,
407        combining_randomness: E,
408        eq_composer: Arc<dyn CompositionPolynomial<E>>,
409        right_numerator_composer: Vec<Arc<dyn CompositionPolynomial<E>>>,
410        left_numerator_composer: Vec<Arc<dyn CompositionPolynomial<E>>>,
411        right_denominator_composer: Vec<Arc<dyn CompositionPolynomial<E>>>,
412        left_denominator_composer: Vec<Arc<dyn CompositionPolynomial<E>>>,
413    ) -> Self {
414        Self {
415            num_variables_ml,
416            combining_randomness,
417            eq_composer,
418            right_numerator_composer,
419            left_numerator_composer,
420            right_denominator_composer,
421            left_denominator_composer,
422        }
423    }
424}
425
426impl<E> CompositionPolynomial<E> for GkrComposition<E>
427where
428    E: FieldElement<BaseField = BaseElement>,
429{
430    fn num_variables(&self) -> usize {
431        self.num_variables_ml // + TODO
432    }
433
434    fn max_degree(&self) -> usize {
435        3 // TODO
436    }
437
438    fn evaluate(&self, query: &[E]) -> E {
439        let eval_right_numerator = self.right_numerator_composer[0].evaluate(query);
440        let eval_left_numerator = self.left_numerator_composer[0].evaluate(query);
441        let eval_right_denominator = self.right_denominator_composer[0].evaluate(query);
442        let eval_left_denominator = self.left_denominator_composer[0].evaluate(query);
443        let eq_eval = self.eq_composer.evaluate(query);
444
445        let res = eq_eval
446            * ((eval_left_numerator * eval_right_denominator
447                + eval_right_numerator * eval_left_denominator)
448                + eval_left_denominator * eval_right_denominator * self.combining_randomness);
449        res
450    }
451}
452
453/// Generates a composed ML polynomial for the initial GKR layer from a vector of composition
454/// polynomials.
455/// The composition polynomials are divided into LeftNumerator, RightNumerator, LeftDenominator
456/// and RightDenominator.
457/// TODO: Generalize this to the case where each numerator/denominator contains more than one
458/// composition polynomial i.e., a merged composed ML polynomial.
459pub fn gkr_composition_from_composition_polys<
460    E: FieldElement<BaseField = BaseElement> + 'static,
461>(
462    composition_polys: &Vec<Vec<Arc<dyn CompositionPolynomial<E>>>>,
463    combining_randomness: E,
464    num_variables: usize,
465) -> GkrComposition<E> {
466    let eq_composer = Arc::new(ProjectionComposition::new(4));
467    let left_numerator = composition_polys[0].to_owned();
468    let right_numerator = composition_polys[1].to_owned();
469    let left_denominator = composition_polys[2].to_owned();
470    let right_denominator = composition_polys[3].to_owned();
471    GkrComposition::new(
472        num_variables,
473        combining_randomness,
474        eq_composer,
475        right_numerator,
476        left_numerator,
477        right_denominator,
478        left_denominator,
479    )
480}
481
482/// Generates a plain oracle for the sum-check protocol except the final one.
483pub fn gen_plain_gkr_oracle<E: FieldElement<BaseField = BaseElement> + 'static>(
484    num_rounds: usize,
485    r_sum_check: E,
486) -> ComposedMultiLinearsOracle<E> {
487    let gkr_composer = Arc::new(GkrCompositionVanilla::new(num_rounds, 0, r_sum_check, vec![]));
488
489    let ml_oracles = vec![
490        MultiLinearOracle { id: 0 },
491        MultiLinearOracle { id: 1 },
492        MultiLinearOracle { id: 2 },
493        MultiLinearOracle { id: 3 },
494        MultiLinearOracle { id: 4 },
495    ];
496
497    let oracle = ComposedMultiLinearsOracle {
498        composer: gkr_composer,
499        multi_linears: ml_oracles,
500    };
501    oracle
502}
503
504fn to_index<E: FieldElement<BaseField = BaseElement>>(index: &[E]) -> usize {
505    let res = index.iter().fold(E::ZERO, |acc, term| acc * E::ONE.double() + (*term));
506    let res = res.base_element(0);
507    res.as_int() as usize
508}
509
510fn inner_product<E: FieldElement>(evaluations: &[E], tensored_query: &[E]) -> E {
511    assert_eq!(evaluations.len(), tensored_query.len());
512    evaluations
513        .iter()
514        .zip(tensored_query.iter())
515        .fold(E::ZERO, |acc, (x_i, y_i)| acc + *x_i * *y_i)
516}
517
518pub fn tensorize<E: FieldElement>(query: &[E]) -> Vec<E> {
519    let nu = query.len();
520    let n = 1 << nu;
521
522    (0..n).map(|i| lagrange_basis_eval(query, i)).collect()
523}
524
525fn lagrange_basis_eval<E: FieldElement>(query: &[E], i: usize) -> E {
526    query
527        .iter()
528        .enumerate()
529        .map(|(j, x_j)| if i & (1 << j) == 0 { E::ONE - *x_j } else { *x_j })
530        .fold(E::ONE, |acc, v| acc * v)
531}
532
533pub fn compute_claim<E: FieldElement>(poly: &ComposedMultiLinears<E>) -> E {
534    let cube_size = 1 << poly.num_variables_ml();
535    let mut res = E::ZERO;
536
537    for i in 0..cube_size {
538        let eval_point: Vec<E> =
539            poly.multi_linears.iter().map(|poly| poly.evaluations[i]).collect();
540        res += poly.composer.evaluate(&eval_point);
541    }
542    res
543}