labrador/core/
aggregate.rs

1use crate::relation::env_params::EnvironmentParameters;
2use crate::ring::rq::Rq;
3use crate::ring::rq_matrix::RqMatrix;
4use crate::ring::rq_vector::RqVector;
5use crate::ring::zq::Zq;
6
7use super::inner_product;
8
9/// This struct serves as aggregation of functions with constant value 0.
10pub struct ZeroConstantFunctionsAggregation<'a> {
11    ep: &'a EnvironmentParameters,
12    a_double_prime: Vec<RqMatrix>,
13    phi_double_prime: Vec<Vec<RqVector>>,
14}
15
16impl<'a> ZeroConstantFunctionsAggregation<'a> {
17    pub fn new(parameters: &'a EnvironmentParameters) -> Self {
18        Self {
19            ep: parameters,
20            a_double_prime: vec![
21                RqMatrix::symmetric_zero(parameters.multiplicity);
22                parameters.const_agg_length
23            ],
24            phi_double_prime: vec![
25                vec![RqVector::zero(parameters.rank); parameters.multiplicity];
26                parameters.const_agg_length
27            ],
28        }
29    }
30
31    /// Calculate a_double_primes from a_prime, a_{i,j}^{''k} = \sum_{l=1}^{L}\psi_l^{k}a_{ij}^{'(l)}
32    ///
33    /// @param: vector_psi: \psi_l^k
34    /// @param: a_prime: a_{ij}^{'(l)}, each a_{ij} is a ring element (PolyRing)
35    ///
36    /// @return: a_{ij}^{''(k)}, return a vector length k of matrix a_{ij}^{''}
37    pub fn calculate_agg_a_double_prime(&mut self, vector_psi: &[Vec<Zq>], a_prime: &[RqMatrix]) {
38        for i in 0..self.ep.multiplicity {
39            for j in 0..i + 1 {
40                let a_prime_l_vector: Vec<&Rq> =
41                    a_prime.iter().map(|matrix| matrix.get_cell(i, j)).collect();
42
43                for (k, matrix) in self.a_double_prime.iter_mut().enumerate() {
44                    matrix.set_cell(
45                        i,
46                        j,
47                        inner_product::compute_linear_combination(
48                            &a_prime_l_vector,
49                            &vector_psi[k],
50                        ),
51                    );
52                }
53            }
54        }
55    }
56
57    /// calculate \phi_{i}^{''(k)} = \sum_{l=1}^{L}\psi_l^{k}\phi_{i}^{'(l)} + \sum(\omega_j^{k} * \sigma_{-1} * pi_i^{j})
58    /// in the prover process, page 17 from the paper.
59    ///
60    /// @param: phi_ct: \phi_{i}^{'(l)}
61    /// @param: pi: pi_i^{j}
62    /// @param: random_psi: \psi_l^{k}
63    /// @param: random_omega: \omega_j^{k}
64    ///
65    /// return: \phi_{i}^{''(k)}
66    pub fn calculate_agg_phi_double_prime(
67        &mut self,
68        phi_prime: &[Vec<RqVector>],
69        conjugated_pi: &[RqMatrix],
70        vector_psi: &[Vec<Zq>],
71        vector_omega: &[Vec<Zq>],
72    ) {
73        for i in 0..self.ep.multiplicity {
74            let phi_prime_l_vector: Vec<&RqVector> =
75                phi_prime.iter().map(|elems| &elems[i]).collect();
76            for (k, phi_k) in self.phi_double_prime.iter_mut().enumerate() {
77                phi_k[i] =
78                    inner_product::compute_linear_combination(&phi_prime_l_vector, &vector_psi[k]);
79            }
80        }
81
82        for (i, pi_i) in conjugated_pi.iter().enumerate() {
83            for (k, phi_k) in self.phi_double_prime.iter_mut().enumerate() {
84                phi_k[i] = &phi_k[i]
85                    + &inner_product::compute_linear_combination(pi_i.elements(), &vector_omega[k]);
86            }
87        }
88    }
89
90    /// calculate b^{''(k)} = \sum_{i,j=1}^{r} a_{ij}^{''(k)} * <s_i, s_j> + \sum_{i=1}^{r} <\phi_{i}^{''(k)} * s_i>
91    ///
92    /// @param: a_ct_aggr: a_{ij}^{''(k)}
93    /// @param: phi_ct_aggr: \phi_{i}^{''(k)}
94    /// @param: witness: s_i
95    ///
96    /// @return: b^{''(k)}
97    pub fn calculate_agg_b_double_prime(&mut self, witness: &[RqVector]) -> RqVector {
98        (0..self.ep.kappa)
99            .map(|k| {
100                (0..self.ep.multiplicity)
101                    .map(|i| {
102                        &(0..self.ep.multiplicity).map(|j| {
103                    // calculate a_{ij}^{''(k)} * <s_i, s_j>
104                    self.a_double_prime[k].get_cell(i, j)
105                        * &inner_product::compute_linear_combination(witness[i].elements(), witness[j].elements())
106                })
107                .fold(
108                    // sum over all i,j
109                    Rq::zero(),
110                    |acc, val| &acc + &val,
111                )
112                // add \phi_{i}^{''(k)} * s[i]
113                + &inner_product::compute_linear_combination(self.phi_double_prime[k][i].elements(), witness[i].elements())
114                    }) // sum over all i,j
115                    .fold(Rq::zero(), |acc, val| &acc + &val)
116            })
117            .collect()
118    }
119
120    pub fn get_alpha_double_prime(&self) -> &[RqMatrix] {
121        &self.a_double_prime
122    }
123
124    pub fn get_phi_double_prime(&self) -> &[Vec<RqVector>] {
125        &self.phi_double_prime
126    }
127}
128
129pub struct FunctionsAggregation<'a> {
130    ep: &'a EnvironmentParameters,
131    aggregated_a: RqMatrix,
132    aggregated_phi: Vec<RqVector>,
133    aggregated_b: Rq,
134}
135
136impl<'a> FunctionsAggregation<'a> {
137    pub fn new(parameters: &'a EnvironmentParameters) -> Self {
138        Self {
139            ep: parameters,
140            aggregated_a: RqMatrix::symmetric_zero(parameters.multiplicity),
141            aggregated_phi: vec![RqVector::zero(parameters.rank); parameters.multiplicity],
142            aggregated_b: Rq::zero(),
143        }
144    }
145
146    /// calculate a_i = \sum(alpha_k * a_{ij}) + \sum(beta_k * a_{ij}^{''(k)})
147    /// equation 5, in the verifier process, page 18 from the paper.
148    ///
149    /// @param: a_constraint: a_{ij}
150    /// @param: a_ct_aggr: a_{ij}^{''(k)}
151    /// @param: random_alpha: alpha_k
152    /// @param: random_beta: beta_k
153    /// @param: ep: struct SizeParams
154    ///
155    /// @return: a_i
156    pub fn calculate_agg_a(
157        &mut self,
158        a_constraint: &[RqMatrix],
159        a_double_prime: &[RqMatrix],
160        vector_alpha: &RqVector,
161        vector_beta: &RqVector,
162    ) {
163        for i in 0..self.ep.multiplicity {
164            for j in 0..i + 1 {
165                let a_constraint_k: Vec<&Rq> = a_constraint
166                    .iter()
167                    .map(|matrix| matrix.get_cell(i, j))
168                    .collect();
169                let a_double_prime_k: Vec<&Rq> = a_double_prime
170                    .iter()
171                    .map(|matrix| matrix.get_cell(i, j))
172                    .collect();
173                self.aggregated_a.set_cell(
174                    i,
175                    j,
176                    &inner_product::compute_linear_combination::<&Rq, Rq, Rq>(
177                        &a_constraint_k,
178                        vector_alpha.elements(),
179                    ) + &inner_product::compute_linear_combination(
180                        &a_double_prime_k,
181                        vector_beta.elements(),
182                    ),
183                );
184            }
185        }
186    }
187
188    /// calculate phi_i = \sum(alpha_k * \phi_{i}^{k}) + \sum(beta_k * \phi_{i}^{''(k)})
189    /// equation 6, in the verifier process, page 18 from the paper.
190    ///
191    /// param: phi_constraint: \phi_{i}^{k}
192    /// param: phi_ct_aggr: \phi_{i}^{''(k)}
193    /// param: random_alpha: alpha_k
194    /// param: random_beta: beta_k
195    /// param: ep: struct SizeParams
196    ///
197    /// return: phi_i
198    pub fn calculate_aggr_phi(
199        &mut self,
200        phi_constraint: &[Vec<RqVector>],
201        phi_double_prime: &[Vec<RqVector>],
202        vector_alpha: &RqVector,
203        vector_beta: &RqVector,
204    ) {
205        for i in 0..self.ep.multiplicity {
206            let phi_constraint_k: Vec<&RqVector> =
207                phi_constraint.iter().map(|element| &element[i]).collect();
208            let phi_double_prime_k: Vec<&RqVector> =
209                phi_double_prime.iter().map(|element| &element[i]).collect();
210            self.aggregated_phi[i] =
211                &inner_product::compute_linear_combination::<&RqVector, RqVector, Rq>(
212                    &phi_constraint_k,
213                    vector_alpha.elements(),
214                ) + &inner_product::compute_linear_combination(
215                    &phi_double_prime_k,
216                    vector_beta.elements(),
217                );
218        }
219    }
220
221    /// calculate b_i = \sum(alpha_k * b^{k}) + \sum(beta_k * b^{''(k})
222    /// equation 7, in the verifier process, page 18 from the paper.
223    ///
224    /// @param: b_constraint: b^{k}
225    /// @param: b_ct_aggr: b^{''(k)}
226    /// @param: random_alpha: alpha_k
227    /// @param: random_beta: beta_k
228    /// @param: ep: struct SizeParams
229    ///
230    /// @return: b_i
231    pub fn calculate_aggr_b(
232        &mut self,
233        b_constraint: &RqVector,
234        b_double_prime: &RqVector,
235        vector_alpha: &RqVector,
236        vector_beta: &RqVector,
237    ) {
238        self.aggregated_b = &inner_product::compute_linear_combination(
239            b_constraint.elements(),
240            vector_alpha.elements(),
241        ) + &inner_product::compute_linear_combination(
242            b_double_prime.elements(),
243            vector_beta.elements(),
244        )
245    }
246
247    pub fn get_agg_a(&self) -> &RqMatrix {
248        &self.aggregated_a
249    }
250
251    pub fn get_appr_phi(&self) -> &[RqVector] {
252        &self.aggregated_phi
253    }
254
255    pub fn get_aggr_b(&self) -> &Rq {
256        &self.aggregated_b
257    }
258}
259
260#[cfg(test)]
261#[allow(clippy::needless_range_loop)]
262mod constant_agg_tests {
263    use crate::relation::env_params;
264
265    use super::*;
266
267    mod variable_generator {
268        use super::*;
269        use rand::{
270            distr::{Distribution, Uniform},
271            rng,
272        };
273        fn sample_zq_vector(length: usize) -> Vec<Zq> {
274            let uniform = Uniform::new_inclusive(Zq::ZERO, Zq::NEG_ONE).unwrap();
275            let mut coeffs = vec![Zq::ZERO; length];
276            coeffs
277                .iter_mut()
278                .for_each(|c| *c = uniform.sample(&mut rng()));
279            coeffs
280        }
281
282        pub fn generate_vector_psi(vec_length: usize, inner_vec_size: usize) -> Vec<Vec<Zq>> {
283            let mut vector_psi = Vec::new();
284            for _ in 0..vec_length {
285                vector_psi.push(sample_zq_vector(inner_vec_size));
286            }
287            vector_psi
288        }
289
290        pub fn generate_a_prime(vec_length: usize, matrix_size: usize) -> Vec<RqMatrix> {
291            let mut a_prime = Vec::new();
292            for _ in 0..vec_length {
293                a_prime.push(RqMatrix::symmetric_random(&mut rng(), matrix_size));
294            }
295            a_prime
296        }
297
298        pub fn generate_phi_prime(
299            vec_length: usize,
300            inner_vec_length: usize,
301            inner_inner_vec_length: usize,
302        ) -> Vec<Vec<RqVector>> {
303            let mut phi_prime = Vec::new();
304            for _ in 0..vec_length {
305                let mut inner_vec = Vec::new();
306                for _ in 0..inner_vec_length {
307                    inner_vec.push(RqVector::random(&mut rng(), inner_inner_vec_length));
308                }
309                phi_prime.push(inner_vec);
310            }
311            phi_prime
312        }
313
314        pub fn generate_conjugated_pi(
315            vec_length: usize,
316            matrix_row: usize,
317            matrix_col: usize,
318        ) -> Vec<RqMatrix> {
319            let mut conjugated_pi = Vec::new();
320            for _ in 0..vec_length {
321                conjugated_pi.push(RqMatrix::random(&mut rng(), matrix_row, matrix_col));
322            }
323            conjugated_pi
324        }
325
326        pub fn generate_omega(vec_length: usize, inner_vec_length: usize) -> Vec<Vec<Zq>> {
327            let mut vector_omega = Vec::new();
328            for _ in 0..vec_length {
329                vector_omega.push(variable_generator::sample_zq_vector(inner_vec_length));
330            }
331            vector_omega
332        }
333
334        pub fn generate_witness(vec_length: usize, inner_vec_length: usize) -> Vec<RqVector> {
335            let mut witness = Vec::new();
336            for _ in 0..vec_length {
337                witness.push(RqVector::random(&mut rng(), inner_vec_length));
338            }
339            witness
340        }
341    }
342
343    #[test]
344    fn test_calculate_agg_a_double_prime() {
345        let params = EnvironmentParameters::default();
346        let mut aggregator = ZeroConstantFunctionsAggregation::new(&params);
347
348        let vector_psi =
349            variable_generator::generate_vector_psi(params.const_agg_length, params.constraint_l);
350        let a_prime =
351            variable_generator::generate_a_prime(params.constraint_l, params.multiplicity);
352
353        aggregator.calculate_agg_a_double_prime(&vector_psi, &a_prime);
354
355        for k in 0..params.const_agg_length {
356            for i in 0..params.multiplicity {
357                for j in 0..params.multiplicity {
358                    let mut rhs = Rq::zero();
359                    for l in 0..params.constraint_l {
360                        rhs = &rhs + &(a_prime[l].get_cell(i, j) * &vector_psi[k][l])
361                    }
362                    assert_eq!(*aggregator.a_double_prime[k].get_cell(i, j), rhs);
363                }
364            }
365        }
366    }
367
368    #[test]
369    fn test_calculate_agg_phi_double_prime() {
370        let params = EnvironmentParameters::default();
371        let mut aggregator = ZeroConstantFunctionsAggregation::new(&params);
372
373        let phi_prime = variable_generator::generate_phi_prime(
374            params.constraint_l,
375            params.multiplicity,
376            params.rank,
377        );
378        let conjugated_pi = variable_generator::generate_conjugated_pi(
379            params.multiplicity,
380            2 * env_params::SECURITY_PARAMETER,
381            params.rank,
382        );
383        let vector_psi =
384            variable_generator::generate_vector_psi(params.const_agg_length, params.constraint_l);
385        let vector_omega = variable_generator::generate_omega(
386            params.const_agg_length,
387            2 * env_params::SECURITY_PARAMETER,
388        );
389
390        aggregator.calculate_agg_phi_double_prime(
391            &phi_prime,
392            &conjugated_pi,
393            &vector_psi,
394            &vector_omega,
395        );
396
397        for k in 0..params.const_agg_length {
398            for i in 0..params.multiplicity {
399                let mut rhs = RqVector::zero(params.rank);
400                for l in 0..params.constraint_l {
401                    rhs = &rhs + &(&phi_prime[l][i] * vector_psi[k][l]);
402                }
403                let mut lhs = RqVector::zero(params.rank);
404                for j in 0..2 * env_params::SECURITY_PARAMETER {
405                    lhs = &lhs + &(&conjugated_pi[i].elements()[j] * vector_omega[k][j]);
406                }
407                assert_eq!(aggregator.phi_double_prime[k][i], &rhs + &lhs);
408            }
409        }
410    }
411
412    #[test]
413    fn test_calculate_agg_b_double_prime() {
414        let params = EnvironmentParameters::default();
415        let mut aggregator = ZeroConstantFunctionsAggregation::new(&params);
416
417        let phi_prime = variable_generator::generate_phi_prime(
418            params.constraint_l,
419            params.multiplicity,
420            params.rank,
421        );
422        let conjugated_pi = variable_generator::generate_conjugated_pi(
423            params.multiplicity,
424            2 * env_params::SECURITY_PARAMETER,
425            params.rank,
426        );
427        let vector_psi =
428            variable_generator::generate_vector_psi(params.const_agg_length, params.constraint_l);
429        let vector_omega = variable_generator::generate_omega(
430            params.const_agg_length,
431            2 * env_params::SECURITY_PARAMETER,
432        );
433        let a_prime =
434            variable_generator::generate_a_prime(params.constraint_l, params.multiplicity);
435        let witness_vector = variable_generator::generate_witness(params.multiplicity, params.rank);
436
437        aggregator.calculate_agg_a_double_prime(&vector_psi, &a_prime);
438        aggregator.calculate_agg_phi_double_prime(
439            &phi_prime,
440            &conjugated_pi,
441            &vector_psi,
442            &vector_omega,
443        );
444        let b_double_prime = aggregator.calculate_agg_b_double_prime(&witness_vector);
445
446        for k in 0..params.const_agg_length {
447            let mut rhs = Rq::zero();
448            for i in 0..params.multiplicity {
449                for j in 0..params.multiplicity {
450                    rhs = &rhs
451                        + &(aggregator.a_double_prime[k].get_cell(i, j)
452                            * &inner_product::compute_linear_combination(
453                                witness_vector[i].elements(),
454                                witness_vector[j].elements(),
455                            ));
456                }
457            }
458            let mut lhs = Rq::zero();
459            for i in 0..params.multiplicity {
460                lhs = &lhs
461                    + (&inner_product::compute_linear_combination(
462                        aggregator.phi_double_prime[k][i].elements(),
463                        witness_vector[i].elements(),
464                    ));
465            }
466            assert_eq!(b_double_prime.elements()[k], &rhs + &lhs);
467        }
468    }
469}
470
471#[cfg(test)]
472#[allow(clippy::needless_range_loop)]
473mod func_agg_tests {
474    use super::*;
475
476    mod variable_generator {
477        use super::*;
478        use rand::rng;
479
480        pub fn generate_rq_vector(vec_length: usize) -> RqVector {
481            let mut vector_alpha = Vec::new();
482            for _ in 0..vec_length {
483                vector_alpha.push(Rq::random(&mut rng()));
484            }
485            RqVector::new(vector_alpha)
486        }
487
488        pub fn generate_matrix_vector(vec_length: usize, matrix_size: usize) -> Vec<RqMatrix> {
489            let mut a_constraint = Vec::new();
490            for _ in 0..vec_length {
491                a_constraint.push(RqMatrix::symmetric_random(&mut rng(), matrix_size));
492            }
493            a_constraint
494        }
495
496        pub fn generate_rqvector_vector(
497            vec_length: usize,
498            inner_vec_length: usize,
499            inner_inner_vec_length: usize,
500        ) -> Vec<Vec<RqVector>> {
501            let mut phi_constraint = Vec::new();
502            for _ in 0..vec_length {
503                let mut inner_vec = Vec::new();
504                for _ in 0..inner_vec_length {
505                    inner_vec.push(RqVector::random(&mut rng(), inner_inner_vec_length));
506                }
507                phi_constraint.push(inner_vec);
508            }
509            phi_constraint
510        }
511    }
512
513    #[test]
514    fn test_calculate_agg_a() {
515        let params = EnvironmentParameters::default();
516        let mut aggregator = FunctionsAggregation::new(&params);
517
518        let vector_alpha = variable_generator::generate_rq_vector(params.constraint_k);
519        let vector_beta = variable_generator::generate_rq_vector(params.const_agg_length);
520        let a_constraint =
521            variable_generator::generate_matrix_vector(params.constraint_k, params.multiplicity);
522        let a_double_prime = variable_generator::generate_matrix_vector(
523            params.const_agg_length,
524            params.multiplicity,
525        );
526
527        aggregator.calculate_agg_a(&a_constraint, &a_double_prime, &vector_alpha, &vector_beta);
528
529        for i in 0..params.multiplicity {
530            for j in 0..params.multiplicity {
531                let mut rhs = Rq::zero();
532                for k in 0..params.constraint_k {
533                    rhs = &rhs + &(a_constraint[k].get_cell(i, j) * &vector_alpha.elements()[k])
534                }
535                let mut lhs = Rq::zero();
536                for k in 0..params.const_agg_length {
537                    lhs = &lhs + &(a_double_prime[k].get_cell(i, j) * &vector_beta.elements()[k])
538                }
539                assert_eq!(*aggregator.aggregated_a.get_cell(i, j), &rhs + &lhs);
540            }
541        }
542    }
543
544    #[test]
545    fn test_calculate_agg_phi() {
546        let params = EnvironmentParameters::default();
547        let mut aggregator = FunctionsAggregation::new(&params);
548
549        let vector_alpha = variable_generator::generate_rq_vector(params.constraint_k);
550        let vector_beta = variable_generator::generate_rq_vector(params.const_agg_length);
551        let phi_constraint = variable_generator::generate_rqvector_vector(
552            params.constraint_k,
553            params.multiplicity,
554            params.rank,
555        );
556        let phi_double_prime = variable_generator::generate_rqvector_vector(
557            params.const_agg_length,
558            params.multiplicity,
559            params.rank,
560        );
561
562        aggregator.calculate_aggr_phi(
563            &phi_constraint,
564            &phi_double_prime,
565            &vector_alpha,
566            &vector_beta,
567        );
568
569        for i in 0..params.multiplicity {
570            let mut rhs = RqVector::zero(params.rank);
571            for k in 0..params.constraint_k {
572                rhs = &rhs + &(&phi_constraint[k][i] * &vector_alpha.elements()[k])
573            }
574            let mut lhs = RqVector::zero(params.rank);
575            for k in 0..params.const_agg_length {
576                lhs = &lhs + &(&phi_double_prime[k][i] * &vector_beta.elements()[k])
577            }
578            assert_eq!(aggregator.aggregated_phi[i], &rhs + &lhs);
579        }
580    }
581
582    #[test]
583    fn test_calculate_agg_b() {
584        let params = EnvironmentParameters::default();
585        let mut aggregator = FunctionsAggregation::new(&params);
586
587        let vector_alpha = variable_generator::generate_rq_vector(params.constraint_k);
588        let vector_beta = variable_generator::generate_rq_vector(params.const_agg_length);
589        let b_constraint = variable_generator::generate_rq_vector(params.constraint_k);
590        let b_double_prime = variable_generator::generate_rq_vector(params.const_agg_length);
591
592        aggregator.calculate_aggr_b(&b_constraint, &b_double_prime, &vector_alpha, &vector_beta);
593
594        let mut rhs = Rq::zero();
595        for k in 0..params.constraint_k {
596            rhs = &rhs + &(&b_constraint.elements()[k] * &vector_alpha.elements()[k])
597        }
598        let mut lhs = Rq::zero();
599        for k in 0..params.const_agg_length {
600            lhs = &lhs + &(&b_double_prime.elements()[k] * &vector_beta.elements()[k])
601        }
602        assert_eq!(aggregator.aggregated_b, &rhs + &lhs);
603    }
604}