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