Skip to content

Commit

Permalink
test: add evaluation unit test
Browse files Browse the repository at this point in the history
  • Loading branch information
huitseeker committed Oct 26, 2023
1 parent 2d373e9 commit 729ba78
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ cfg-if = "1.0.0"
once_cell = "1.18.0"
anyhow = "1.0.72"
rand = "0.8.4"
rand_xorshift = "0.3.0"

[target.'cfg(any(target_arch = "x86_64", target_arch = "aarch64"))'.dependencies]
pasta-msm = { git="https://github.com/lurk-lab/pasta-msm", branch="dev", version = "0.1.4" }
Expand Down
19 changes: 19 additions & 0 deletions ThirdPartyNotices.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,22 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

------------------------------------------------------------
https://github.com/AztecProtocol/aztec-packages/

Licensed under Apache 2.0

Copyright 2022 Aztec

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
112 changes: 112 additions & 0 deletions src/spartan/polys/multilinear.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ mod tests {

use super::*;
use pasta_curves::Fp;
use rand_core::SeedableRng;

fn make_mlp<F: PrimeField>(len: usize, value: F) -> MultilinearPolynomial<F> {
MultilinearPolynomial {
Expand Down Expand Up @@ -335,4 +336,115 @@ mod tests {
test_evaluation_with::<provider::bn256_grumpkin::bn256::Scalar>();
test_evaluation_with::<provider::secp_secq::secp256k1::Scalar>();
}

pub fn partial_eval<F: PrimeField>(
poly: &MultilinearPolynomial<F>,
point: &[F],
) -> MultilinearPolynomial<F> {
// Get size of partial evaluation point u = (u_0,...,u_{m-1})
let m = point.len();

// Assert that the size of the polynomial being evaluated is a power of 2 greater than (1 << m)
assert!(poly.Z.len().is_power_of_two());
assert!(poly.Z.len() >= 1 << m);
let n = poly.Z.len().trailing_zeros() as usize;

// Partial evaluation is done in m rounds l = 0,...,m-1.
let mut n_l = 1 << (n - 1);

// Temporary buffer of half the size of the polynomial
let mut tmp = vec![F::ZERO; n_l];

let prev = &poly.Z;

// Evaluate variable X_{n-1} at u_{m-1}
let u_l = point[m - 1];
for i in 0..n_l {
tmp[i] = prev[i] + u_l * (prev[i + n_l] - prev[i]);
}

// Evaluate m-1 variables X_{n-l-1}, ..., X_{n-2} at m-1 remaining values u_0,...,u_{m-2})
for l in 1..m {
n_l = 1 << (n - l - 1);
let u_l = point[m - l - 1];
for i in 0..n_l {
tmp[i] = tmp[i] + u_l * (tmp[i + n_l] - tmp[i]);
}
}
tmp.truncate(1 << (poly.num_vars - m));

MultilinearPolynomial::new(tmp)
}

fn make_rand_mlp<F: PrimeField, R: RngCore>(
var_count: usize,
mut rng: &mut R,
) -> MultilinearPolynomial<F> {
let eqpoly = EqPolynomial::new(
std::iter::from_fn(|| Some(F::random(&mut rng)))
.take(var_count)
.collect::<Vec<_>>(),
);
MultilinearPolynomial::new(eqpoly.evals())
}

fn partial_evaluate_mle_with<F: PrimeField>() {
// Initialize a random polynomial
let n = 5;
let mut rng = rand_xorshift::XorShiftRng::from_seed([0u8; 16]);
let poly = make_rand_mlp::<F, _>(n, &mut rng);

// Define a random multivariate evaluation point u = (u_0, u_1, u_2, u_3, u_4)
let u_0 = F::random(&mut rng);
let u_1 = F::random(&mut rng);
let u_2 = F::random(&mut rng);
let u_3 = F::random(&mut rng);
let u_4 = F::random(&mut rng);
let u_challenge = vec![u_4, u_3, u_2, u_1, u_0];

// Directly computing v = p(u_0,...,u_4) and comparing it with the result of
// first computing the partial evaluation in the last 3 variables
// g(X_0,X_1) = p(X_0,X_1,u_2,u_3,u_4), then v = g(u_0,u_1)

// Compute v = p(u_0,...,u_4)
let v_expected = poly.evaluate(&u_challenge[..]);

// Compute g(X_0,X_1) = p(X_0,X_1,u_2,u_3,u_4), then v = g(u_0,u_1)
let u_part_1 = vec![u_1, u_0]; // note the endianness difference
let u_part_2 = vec![u_2, u_3, u_4];
let partial_evaluated_poly = partial_eval(&poly, &u_part_2);
let v_result = partial_evaluated_poly.evaluate(&u_part_1);

assert_eq!(v_result, v_expected);
}

#[test]
fn test_partial_evaluate_mle() {
partial_evaluate_mle_with::<Fp>();
partial_evaluate_mle_with::<bn256::Scalar>();
partial_evaluate_mle_with::<secp256k1::Scalar>();
}

fn partial_and_evaluate_with<F: PrimeField>() {
for _i in 0..50 {
// Initialize a random polynomial
let n = 7;
let mut rng = rand_xorshift::XorShiftRng::from_seed([0u8; 16]);
let poly = make_rand_mlp::<F, _>(n, &mut rng);

// draw a random point
let pt: Vec<_> = std::iter::from_fn(|| Some(F::random(&mut rng)))
.take(n)
.collect();
let rev_pt: Vec<_> = pt.iter().cloned().rev().collect();
assert_eq!(poly.evaluate(&pt), partial_eval(&poly, &rev_pt).Z[0])
}
}

#[test]
fn test_partial_and_evaluate() {
partial_and_evaluate_with::<Fp>();
partial_and_evaluate_with::<bn256::Scalar>();
partial_and_evaluate_with::<secp256k1::Scalar>();
}
}

0 comments on commit 729ba78

Please sign in to comment.