From 186657e22f39d5014041157a4f16b84b519e1e08 Mon Sep 17 00:00:00 2001 From: Peter Nose Date: Tue, 23 Apr 2024 12:41:53 +0200 Subject: [PATCH 1/2] secret-sharing/src/vss/lagrange: Add function for basis polynomials --- secret-sharing/src/vss/lagrange/naive.rs | 31 ++++++++++++++------ secret-sharing/src/vss/lagrange/optimized.rs | 29 ++++++++++++------ 2 files changed, 42 insertions(+), 18 deletions(-) diff --git a/secret-sharing/src/vss/lagrange/naive.rs b/secret-sharing/src/vss/lagrange/naive.rs index 9869feddd70..63695ce0800 100644 --- a/secret-sharing/src/vss/lagrange/naive.rs +++ b/secret-sharing/src/vss/lagrange/naive.rs @@ -9,28 +9,41 @@ use crate::vss::polynomial::Polynomial; /// /// The Lagrange polynomial is defined as: /// ```text -/// L(x) = \sum_{i=0}^n y_i * L_i(x) +/// L(x) = \sum_{i=0}^n y_i * L_i(x) /// ``` /// where `L_i(x)` represents the i-th Lagrange basis polynomial. pub fn lagrange_naive(xs: &[Fp], ys: &[Fp]) -> Polynomial where Fp: PrimeField, { - let ls = (0..xs.len()) - .map(|i| basis_polynomial_naive(i, xs)) - .collect::>(); - + let ls = basis_polynomials_naive(xs); zip(ls, ys).map(|(li, &yi)| li * yi).sum() } -/// Returns i-th Lagrange basis polynomials for the given set of x values. +/// Returns Lagrange basis polynomials for the given set of x values. +/// +/// The i-th Lagrange basis polynomial is defined as: +/// ```text +/// L_i(x) = \prod_{j=0,j≠i}^n (x - x_j) / (x_i - x_j) +/// ``` +/// i.e. it holds `L_i(x_i)` = 1 and `L_i(x_j) = 0` for all `j ≠ i`. +fn basis_polynomials_naive(xs: &[Fp]) -> Vec> +where + Fp: PrimeField, +{ + (0..xs.len()) + .map(|i| basis_polynomial_naive(xs, i)) + .collect() +} + +/// Returns i-th Lagrange basis polynomial for the given set of x values. /// /// The i-th Lagrange basis polynomial is defined as: /// ```text -/// L_i(x) = \prod_{j=0,j≠i}^n (x - x_j) / (x_i - x_j) +/// L_i(x) = \prod_{j=0,j≠i}^n (x - x_j) / (x_i - x_j) /// ``` /// i.e. it holds `L_i(x_i)` = 1 and `L_i(x_j) = 0` for all `j ≠ i`. -fn basis_polynomial_naive(i: usize, xs: &[Fp]) -> Polynomial +fn basis_polynomial_naive(xs: &[Fp], i: usize) -> Polynomial where Fp: PrimeField, { @@ -109,7 +122,7 @@ mod tests { let xs = scalars(&[1, 2, 3]); for i in 0..xs.len() { - let p = basis_polynomial_naive(i, &xs); + let p = basis_polynomial_naive(&xs, i); // Verify points. for (j, x) in xs.iter().enumerate() { diff --git a/secret-sharing/src/vss/lagrange/optimized.rs b/secret-sharing/src/vss/lagrange/optimized.rs index 3b09f4ca28b..b0d2858118b 100644 --- a/secret-sharing/src/vss/lagrange/optimized.rs +++ b/secret-sharing/src/vss/lagrange/optimized.rs @@ -17,24 +17,35 @@ pub fn lagrange(xs: &[Fp], ys: &[Fp]) -> Polynomial where Fp: PrimeField, { - let m = multiplier(xs); - let ls = (0..xs.len()) - .map(|i| basis_polynomial(i, xs, &m)) - .collect::>(); - + let ls = basis_polynomials(xs); zip(ls, ys).map(|(li, &yi)| li * yi).sum() } -/// Returns i-th Lagrange basis polynomials for the given set of x values. +/// Returns Lagrange basis polynomials for the given set of x values. /// /// The i-th Lagrange basis polynomial is defined as: /// ```text -/// L_i(x) = \prod_{j=0,j≠i}^n (x - x_j) / (x_i - x_j) +/// L_i(x) = \prod_{j=0,j≠i}^n (x - x_j) / (x_i - x_j) +/// ``` +/// i.e. it holds `L_i(x_i)` = 1 and `L_i(x_j) = 0` for all `j ≠ i`. +fn basis_polynomials(xs: &[Fp]) -> Vec> +where + Fp: PrimeField, +{ + let m = multiplier(xs); + (0..xs.len()).map(|i| basis_polynomial(xs, i, &m)).collect() +} + +/// Returns i-th Lagrange basis polynomial for the given set of x values. +/// +/// The i-th Lagrange basis polynomial is defined as: +/// ```text +/// L_i(x) = \prod_{j=0,j≠i}^n (x - x_j) / (x_i - x_j) /// ``` /// i.e. it holds `L_i(x_i)` = 1 and `L_i(x_j) = 0` for all `j ≠ i`. fn basis_polynomial( - i: usize, xs: &[Fp], + i: usize, multiplier: &Multiplier>, ) -> Polynomial where @@ -129,7 +140,7 @@ mod tests { let m = multiplier(&xs); for i in 0..xs.len() { - let p = basis_polynomial(i, &xs, &m); + let p = basis_polynomial(&xs, i, &m); // Verify points. for (j, x) in xs.iter().enumerate() { From 1eaa8c09f799c1e794dc206ce27a1638536d0e70 Mon Sep 17 00:00:00 2001 From: Peter Nose Date: Tue, 23 Apr 2024 14:38:31 +0200 Subject: [PATCH 2/2] secret-sharing/src/vss/lagrange: Implement Lagrange coefficients --- .changelog/5658.feature.md | 1 + secret-sharing/src/vss/lagrange/naive.rs | 165 ++++++++++++++-- secret-sharing/src/vss/lagrange/optimized.rs | 186 ++++++++++++++++--- 3 files changed, 315 insertions(+), 37 deletions(-) create mode 100644 .changelog/5658.feature.md diff --git a/.changelog/5658.feature.md b/.changelog/5658.feature.md new file mode 100644 index 00000000000..b052ec0c7a7 --- /dev/null +++ b/.changelog/5658.feature.md @@ -0,0 +1 @@ +secret-sharing/src/vss/lagrange: Implement Lagrange coefficients diff --git a/secret-sharing/src/vss/lagrange/naive.rs b/secret-sharing/src/vss/lagrange/naive.rs index 63695ce0800..7919851ed9f 100644 --- a/secret-sharing/src/vss/lagrange/naive.rs +++ b/secret-sharing/src/vss/lagrange/naive.rs @@ -62,6 +62,44 @@ where nom } +/// Returns Lagrange coefficients for the given set of x values. +/// +/// The i-th Lagrange coefficient is defined as: +/// ```text +/// L_i(0) = \prod_{j=0,j≠i}^n x_j / (x_j - x_i) +/// ``` +pub fn coefficients_naive(xs: &[Fp]) -> Vec +where + Fp: PrimeField, +{ + (0..xs.len()).map(|i| coefficient_naive(xs, i)).collect() +} + +/// Returns i-th Lagrange coefficient for the given set of x values. +/// +/// The i-th Lagrange coefficient is defined as: +/// ```text +/// L_i(0) = \prod_{j=0,j≠i}^n x_j / (x_j - x_i) +/// ``` +fn coefficient_naive(xs: &[Fp], i: usize) -> Fp +where + Fp: PrimeField, +{ + let mut nom = Fp::ONE; + let mut denom = Fp::ONE; + for j in 0..xs.len() { + if j == i { + continue; + } + nom *= xs[j]; // x_j + denom *= xs[j] - xs[i]; // (x_j - x_i) + } + let denom_inv = denom.invert().expect("values should be unique"); + nom *= denom_inv; // L_i(0) = nom / denom + + nom +} + #[cfg(test)] mod tests { extern crate test; @@ -73,7 +111,10 @@ mod tests { use group::ff::Field; use rand::{rngs::StdRng, RngCore, SeedableRng}; - use super::{basis_polynomial_naive, lagrange_naive}; + use super::{ + basis_polynomial_naive, basis_polynomials_naive, coefficient_naive, coefficients_naive, + lagrange_naive, + }; fn scalar(value: i64) -> p384::Scalar { scalars(&vec![value])[0] @@ -119,23 +160,47 @@ mod tests { #[test] fn test_basis_polynomial_naive() { - let xs = scalars(&[1, 2, 3]); + let vec = [ + scalars(&[1]), + scalars(&[1, 2, 3]), + scalars(&(1..=50).collect::>()), + ]; - for i in 0..xs.len() { - let p = basis_polynomial_naive(&xs, i); + for xs in vec { + for i in 0..xs.len() { + let p = basis_polynomial_naive(&xs, i); - // Verify points. - for (j, x) in xs.iter().enumerate() { - if j == i { - assert_eq!(p.eval(x), scalar(1)); // L_i(x_i) = 1 - } else { - assert_eq!(p.eval(x), scalar(0)); // L_i(x_j) = 0 + // Verify points. + for (j, x) in xs.iter().enumerate() { + if j == i { + assert_eq!(p.eval(x), scalar(1)); // L_i(x_i) = 1 + } else { + assert_eq!(p.eval(x), scalar(0)); // L_i(x_j) = 0 + } } + + // Verify degree. + assert_eq!(p.degree(), xs.len() - 1); + assert_eq!(p.size(), xs.len()); } + } + } - // Verify degree. - assert_eq!(p.degree(), 2); - assert_eq!(p.size(), 3); + #[test] + fn test_coefficient_naive() { + let vec = [ + scalars(&[1]), + scalars(&[1, 2, 3]), + scalars(&(1..=50).collect::>()), + ]; + + for xs in vec { + for i in 0..xs.len() { + let c = coefficient_naive(&xs, i); + let p = basis_polynomial_naive(&xs, i); + + assert_eq!(c, p.eval(&scalar(0))); + } } } @@ -149,18 +214,36 @@ mod tests { }); } + fn bench_basis_polynomials_naive(b: &mut Bencher, n: usize) { + let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let xs = random_scalars(n, &mut rng); + + b.iter(|| { + let _p = basis_polynomials_naive(&xs); + }); + } + + fn bench_coefficients_naive(b: &mut Bencher, n: usize) { + let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let xs = random_scalars(n, &mut rng); + + b.iter(|| { + let _p = coefficients_naive(&xs); + }); + } + #[bench] - fn bench_lagrange_naive_1(b: &mut Bencher) { + fn bench_lagrange_naive_01(b: &mut Bencher) { bench_lagrange_naive(b, 1) } #[bench] - fn bench_lagrange_naive_2(b: &mut Bencher) { + fn bench_lagrange_naive_02(b: &mut Bencher) { bench_lagrange_naive(b, 2) } #[bench] - fn bench_lagrange_naive_5(b: &mut Bencher) { + fn bench_lagrange_naive_05(b: &mut Bencher) { bench_lagrange_naive(b, 5) } @@ -173,4 +256,54 @@ mod tests { fn bench_lagrange_naive_20(b: &mut Bencher) { bench_lagrange_naive(b, 20) } + + #[bench] + fn bench_basis_polynomials_naive_01(b: &mut Bencher) { + bench_basis_polynomials_naive(b, 1) + } + + #[bench] + fn bench_basis_polynomials_naive_02(b: &mut Bencher) { + bench_basis_polynomials_naive(b, 2) + } + + #[bench] + fn bench_basis_polynomials_naive_05(b: &mut Bencher) { + bench_basis_polynomials_naive(b, 5) + } + + #[bench] + fn bench_basis_polynomials_naive_10(b: &mut Bencher) { + bench_basis_polynomials_naive(b, 10) + } + + #[bench] + fn bench_basis_polynomials_naive_20(b: &mut Bencher) { + bench_basis_polynomials_naive(b, 20) + } + + #[bench] + fn bench_coefficients_naive_01(b: &mut Bencher) { + bench_coefficients_naive(b, 1) + } + + #[bench] + fn bench_coefficients_naive_02(b: &mut Bencher) { + bench_coefficients_naive(b, 2) + } + + #[bench] + fn bench_coefficients_naive_05(b: &mut Bencher) { + bench_coefficients_naive(b, 5) + } + + #[bench] + fn bench_coefficients_naive_10(b: &mut Bencher) { + bench_coefficients_naive(b, 10) + } + + #[bench] + fn bench_coefficients_naive_20(b: &mut Bencher) { + bench_coefficients_naive(b, 20) + } } diff --git a/secret-sharing/src/vss/lagrange/optimized.rs b/secret-sharing/src/vss/lagrange/optimized.rs index b0d2858118b..f74ea9eab9f 100644 --- a/secret-sharing/src/vss/lagrange/optimized.rs +++ b/secret-sharing/src/vss/lagrange/optimized.rs @@ -32,7 +32,7 @@ fn basis_polynomials(xs: &[Fp]) -> Vec> where Fp: PrimeField, { - let m = multiplier(xs); + let m = multiplier_for_basis_polynomials(xs); (0..xs.len()).map(|i| basis_polynomial(xs, i, &m)).collect() } @@ -67,8 +67,54 @@ where nom } +/// Returns Lagrange coefficients for the given set of x values. +/// +/// The i-th Lagrange coefficient is defined as: +/// ```text +/// L_i(0) = \prod_{j=0,j≠i}^n x_j / (x_j - x_i) +/// ``` +pub fn coefficients(xs: &[Fp]) -> Vec +where + Fp: PrimeField, +{ + let m = multiplier_for_coefficients(xs); + (0..xs.len()).map(|i| coefficient(xs, i, &m)).collect() +} + +/// Returns i-th Lagrange coefficient for the given set of x values. +/// +/// The i-th Lagrange coefficient is defined as: +/// ```text +/// L_i(0) = \prod_{j=0,j≠i}^n x_j / (x_j - x_i) +/// ``` +fn coefficient(xs: &[Fp], i: usize, multiplier: &Multiplier) -> Fp +where + Fp: PrimeField, +{ + let mut nom = multiplier.get_product(i).unwrap_or(Fp::ONE); + let mut denom = Fp::ONE; + for j in 0..xs.len() { + if j == i { + continue; + } + denom *= xs[j] - xs[i]; // (x_j - x_i) + } + let denom_inv = denom.invert().expect("values should be unique"); + nom *= denom_inv; // L_i(0) = nom / denom + + nom +} + +/// Creates a multiplier for the nominators in the Lagrange coefficients. +fn multiplier_for_coefficients(xs: &[Fp]) -> Multiplier +where + Fp: PrimeField, +{ + Multiplier::new(xs) +} + /// Creates a multiplier for the nominators in the Lagrange basis polynomials. -fn multiplier(xs: &[Fp]) -> Multiplier> +fn multiplier_for_basis_polynomials(xs: &[Fp]) -> Multiplier> where Fp: PrimeField, { @@ -90,7 +136,10 @@ mod tests { use group::ff::Field; use rand::{rngs::StdRng, RngCore, SeedableRng}; - use super::{basis_polynomial, lagrange, multiplier}; + use super::{ + basis_polynomial, basis_polynomials, coefficient, coefficients, lagrange, + multiplier_for_basis_polynomials, multiplier_for_coefficients, + }; fn scalar(value: i64) -> p384::Scalar { scalars(&vec![value])[0] @@ -136,24 +185,51 @@ mod tests { #[test] fn test_basis_polynomial() { - let xs = scalars(&[1, 2, 3]); - let m = multiplier(&xs); - - for i in 0..xs.len() { - let p = basis_polynomial(&xs, i, &m); - - // Verify points. - for (j, x) in xs.iter().enumerate() { - if j == i { - assert_eq!(p.eval(x), scalar(1)); // L_i(x_i) = 1 - } else { - assert_eq!(p.eval(x), scalar(0)); // L_i(x_j) = 0 + let vec = [ + scalars(&[1]), + scalars(&[1, 2, 3]), + scalars(&(1..=50).collect::>()), + ]; + + for xs in vec { + let m = multiplier_for_basis_polynomials(&xs); + + for i in 0..xs.len() { + let p = basis_polynomial(&xs, i, &m); + + // Verify points. + for (j, x) in xs.iter().enumerate() { + if j == i { + assert_eq!(p.eval(x), scalar(1)); // L_i(x_i) = 1 + } else { + assert_eq!(p.eval(x), scalar(0)); // L_i(x_j) = 0 + } } + + // Verify degree. + assert_eq!(p.degree(), xs.len() - 1); + assert_eq!(p.size(), xs.len()); } + } + } - // Verify degree. - assert_eq!(p.degree(), 2); - assert_eq!(p.size(), 3); + #[test] + fn test_coefficient() { + let vec = [ + scalars(&[1]), + scalars(&[1, 2, 3]), + scalars(&(1..=50).collect::>()), + ]; + + for xs in vec { + let sm = multiplier_for_coefficients(&xs); + let pm = multiplier_for_basis_polynomials(&xs); + for i in 0..xs.len() { + let c = coefficient(&xs, i, &sm); + let p = basis_polynomial(&xs, i, &pm); + + assert_eq!(c, p.eval(&scalar(0))); + } } } @@ -168,18 +244,36 @@ mod tests { }); } + fn bench_basis_polynomials(b: &mut Bencher, n: usize) { + let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let xs = random_scalars(n, &mut rng); + + b.iter(|| { + let _p = basis_polynomials(&xs); + }); + } + + fn bench_coefficients(b: &mut Bencher, n: usize) { + let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let xs = random_scalars(n, &mut rng); + + b.iter(|| { + let _p = coefficients(&xs); + }); + } + #[bench] - fn bench_lagrange_1(b: &mut Bencher) { + fn bench_lagrange_01(b: &mut Bencher) { bench_lagrange(b, 1) } #[bench] - fn bench_lagrange_2(b: &mut Bencher) { + fn bench_lagrange_02(b: &mut Bencher) { bench_lagrange(b, 2) } #[bench] - fn bench_lagrange_5(b: &mut Bencher) { + fn bench_lagrange_05(b: &mut Bencher) { bench_lagrange(b, 5) } @@ -192,4 +286,54 @@ mod tests { fn bench_lagrange_20(b: &mut Bencher) { bench_lagrange(b, 20) } + + #[bench] + fn bench_basis_polynomials_01(b: &mut Bencher) { + bench_basis_polynomials(b, 1) + } + + #[bench] + fn bench_basis_polynomials_02(b: &mut Bencher) { + bench_basis_polynomials(b, 2) + } + + #[bench] + fn bench_basis_polynomials_05(b: &mut Bencher) { + bench_basis_polynomials(b, 5) + } + + #[bench] + fn bench_basis_polynomials_10(b: &mut Bencher) { + bench_basis_polynomials(b, 10) + } + + #[bench] + fn bench_basis_polynomials_20(b: &mut Bencher) { + bench_basis_polynomials(b, 20) + } + + #[bench] + fn bench_coefficients_01(b: &mut Bencher) { + bench_coefficients(b, 1) + } + + #[bench] + fn bench_coefficients_02(b: &mut Bencher) { + bench_coefficients(b, 2) + } + + #[bench] + fn bench_coefficients_05(b: &mut Bencher) { + bench_coefficients(b, 5) + } + + #[bench] + fn bench_coefficients_10(b: &mut Bencher) { + bench_coefficients(b, 10) + } + + #[bench] + fn bench_coefficients_20(b: &mut Bencher) { + bench_coefficients(b, 20) + } }